Merge pull request #14 from snowfallorg/feat/docs

wip: initial frost support
This commit is contained in:
Jake Hamilton 2023-08-17 01:02:37 -07:00 committed by GitHub
commit c028651883
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 873 additions and 479 deletions

View file

@ -22,7 +22,7 @@
# `lib.flake-utils-plus.mkApp`. # `lib.flake-utils-plus.mkApp`.
# Usage: mkLib { inherit inputs; src = ./.; } # Usage: mkLib { inherit inputs; src = ./.; }
# result: lib # result: lib
mkLib = import ./lib core-inputs; mkLib = import ./snowfall-lib core-inputs;
# A convenience wrapper to create the library and then call `lib.mkFlake`. # A convenience wrapper to create the library and then call `lib.mkFlake`.
# Usage: mkFlake { inherit inputs; src = ./.; ... } # Usage: mkFlake { inherit inputs; src = ./.; ... }
@ -50,5 +50,36 @@
homeModules = { homeModules = {
user = ./modules/home/user/default.nix; user = ./modules/home/user/default.nix;
}; };
_snowfall = rec {
raw-config = config;
config = {
root = ./.;
src = ./.;
namespace = "snowfall";
lib-dir = "snowfall-lib";
meta = {
name = "snowfall-lib";
title = "Snowfall Lib";
};
};
internal-lib =
let
lib = mkLib {
src = ./.;
inputs = inputs // {
self = { };
};
};
in
builtins.removeAttrs
lib.snowfall
[ "internal" ];
};
}; };
} }

View file

@ -1,60 +0,0 @@
{ core-inputs
, user-inputs
, snowfall-lib
, snowfall-config
}:
let
inherit (core-inputs.nixpkgs.lib)
assertMsg
mapAttrsToList
mapAttrs
flatten
foldl
recursiveUpdate
mergeAttrs
isDerivation;
in
{
attrs = {
# Map and flatten an attribute set into a list.
# Type: (a -> b -> [c]) -> Attrs -> [c]
# Usage: map-concat-attrs-to-list (name: value: [name value]) { x = 1; y = 2; }
# result: [ "x" 1 "y" 2 ]
map-concat-attrs-to-list = f: attrs:
flatten (mapAttrsToList f attrs);
# Recursively merge a list of attribute sets.
# Type: [Attrs] -> Attrs
# Usage: merge-deep [{ x = 1; } { x = 2; }]
# result: { x = 2; }
merge-deep = foldl recursiveUpdate { };
# Merge the root of a list of attribute sets.
# Type: [Attrs] -> Attrs
# Usage: merge-shallow [{ x = 1; } { x = 2; }]
# result: { x = 2; }
merge-shallow = foldl mergeAttrs { };
# Merge shallow for packages, but allow one deeper layer of attribute sets.
# Type: [Attrs] -> Attrs
# Usage: merge-shallow-packages [ { inherit (pkgs) vim; some.value = true; } { some.value = false; } ]
# result: { vim = ...; some.value = false; }
merge-shallow-packages = items:
foldl
(result: item:
result // (mapAttrs
(name: value:
if isDerivation value then
value
else if builtins.isAttrs value then
(result.${name} or { }) // value
else
value
)
item)
)
{ }
items;
};
}

View file

@ -1,37 +0,0 @@
{ core-inputs
, user-inputs
, snowfall-lib
, snowfall-config
}:
let
inherit (builtins) baseNameOf dirOf;
inherit (core-inputs.nixpkgs.lib) id foldr flip;
in
{
fp = rec {
# Compose two functions.
# Type: (b -> c) -> (a -> b) -> a -> c
# Usage: compose add-two add-one
# result: (x: add-two (add-one x))
compose = f: g: x: f (g x);
# Compose many functions.
# Type: [(x -> y)] -> a -> b
# Usage: compose-all [ add-two add-one ]
# result: (x: add-two (add-one x))
compose-all = foldr compose id;
# Call a function with an argument.
# Type: (a -> b) -> a -> b
# Usage: call (x: x + 1) 0
# result: 1
call = f: x: f x;
# Apply an argument to a function.
# Type: a -> (a -> b) -> b
# Usage: call (x: x + 1) 0
# result: 1
apply = flip call;
};
}

View file

@ -1,161 +0,0 @@
{ core-inputs
, user-inputs
, snowfall-lib
, snowfall-config
}:
let
inherit (builtins) readDir pathExists;
inherit (core-inputs) flake-utils-plus;
inherit (core-inputs.nixpkgs.lib) assertMsg filterAttrs mapAttrsToList flatten;
file-name-regex = "(.*)\\.(.*)$";
in
{
fs = rec {
# Matchers for file kinds. These are often used with `readDir`.
# Type: String -> Bool
# Usage: is-file-kind "directory"
# result: false
is-file-kind = kind: kind == "regular";
is-symlink-kind = kind: kind == "symlink";
is-directory-kind = kind: kind == "directory";
is-unknown-kind = kind: kind == "unknown";
# Get a file path relative to the user's flake.
# Type: Path -> Path
# Usage: get-file "systems"
# result: "/user-source/systems"
get-file = path: "${user-inputs.src}/${path}";
# Get a file path relative to the user's snowfall directory.
# Type: Path -> Path
# Usage: get-snowfall-file "systems"
# result: "/user-source/snowfall-dir/systems"
get-snowfall-file = path: "${snowfall-config.root}/${path}";
# Get a file path relative to the this flake.
# Type: Path -> Path
# Usage: get-file "systems"
# result: "/user-source/systems"
internal-get-file = path: "${core-inputs.src}/${path}";
# Safely read from a directory if it exists.
# Type: Path -> Attrs
# Usage: safe-read-directory ./some/path
# result: { "my-file.txt" = "regular"; }
safe-read-directory = path:
if pathExists path then
readDir path
else
{ };
# Get directories at a given path.
# Type: Path -> [Path]
# Usage: get-directories ./something
# result: [ "./something/a-directory" ]
get-directories = path:
let
entries = safe-read-directory path;
filtered-entries = filterAttrs (name: kind: is-directory-kind kind) entries;
in
mapAttrsToList (name: kind: "${path}/${name}") filtered-entries;
# Get files at a given path.
# Type: Path -> [Path]
# Usage: get-files ./something
# result: [ "./something/a-file" ]
get-files = path:
let
entries = safe-read-directory path;
filtered-entries = filterAttrs (name: kind: is-file-kind kind) entries;
in
mapAttrsToList (name: kind: "${path}/${name}") filtered-entries;
# Get files at a given path, traversing any directories within.
# Type: Path -> [Path]
# Usage: get-files-recursive ./something
# result: [ "./something/some-directory/a-file" ]
get-files-recursive = path:
let
entries = safe-read-directory path;
filtered-entries =
filterAttrs
(name: kind: (is-file-kind kind) || (is-directory-kind kind))
entries;
map-file = name: kind:
let
path' = "${path}/${name}";
in
if is-directory-kind kind then
get-files-recursive path'
else
path';
files = snowfall-lib.attrs.map-concat-attrs-to-list
map-file
filtered-entries;
in
files;
# Get nix files at a given path.
# Type: Path -> [Path]
# Usage: get-nix-files "./something"
# result: [ "./something/a.nix" ]
get-nix-files = path:
builtins.filter
(snowfall-lib.path.has-file-extension "nix")
(get-files path);
# Get nix files at a given path, traversing any directories within.
# Type: Path -> [Path]
# Usage: get-nix-files "./something"
# result: [ "./something/a.nix" ]
get-nix-files-recursive = path:
builtins.filter
(snowfall-lib.path.has-file-extension "nix")
(get-files-recursive path);
# Get nix files at a given path named "default.nix".
# Type: Path -> [Path]
# Usage: get-default-nix-files "./something"
# result: [ "./something/default.nix" ]
get-default-nix-files = path:
builtins.filter
(name: builtins.baseNameOf name == "default.nix")
(get-files path);
# Get nix files at a given path named "default.nix", traversing any directories within.
# Type: Path -> [Path]
# Usage: get-default-nix-files-recursive "./something"
# result: [ "./something/some-directory/default.nix" ]
get-default-nix-files-recursive = path:
builtins.filter
(name: builtins.baseNameOf name == "default.nix")
(get-files-recursive path);
# Get nix files at a given path not named "default.nix".
# Type: Path -> [Path]
# Usage: get-non-default-nix-files "./something"
# result: [ "./something/a.nix" ]
get-non-default-nix-files = path:
builtins.filter
(name:
(snowfall-lib.path.has-file-extension "nix" name)
&& (builtins.baseNameOf name != "default.nix")
)
(get-files path);
# Get nix files at a given path not named "default.nix",
# traversing any directories within.
# Type: Path -> [Path]
# Usage: get-non-default-nix-files-recursive "./something"
# result: [ "./something/some-directory/a.nix" ]
get-non-default-nix-files-recursive = path:
builtins.filter
(name:
(snowfall-lib.path.has-file-extension "nix" name)
&& (builtins.baseNameOf name != "default.nix")
)
(get-files-recursive path);
};
}

View file

@ -1,78 +0,0 @@
{ core-inputs
, user-inputs
, snowfall-lib
, snowfall-config
}:
let
inherit (builtins) toString baseNameOf dirOf concatStringsSep;
inherit (core-inputs.nixpkgs.lib) assertMsg last init;
file-name-regex = "(.*)\\.(.*)$";
in
{
path = rec {
# Split a file name and its extension.
# Type: String -> [String]
# Usage: split-file-extension "my-file.md"
# result: [ "my-file" "md" ]
split-file-extension = file:
let
match = builtins.match file-name-regex file;
in
assert assertMsg (match != null) "lib.snowfall.split-file-extension: File must have an extension to split.";
match;
# Check if a file name has a file extension.
# Type: String -> Bool
# Usage: has-any-file-extension "my-file.txt"
# result: true
has-any-file-extension = file:
let
match = builtins.match file-name-regex (toString file);
in
match != null;
# Get the file extension of a file name.
# Type: String -> String
# Usage: get-file-extension "my-file.final.txt"
# result: "txt"
get-file-extension = file:
if has-any-file-extension file then
let
match = builtins.match file-name-regex (toString file);
in
last match
else
"";
# Check if a file name has a specific file extension.
# Type: String -> String -> Bool
# Usage: has-file-extension "txt" "my-file.txt"
# result: true
has-file-extension = extension: file:
if has-any-file-extension file then
extension == get-file-extension file
else
false;
# Get the parent directory for a given path.
# Type: Path -> Path
# Usage: get-parent-directory "/a/b/c"
# result: "/a/b"
get-parent-directory = snowfall-lib.fp.compose baseNameOf dirOf;
# Get the file name of a path without its extension.
# Type: Path -> String
# Usage: get-file-name-without-extension ./some-directory/my-file.pdf
# result: "my-file"
get-file-name-without-extension = path:
let
file-name = baseNameOf path;
in
if has-any-file-extension file-name then
concatStringsSep "" (init (split-file-extension file-name))
else
file-name;
};
}

View file

@ -1,10 +1,12 @@
{ pkgs, lib, options, config, inputs, ... }: args@{ pkgs, lib, options, config, ... }:
let let
inherit (lib) types mkOption mkDefault foldl optionalAttrs optional; inherit (lib) types mkOption mkDefault foldl optionalAttrs optional;
cfg = config.snowfallorg; cfg = config.snowfallorg;
inputs = args.inputs or { };
user-names = builtins.attrNames cfg.user; user-names = builtins.attrNames cfg.user;
create-system-users = system-users: name: create-system-users = system-users: name:
@ -58,29 +60,32 @@ in
config = mkOption { config = mkOption {
# HM-compatible options taken from: # HM-compatible options taken from:
# https://github.com/nix-community/home-manager/blob/0ee5ab611dc1fbb5180bd7d88d2aeb7841a4d179/nixos/common.nix#L14 # https://github.com/nix-community/home-manager/blob/0ee5ab611dc1fbb5180bd7d88d2aeb7841a4d179/nixos/common.nix#L14
# @NOTE(jakehamilton): This has been adapted to support documentation generation without
# having home-manager options fully declared.
type = types.submoduleWith { type = types.submoduleWith {
specialArgs = { specialArgs = {
osConfig = config; osConfig = config;
modulesPath = "${inputs.home-manager}/modules"; modulesPath = "${inputs.home-manager or "/"}/modules";
} // config.home-manager.extraSpecialArgs; } // (config.home-manager.extraSpecialArgs or { });
modules = [ modules = [
({ lib, modulesPath, ... }: { ({ lib, modulesPath, ... }:
imports = import "${modulesPath}/modules.nix" { if inputs ? home-manager then {
inherit pkgs lib; imports = import "${modulesPath}/modules.nix" {
useNixpkgsModule = !config.home-manager.useGlobalPkgs; inherit pkgs lib;
}; useNixpkgsModule = !(config.home-manager.useGlobalPkgs or false);
};
config = { config = {
submoduleSupport.enable = true; submoduleSupport.enable = true;
submoduleSupport.externalPackageInstall = cfg.useUserPackages; submoduleSupport.externalPackageInstall = cfg.useUserPackages;
home.username = config.users.users.${name}.name; home.username = config.users.users.${name}.name;
home.homeDirectory = config.users.users.${name}.home; home.homeDirectory = config.users.users.${name}.home;
nix.package = config.nix.package; nix.package = config.nix.package;
}; };
}) } else { })
] ++ config.home-manager.sharedModules; ] ++ (config.home-manager.sharedModules or [ ]);
}; };
}; };
}; };

View file

@ -0,0 +1,84 @@
{ core-inputs
, user-inputs
, snowfall-lib
, snowfall-config
}:
let
inherit (core-inputs.nixpkgs.lib)
assertMsg
mapAttrsToList
mapAttrs
flatten
foldl
recursiveUpdate
mergeAttrs
isDerivation;
in
{
attrs = {
## Map and flatten an attribute set into a list.
## Example Usage:
## ```nix
## map-concat-attrs-to-list (name: value: [name value]) { x = 1; y = 2; }
## ```
## Result:
## ```nix
## [ "x" 1 "y" 2 ]
## ```
#@ (a -> b -> [c]) -> Attrs -> [c]
map-concat-attrs-to-list = f: attrs:
flatten (mapAttrsToList f attrs);
## Recursively merge a list of attribute sets.
## Example Usage:
## ```nix
## merge-deep [{ x = 1; } { x = 2; }]
## ```
## Result:
## ```nix
## { x = 2; }
## ```
#@ [Attrs] -> Attrs
merge-deep = foldl recursiveUpdate { };
## Merge the root of a list of attribute sets.
## Example Usage:
## ```nix
## merge-shallow [{ x = 1; } { x = 2; }]
## ```
## Result:
## ```nix
## { x = 2; }
## ```
#@ [Attrs] -> Attrs
merge-shallow = foldl mergeAttrs { };
## Merge shallow for packages, but allow one deeper layer of attribute sets.
## Example Usage:
## ```nix
## merge-shallow-packages [ { inherit (pkgs) vim; some.value = true; } { some.value = false; } ]
## ```
## Result:
## ```nix
## { vim = ...; some.value = false; }
## ```
#@ [Attrs] -> Attrs
merge-shallow-packages = items:
foldl
(result: item:
result // (mapAttrs
(name: value:
if isDerivation value then
value
else if builtins.isAttrs value then
(result.${name} or { }) // value
else
value
)
item)
)
{ }
items;
};
}

View file

@ -8,7 +8,13 @@ user-options:
let let
raw-snowfall-config = user-options.snowfall or { }; raw-snowfall-config = user-options.snowfall or { };
snowfall-config = raw-snowfall-config // { snowfall-config = raw-snowfall-config // {
src = user-options.src;
root = raw-snowfall-config.root or user-options.src; root = raw-snowfall-config.root or user-options.src;
namespace = raw-snowfall-config.namespace or "internal";
meta = {
name = raw-snowfall-config.meta.name or null;
title = raw-snowfall-config.meta.title or null;
};
}; };
user-inputs = user-options.inputs // { src = user-options.src; }; user-inputs = user-options.inputs // { src = user-options.src; };
@ -53,7 +59,9 @@ let
core-inputs-libs = get-libs (without-self core-inputs); core-inputs-libs = get-libs (without-self core-inputs);
user-inputs-libs = get-libs (without-self user-inputs); user-inputs-libs = get-libs (without-self user-inputs);
snowfall-lib-root = "${core-inputs.src}/lib"; # @NOTE(jakehamilton): This root is different to accomodate the creation
# of a fake user-lib in order to run documentation on this flake.
snowfall-lib-root = "${core-inputs.src}/snowfall-lib";
snowfall-lib-dirs = snowfall-lib-dirs =
let let
files = builtins.readDir snowfall-lib-root; files = builtins.readDir snowfall-lib-root;

View file

@ -9,29 +9,52 @@ let
in in
rec { rec {
flake = rec { flake = rec {
# Remove the `self` attribute from an attribute set. ## Remove the `self` attribute from an attribute set.
# Type: Attrs -> Attrs ## Example Usage:
# Usage: without-self { self = {}; x = true; } ## ```nix
# result: { x = true; } ## without-self { self = {}; x = true; }
## ```
## Result:
## ```nix
## { x = true; }
## ```
#@ Attrs -> Attrs
without-self = flake-inputs: builtins.removeAttrs flake-inputs [ "self" ]; without-self = flake-inputs: builtins.removeAttrs flake-inputs [ "self" ];
# Remove the `src` attribute from an attribute set. ## Remove the `src` attribute from an attribute set.
# Type: Attrs -> Attrs ## Example Usage:
# Usage: without-src { src = ./.; x = true; } ## ```nix
# result: { x = true; } ## without-src { src = ./.; x = true; }
## ```
## Result:
## ```nix
## { x = true; }
## ```
#@ Attrs -> Attrs
without-src = flake-inputs: builtins.removeAttrs flake-inputs [ "src" ]; without-src = flake-inputs: builtins.removeAttrs flake-inputs [ "src" ];
# Remove the `src` and `self` attributes from an attribute set. ## Remove the `src` and `self` attributes from an attribute set.
# Type: Attrs -> Attrs ## Example Usage:
# Usage: without-snowfall-inputs { self = {}; src = ./.; x = true; } ## ```nix
# result: { x = true; } ## without-snowfall-inputs { self = {}; src = ./.; x = true; }
## ```
## Result:
## ```nix
## { x = true; }
## ```
#@ Attrs -> Attrs
without-snowfall-inputs = snowfall-lib.fp.compose without-self without-src; without-snowfall-inputs = snowfall-lib.fp.compose without-self without-src;
# Remove Snowfall-specific attributes so the rest can be safely ## Remove Snowfall-specific attributes so the rest can be safely passed to flake-utils-plus.
# passed to flake-utils-plus. ## Example Usage:
# Type: Attrs -> Attrs ## ```nix
# Usage: without-snowfall-options { src = ./.; x = true; } ## without-snowfall-options { src = ./.; x = true; }
# result: { x = true; } ## ```
## Result:
## ```nix
## { x = true; }
## ```
#@ Attrs -> Attrs
without-snowfall-options = flake-options: without-snowfall-options = flake-options:
builtins.removeAttrs builtins.removeAttrs
flake-options flake-options
@ -51,12 +74,16 @@ rec {
"snowfall" "snowfall"
]; ];
# Transform an attribute set of inputs into an attribute set where ## Transform an attribute set of inputs into an attribute set where the values are the inputs' `lib` attribute. Entries without a `lib` attribute are removed.
# the values are the inputs' `lib` attribute. Entries without a `lib` ## Example Usage:
# attribute are removed. ## ```nix
# Type: Attrs -> Attrs ## get-lib { x = nixpkgs; y = {}; }
# Usage: get-lib { x = nixpkgs; y = {}; } ## ```
# result: { x = nixpkgs.lib; } ## Result:
## ```nix
## { x = nixpkgs.lib; }
## ```
#@ Attrs -> Attrs
get-libs = attrs: get-libs = attrs:
let let
# @PERF(jakehamilton): Replace filter+map with a fold. # @PERF(jakehamilton): Replace filter+map with a fold.
@ -71,7 +98,7 @@ rec {
mkFlake = full-flake-options: mkFlake = full-flake-options:
let let
package-namespace = full-flake-options.package-namespace or "internal"; package-namespace = full-flake-options.package-namespace or snowfall-config.namespace or "internal";
custom-flake-options = flake.without-snowfall-options full-flake-options; custom-flake-options = flake.without-snowfall-options full-flake-options;
alias = full-flake-options.alias or { }; alias = full-flake-options.alias or { };
homes = snowfall-lib.home.create-homes (full-flake-options.homes or { }); homes = snowfall-lib.home.create-homes (full-flake-options.homes or { });
@ -149,6 +176,12 @@ rec {
}; };
outputsBuilder = outputs-builder; outputsBuilder = outputs-builder;
_snowfall = {
config = snowfall-config;
raw-config = full-flake-options.snowfall or { };
user-lib = snowfall-lib.internal.user-lib;
};
}; };
flake-utils-plus-outputs = flake-utils-plus-outputs =

View file

@ -0,0 +1,61 @@
{ core-inputs
, user-inputs
, snowfall-lib
, snowfall-config
}:
let
inherit (builtins) baseNameOf dirOf;
inherit (core-inputs.nixpkgs.lib) id foldr flip;
in
{
fp = rec {
## Compose two functions.
## Example Usage:
## ```nix
## compose add-two add-one
## ```
## Result:
## ```nix
## (x: add-two (add-one x))
## ```
#@ (b -> c) -> (a -> b) -> a -> c
compose = f: g: x: f (g x);
## Compose many functions.
## Example Usage:
## ```nix
## compose-all [ add-two add-one ]
## ```
## Result:
## ```nix
## (x: add-two (add-one x))
## ```
#@ [(x -> y)] -> a -> b
compose-all = foldr compose id;
## Call a function with an argument.
## Example Usage:
## ```nix
## call (x: x + 1) 0
## ```
## Result:
## ```nix
## 1
## ```
#@ (a -> b) -> a -> b
call = f: x: f x;
## Apply an argument to a function.
## Example Usage:
## ```nix
## apply 0 (x: x + 1)
## ```
## Result:
## ```nix
## 1
## ```
#@ a -> (a -> b) -> b
apply = flip call;
};
}

244
snowfall-lib/fs/default.nix Normal file
View file

@ -0,0 +1,244 @@
{ core-inputs
, user-inputs
, snowfall-lib
, snowfall-config
}:
let
inherit (builtins) readDir pathExists;
inherit (core-inputs) flake-utils-plus;
inherit (core-inputs.nixpkgs.lib) assertMsg filterAttrs mapAttrsToList flatten;
file-name-regex = "(.*)\\.(.*)$";
in
{
fs = rec {
## Matchers for file kinds. These are often used with `readDir`.
## Example Usage:
## ```nix
## is-file-kind "directory"
## ```
## Result:
## ```nix
## false
## ```
#@ String -> Bool
is-file-kind = kind: kind == "regular";
is-symlink-kind = kind: kind == "symlink";
is-directory-kind = kind: kind == "directory";
is-unknown-kind = kind: kind == "unknown";
## Get a file path relative to the user's flake.
## Example Usage:
## ```nix
## get-file "systems"
## ```
## Result:
## ```nix
## "/user-source/systems"
## ```
#@ Path -> Path
get-file = path: "${user-inputs.src}/${path}";
## Get a file path relative to the user's snowfall directory.
## Example Usage:
## ```nix
## get-snowfall-file "systems"
## ```
## Result:
## ```nix
## "/user-source/snowfall-dir/systems"
## ```
#@ Path -> Path
get-snowfall-file = path: "${snowfall-config.root}/${path}";
## Get a file path relative to the this flake.
## Example Usage:
## ```nix
## get-file "systems"
## ```
## Result:
## ```nix
## "/user-source/systems"
## ```
#@ Path -> Path
internal-get-file = path: "${core-inputs.src}/${path}";
## Safely read from a directory if it exists.
## Example Usage:
## ```nix
## safe-read-directory ./some/path
## ```
## Result:
## ```nix
## { "my-file.txt" = "regular"; }
## ```
#@ Path -> Attrs
safe-read-directory = path:
if pathExists path then
readDir path
else
{ };
## Get directories at a given path.
## Example Usage:
## ```nix
## get-directories ./something
## ```
## Result:
## ```nix
## [ "./something/a-directory" ]
## ```
#@ Path -> [Path]
get-directories = path:
let
entries = safe-read-directory path;
filtered-entries = filterAttrs (name: kind: is-directory-kind kind) entries;
in
mapAttrsToList (name: kind: "${path}/${name}") filtered-entries;
## Get files at a given path.
## Example Usage:
## ```nix
## get-files ./something
## ```
## Result:
## ```nix
## [ "./something/a-file" ]
## ```
#@ Path -> [Path]
get-files = path:
let
entries = safe-read-directory path;
filtered-entries = filterAttrs (name: kind: is-file-kind kind) entries;
in
mapAttrsToList (name: kind: "${path}/${name}") filtered-entries;
## Get files at a given path, traversing any directories within.
## Example Usage:
## ```nix
## get-files-recursive ./something
## ```
## Result:
## ```nix
## [ "./something/some-directory/a-file" ]
## ```
#@ Path -> [Path]
get-files-recursive = path:
let
entries = safe-read-directory path;
filtered-entries =
filterAttrs
(name: kind: (is-file-kind kind) || (is-directory-kind kind))
entries;
map-file = name: kind:
let
path' = "${path}/${name}";
in
if is-directory-kind kind then
get-files-recursive path'
else
path';
files = snowfall-lib.attrs.map-concat-attrs-to-list
map-file
filtered-entries;
in
files;
## Get nix files at a given path.
## Example Usage:
## ```nix
## get-nix-files "./something"
## ```
## Result:
## ```nix
## [ "./something/a.nix" ]
## ```
#@ Path -> [Path]
get-nix-files = path:
builtins.filter
(snowfall-lib.path.has-file-extension "nix")
(get-files path);
## Get nix files at a given path, traversing any directories within.
## Example Usage:
## ```nix
## get-nix-files "./something"
## ```
## Result:
## ```nix
## [ "./something/a.nix" ]
## ```
#@ Path -> [Path]
get-nix-files-recursive = path:
builtins.filter
(snowfall-lib.path.has-file-extension "nix")
(get-files-recursive path);
## Get nix files at a given path named "default.nix".
## Example Usage:
## ```nix
## get-default-nix-files "./something"
## ```
## Result:
## ```nix
## [ "./something/default.nix" ]
## ```
#@ Path -> [Path]
get-default-nix-files = path:
builtins.filter
(name: builtins.baseNameOf name == "default.nix")
(get-files path);
## Get nix files at a given path named "default.nix", traversing any directories within.
## Example Usage:
## ```nix
## get-default-nix-files-recursive "./something"
## ```
## Result:
## ```nix
## [ "./something/some-directory/default.nix" ]
## ```
#@ Path -> [Path]
get-default-nix-files-recursive = path:
builtins.filter
(name: builtins.baseNameOf name == "default.nix")
(get-files-recursive path);
## Get nix files at a given path not named "default.nix".
## Example Usage:
## ```nix
## get-non-default-nix-files "./something"
## ```
## Result:
## ```nix
## [ "./something/a.nix" ]
## ```
#@ Path -> [Path]
get-non-default-nix-files = path:
builtins.filter
(name:
(snowfall-lib.path.has-file-extension "nix" name)
&& (builtins.baseNameOf name != "default.nix")
)
(get-files path);
## Get nix files at a given path not named "default.nix", traversing any directories within.
## Example Usage:
## ```nix
## get-non-default-nix-files-recursive "./something"
## ```
## Result:
## ```nix
## [ "./something/some-directory/a.nix" ]
## ```
#@ Path -> [Path]
get-non-default-nix-files-recursive = path:
builtins.filter
(name:
(snowfall-lib.path.has-file-extension "nix" name)
&& (builtins.baseNameOf name != "default.nix")
)
(get-files-recursive path);
};
}

View file

@ -31,19 +31,32 @@ in
{ {
home = rec { home = rec {
# Modules in home-manager expect `hm` to be available directly on `lib` itself. # Modules in home-manager expect `hm` to be available directly on `lib` itself.
home-lib = snowfall-lib.internal.system-lib.extend (final: prev: home-lib =
# @NOTE(jakehamilton): This order is important, this library's extend and other utilities must write # @NOTE(jakehamilton): This prevents an error during evaluation if the input does
# _over_ the original `system-lib`. # not exist.
snowfall-lib.internal.system-lib if user-inputs ? home-manager then
// prev snowfall-lib.internal.system-lib.extend
// { (final: prev:
hm = snowfall-lib.internal.system-lib.home-manager.hm; # @NOTE(jakehamilton): This order is important, this library's extend and other utilities must write
}); # _over_ the original `system-lib`.
snowfall-lib.internal.system-lib
// prev
// {
hm = snowfall-lib.internal.system-lib.home-manager.hm;
})
else
{ };
# Get the user and host from a combined string. ## Get the user and host from a combined string.
# Type: String -> Attrs ## Example Usage:
# Usage: split-user-and-host "myuser@myhost" ## ```nix
# result: { user = "myuser"; host = "myhost"; } ## split-user-and-host "myuser@myhost"
## ```
## Result:
## ```nix
## { user = "myuser"; host = "myhost"; }
## ```
#@ String -> Attrs
split-user-and-host = target: split-user-and-host = target:
let let
raw-name-parts = builtins.split "@" target; raw-name-parts = builtins.split "@" target;
@ -61,10 +74,16 @@ in
}; };
# Create a home. ## Create a home.
# Type: Attrs -> Attrs ## Example Usage:
# Usage: create-home { path = ./homes/my-home; } ## ```nix
# result: <flake-utils-plus-home-configuration> ## create-home { path = ./homes/my-home; }
## ```
## Result:
## ```nix
## <flake-utils-plus-home-configuration>
## ```
#@ Attrs -> Attrs
create-home = create-home =
{ path { path
, name ? builtins.unsafeDiscardStringContext (snowfall-lib.system.get-inferred-system-name path) , name ? builtins.unsafeDiscardStringContext (snowfall-lib.system.get-inferred-system-name path)
@ -122,10 +141,16 @@ in
}); });
}; };
# Get structured data about all homes for a given target. ## Get structured data about all homes for a given target.
# Type: String -> [Attrs] ## Example Usage:
# Usage: get-target-homes-metadata ./homes ## ```nix
# result: [ { system = "x86_64-linux"; name = "my-home"; path = "/homes/x86_64-linux/my-home";} ] ## get-target-homes-metadata ./homes
## ```
## Result:
## ```nix
## [ { system = "x86_64-linux"; name = "my-home"; path = "/homes/x86_64-linux/my-home";} ]
## ```
#@ String -> [Attrs]
get-target-homes-metadata = target: get-target-homes-metadata = target:
let let
homes = snowfall-lib.fs.get-directories target; homes = snowfall-lib.fs.get-directories target;
@ -145,10 +170,16 @@ in
in in
home-configurations; home-configurations;
# Create all available homes. ## Create all available homes.
# Type: Attrs -> Attrs ## Example Usage:
# Usage: create-homes { users."my-user@my-system".specialArgs.x = true; modules = [ my-shared-module ]; } ## ```nix
# result: { "my-user@my-system" = <flake-utils-plus-home-configuration>; } ## create-homes { users."my-user@my-system".specialArgs.x = true; modules = [ my-shared-module ]; }
## ```
## Result:
## ```nix
## { "my-user@my-system" = <flake-utils-plus-home-configuration>; }
## ```
#@ Attrs -> Attrs
create-homes = homes: create-homes = homes:
let let
targets = snowfall-lib.fs.get-directories user-homes-root; targets = snowfall-lib.fs.get-directories user-homes-root;
@ -179,10 +210,16 @@ in
in in
created-homes; created-homes;
# Create system modules for home-manager integration. ## Create system modules for home-manager integration.
# Type: Attrs -> [Module] ## Example Usage:
# Usage: create-home-system-modules { users."my-user@my-system".specialArgs.x = true; modules = [ my-shared-module ]; } ## ```nix
# result: [Module] ## create-home-system-modules { users."my-user@my-system".specialArgs.x = true; modules = [ my-shared-module ]; }
## ```
## Result:
## ```nix
## [Module]
## ```
#@ Attrs -> [Module]
create-home-system-modules = users: create-home-system-modules = users:
let let
created-users = create-homes users; created-users = create-homes users;

View file

@ -42,7 +42,7 @@ let
system-lib = snowfall-lib.attrs.merge-shallow [ system-lib = snowfall-lib.attrs.merge-shallow [
base-lib base-lib
{ internal = user-lib; } { "${snowfall-config.namespace}" = user-lib; }
]; ];
in in
{ {

View file

@ -12,10 +12,16 @@ let
in in
{ {
module = { module = {
# Create flake output modules. ## Create flake output modules.
# Type: Attrs -> Attrs ## Example Usage:
# Usage: create-modules { src = ./my-modules; overrides = { inherit another-module; }; alias = { default = "another-module" }; } ## ```nix
# result: { another-module = ...; my-module = ...; default = ...; } ## create-modules { src = ./my-modules; overrides = { inherit another-module; }; alias = { default = "another-module" }; }
## ```
## Result:
## ```nix
## { another-module = ...; my-module = ...; default = ...; }
## ```
#@ Attrs -> Attrs
create-modules = create-modules =
{ src ? "${user-modules-root}/nixos" { src ? "${user-modules-root}/nixos"
, overrides ? { } , overrides ? { }

View file

@ -12,10 +12,16 @@ let
in in
{ {
overlay = { overlay = {
# Create a flake-utils-plus overlays builder. ## Create a flake-utils-plus overlays builder.
# Type: Attrs -> Attrs -> [(a -> b -> c)] ## Example Usage:
# Usage: create-overlays { src = ./my-overlays; package-namespace = "my-packages"; } ## ```nix
# result: (channels: [ ... ]) ## create-overlays { src = ./my-overlays; package-namespace = "my-packages"; }
## ```
## Result:
## ```nix
## (channels: [ ... ])
## ```
#@ Attrs -> Attrs -> [(a -> b -> c)]
create-overlays-builder = create-overlays-builder =
{ src ? user-overlays-root { src ? user-overlays-root
, package-namespace ? "internal" , package-namespace ? "internal"
@ -41,12 +47,17 @@ in
in in
overlays; overlays;
# Create exported overlays from the user flake. ## Create exported overlays from the user flake. Adapted [from flake-utils-plus](https://github.com/gytis-ivaskevicius/flake-utils-plus/blob/2bf0f91643c2e5ae38c1b26893ac2927ac9bd82a/lib/exportOverlays.nix).
# Adapted from flake-utils-plus: ##
# https://github.com/gytis-ivaskevicius/flake-utils-plus/blob/2bf0f91643c2e5ae38c1b26893ac2927ac9bd82a/lib/exportOverlays.nix ## Example Usage:
# Type: Attrs -> Attrs ## ```nix
# Usage: create-overlays { src = ./my-overlays; packages-src = ./my-packages; package-namespace = "my-namespace"; extra-overlays = {}; } ## create-overlays { src = ./my-overlays; packages-src = ./my-packages; package-namespace = "my-namespace"; extra-overlays = {}; }
# result: { default = final: prev: ...; some-overlay = final: prev: ...; } ## ```
## Result:
## ```nix
## { default = final: prev: ...; some-overlay = final: prev: ...; }
## ```
#@ Attrs -> Attrs
create-overlays = create-overlays =
{ src ? user-overlays-root { src ? user-overlays-root
, packages-src ? user-packages-root , packages-src ? user-packages-root

View file

@ -12,10 +12,16 @@ let
in in
{ {
package = rec { package = rec {
# Create flake output packages. ## Create flake output packages.
# Type: Attrs -> Attrs ## Example Usage:
# Usage: create-packages { inherit channels; src = ./my-packages; overrides = { inherit another-package; }; alias.default = "another-package"; } ## ```nix
# result: { another-package = ...; my-package = ...; default = ...; } ## create-packages { inherit channels; src = ./my-packages; overrides = { inherit another-package; }; alias.default = "another-package"; }
## ```
## Result:
## ```nix
## { another-package = ...; my-package = ...; default = ...; }
## ```
#@ Attrs -> Attrs
create-packages = create-packages =
{ channels { channels
, src ? user-packages-root , src ? user-packages-root
@ -40,7 +46,17 @@ in
in in
{ {
name = builtins.unsafeDiscardStringContext (snowfall-lib.path.get-parent-directory package); name = builtins.unsafeDiscardStringContext (snowfall-lib.path.get-parent-directory package);
drv = callPackageWith extra-inputs package { }; drv =
let
pkg = callPackageWith extra-inputs package { };
in
pkg // {
meta = (pkg.meta or { }) // {
snowfall = {
path = package;
};
};
};
}; };
packages-metadata = builtins.map create-package-metadata user-packages; packages-metadata = builtins.map create-package-metadata user-packages;
merge-packages = packages: metadata: merge-packages = packages: metadata:

View file

@ -0,0 +1,114 @@
{ core-inputs
, user-inputs
, snowfall-lib
, snowfall-config
}:
let
inherit (builtins) toString baseNameOf dirOf concatStringsSep;
inherit (core-inputs.nixpkgs.lib) assertMsg last init;
file-name-regex = "(.*)\\.(.*)$";
in
{
path = rec {
## Split a file name and its extension.
## Example Usage:
## ```nix
## split-file-extension "my-file.md"
## ```
## Result:
## ```nix
## [ "my-file" "md" ]
## ```
#@ String -> [String]
split-file-extension = file:
let
match = builtins.match file-name-regex file;
in
assert assertMsg (match != null) "lib.snowfall.split-file-extension: File must have an extension to split.";
match;
## Check if a file name has a file extension.
## Example Usage:
## ```nix
## has-any-file-extension "my-file.txt"
## ```
## Result:
## ```nix
## true
## ```
#@ String -> Bool
has-any-file-extension = file:
let
match = builtins.match file-name-regex (toString file);
in
match != null;
## Get the file extension of a file name.
## Example Usage:
## ```nix
## get-file-extension "my-file.final.txt"
## ```
## Result:
## ```nix
## "txt"
## ```
#@ String -> String
get-file-extension = file:
if has-any-file-extension file then
let
match = builtins.match file-name-regex (toString file);
in
last match
else
"";
## Check if a file name has a specific file extension.
## Example Usage:
## ```nix
## has-file-extension "txt" "my-file.txt"
## ```
## Result:
## ```nix
## true
## ```
#@ String -> String -> Bool
has-file-extension = extension: file:
if has-any-file-extension file then
extension == get-file-extension file
else
false;
## Get the parent directory for a given path.
## Example Usage:
## ```nix
## get-parent-directory "/a/b/c"
## ```
## Result:
## ```nix
## "/a/b"
## ```
#@ Path -> Path
get-parent-directory = snowfall-lib.fp.compose baseNameOf dirOf;
## Get the file name of a path without its extension.
## Example Usage:
## ```nix
## get-file-name-without-extension ./some-directory/my-file.pdf
## ```
## Result:
## ```nix
## "my-file"
## ```
#@ Path -> String
get-file-name-without-extension = path:
let
file-name = baseNameOf path;
in
if has-any-file-extension file-name then
concatStringsSep "" (init (split-file-extension file-name))
else
file-name;
};
}

View file

@ -12,10 +12,16 @@ let
in in
{ {
shell = { shell = {
# Create flake output packages. ## Create flake output packages.
# Type: Attrs -> Attrs ## Example Usage:
# Usage: create-shells { inherit channels; src = ./my-shells; overrides = { inherit another-shell; }; alias = { default = "another-shell"; }; } ## ```nix
# result: { another-shell = ...; my-shell = ...; default = ...; } ## create-shells { inherit channels; src = ./my-shells; overrides = { inherit another-shell; }; alias = { default = "another-shell"; }; }
## ```
## Result:
## ```nix
## { another-shell = ...; my-shell = ...; default = ...; }
## ```
#@ Attrs -> Attrs
create-shells = create-shells =
{ channels { channels
, src ? user-shells-root , src ? user-shells-root

View file

@ -15,39 +15,69 @@ let
in in
{ {
system = rec { system = rec {
# Get the name of a system based on its file path. ## Get the name of a system based on its file path.
# Type: Path -> String ## Example Usage:
# Usage: get-inferred-system-name "/systems/my-system/default.nix" ## ```nix
# result: "my-system" ## get-inferred-system-name "/systems/my-system/default.nix"
## ```
## Result:
## ```nix
## "my-system"
## ```
#@ Path -> String
get-inferred-system-name = path: get-inferred-system-name = path:
if snowfall-lib.path.has-file-extension "nix" path then if snowfall-lib.path.has-file-extension "nix" path then
snowfall-lib.path.get-parent-directory path snowfall-lib.path.get-parent-directory path
else else
baseNameOf path; baseNameOf path;
# Check whether a named system is macOS. ## Check whether a named system is macOS.
# Type: String -> Bool ## Example Usage:
# Usage: is-darwin "x86_64-linux" ## ```nix
# result: false ## is-darwin "x86_64-linux"
## ```
## Result:
## ```nix
## false
## ```
#@ String -> Bool
is-darwin = hasInfix "darwin"; is-darwin = hasInfix "darwin";
# Check whether a named system is Linux. ## Check whether a named system is Linux.
# Type: String -> Bool ## Example Usage:
# Usage: is-linux "x86_64-linux" ## ```nix
# result: false ## is-linux "x86_64-linux"
## ```
## Result:
## ```nix
## false
## ```
#@ String -> Bool
is-linux = hasInfix "linux"; is-linux = hasInfix "linux";
# Check whether a named system is virtual. ## Check whether a named system is virtual.
# Type: String -> Bool ## Example Usage:
# Usage: is-virtual "x86_64-iso" ## ```nix
# result: true ## is-virtual "x86_64-iso"
## ```
## Result:
## ```nix
## true
## ```
#@ String -> Bool
is-virtual = target: is-virtual = target:
(get-virtual-system-type target) != ""; (get-virtual-system-type target) != "";
# Get the virtual system type of a system target. ## Get the virtual system type of a system target.
# Type: String -> String ## Example Usage:
# Usage: get-virtual-system-type "x86_64-iso" ## ```nix
# result: "iso" ## get-virtual-system-type "x86_64-iso"
## ```
## Result:
## ```nix
## "iso"
## ```
#@ String -> String
get-virtual-system-type = target: get-virtual-system-type = target:
foldl foldl
(result: virtual-system: (result: virtual-system:
@ -59,10 +89,16 @@ in
"" ""
virtual-systems; virtual-systems;
# Get structured data about all systems for a given target. ## Get structured data about all systems for a given target.
# Type: String -> [Attrs] ## Example Usage:
# Usage: get-target-systems-metadata "x86_64-linux" ## ```nix
# result: [ { target = "x86_64-linux"; name = "my-machine"; path = "/systems/x86_64-linux/my-machine"; } ] ## get-target-systems-metadata "x86_64-linux"
## ```
## Result:
## ```nix
## [ { target = "x86_64-linux"; name = "my-machine"; path = "/systems/x86_64-linux/my-machine"; } ]
## ```
#@ String -> [Attrs]
get-target-systems-metadata = target: get-target-systems-metadata = target:
let let
systems = snowfall-lib.fs.get-directories target; systems = snowfall-lib.fs.get-directories target;
@ -82,10 +118,16 @@ in
in in
system-configurations; system-configurations;
# Get the system builder for a given target. ## Get the system builder for a given target.
# Type: String -> Function ## Example Usage:
# Usage: get-system-builder "x86_64-iso" ## ```nix
# result: (args: <system>) ## get-system-builder "x86_64-iso"
## ```
## Result:
## ```nix
## (args: <system>)
## ```
#@ String -> Function
get-system-builder = target: get-system-builder = target:
let let
virtual-system-type = get-virtual-system-type target; virtual-system-type = get-virtual-system-type target;
@ -130,10 +172,16 @@ in
else else
linux-system-builder; linux-system-builder;
# Get the flake output attribute for a system target. ## Get the flake output attribute for a system target.
# Type: String -> String ## Example Usage:
# Usage: get-system-output "aarch64-darwin" ## ```nix
# result: "darwinConfigurations" ## get-system-output "aarch64-darwin"
## ```
## Result:
## ```nix
## "darwinConfigurations"
## ```
#@ String -> String
get-system-output = target: get-system-output = target:
let let
virtual-system-type = get-virtual-system-type target; virtual-system-type = get-virtual-system-type target;
@ -145,10 +193,16 @@ in
else else
"nixosConfigurations"; "nixosConfigurations";
# Get the resolved (non-virtual) system target. ## Get the resolved (non-virtual) system target.
# Type: String -> String ## Example Usage:
# Usage: get-resolved-system-target "x86_64-iso" ## ```nix
# result: "x86_64-linux" ## get-resolved-system-target "x86_64-iso"
## ```
## Result:
## ```nix
## "x86_64-linux"
## ```
#@ String -> String
get-resolved-system-target = target: get-resolved-system-target = target:
let let
virtual-system-type = get-virtual-system-type target; virtual-system-type = get-virtual-system-type target;
@ -158,10 +212,16 @@ in
else else
target; target;
# Create a system. ## Create a system.
# Type: Attrs -> Attrs ## Example Usage:
# Usage: create-system { path = ./systems/my-system; } ## ```nix
# result: <flake-utils-plus-system-configuration> ## create-system { path = ./systems/my-system; }
## ```
## Result:
## ```nix
## <flake-utils-plus-system-configuration>
## ```
#@ Attrs -> Attrs
create-system = create-system =
{ target ? "x86_64-linux" { target ? "x86_64-linux"
, system ? get-resolved-system-target target , system ? get-resolved-system-target target
@ -199,10 +259,16 @@ in
}; };
}; };
# Create all available systems. ## Create all available systems.
# Type: Attrs -> Attrs ## Example Usage:
# Usage: create-systems { hosts.my-host.specialArgs.x = true; modules.nixos = [ my-shared-module ]; } ## ```nix
# result: { my-host = <flake-utils-plus-system-configuration>; } ## create-systems { hosts.my-host.specialArgs.x = true; modules.nixos = [ my-shared-module ]; }
## ```
## Result:
## ```nix
## { my-host = <flake-utils-plus-system-configuration>; }
## ```
#@ Attrs -> Attrs
create-systems = { systems ? { }, homes ? { } }: create-systems = { systems ? { }, homes ? { } }:
let let
targets = snowfall-lib.fs.get-directories user-systems-root; targets = snowfall-lib.fs.get-directories user-systems-root;

View file

@ -12,10 +12,18 @@ let
in in
{ {
template = { template = {
# Create flake templates. ## Create flake templates.
# Type: Attrs -> Attrs ##
# Usage: create-templates { src = ./my-templates; overrides = { inherit another-template; }; alias = { default = "another-template"; }; } ## Example Usage:
# result: { another-template = ...; my-template = ...; default = ...; } ## ```nix
## create-templates { src = ./my-templates; overrides = { inherit another-template; }; alias = { default = "another-template"; }; }
## ```
##
## Result:
## ```nix
## { another-template = ...; my-template = ...; default = ...; }
## ```
#@ Attrs -> Attrs
create-templates = create-templates =
{ src ? user-templates-root { src ? user-templates-root
, overrides ? { } , overrides ? { }