diff --git a/lib/default.nix b/lib/default.nix index 17a8f15..0b760c8 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -347,6 +347,70 @@ let (lib.attrNames (builtins.readDir ./types)) ); + + # render types into an json serializable format + serializeType = type: + let + options = lib.filter (x: !lib.hasPrefix "_" x) (lib.attrNames type.options); + in + lib.listToAttrs ( + map + (option: lib.nameValuePair + option + type.options.${option} + ) + options + ); + + typesSerializerLib = { + rootMountPoint = ""; + options = null; + config._module.args.name = "self.name"; + lib = { + mkOption = option: { + inherit (option) type description; + default = option.default or null; + }; + types = { + attrsOf = subType: { + type = "attrsOf"; + inherit subType; + }; + listOf = subType: { + type = "listOf"; + inherit subType; + }; + nullOr = subType: { + type = "nullOr"; + inherit subType; + }; + enum = choices: { + type = "enum"; + inherit choices; + }; + str = "str"; + bool = "bool"; + int = "int"; + submodule = x: x { inherit (diskoLib.typesSerializerLib) lib config options; }; + }; + }; + diskoLib = { + optionTypes.absolute-pathname = "absolute-pathname"; + deviceType = "devicetype"; + partitionType = "partitiontype"; + subType = types: "onOf ${toString (lib.attrNames types)}"; + }; + }; + + jsonTypes = lib.listToAttrs ( + map + (file: lib.nameValuePair + (lib.removeSuffix ".nix" file) + (diskoLib.serializeType (import ./types/${file} diskoLib.typesSerializerLib)) + ) + (lib.attrNames (builtins.readDir ./types)) + ); + }; in diskoLib diff --git a/lib/types/btrfs.nix b/lib/types/btrfs.nix index 92b5e6d..b87c85f 100644 --- a/lib/types/btrfs.nix +++ b/lib/types/btrfs.nix @@ -17,7 +17,36 @@ description = "A list of options to pass to mount."; }; subvolumes = lib.mkOption { - type = lib.types.attrsOf diskoLib.types.btrfs_subvol; + type = lib.types.attrsOf (lib.types.submodule ({ config, ... }: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = config._module.args.name; + description = "Name of the BTRFS subvolume."; + }; + type = lib.mkOption { + type = lib.types.enum [ "btrfs_subvol" ]; + default = "btrfs_subvol"; + internal = true; + description = "Type"; + }; + extraArgs = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "Extra arguments"; + }; + mountOptions = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ "defaults" ]; + description = "Options to pass to mount"; + }; + mountpoint = lib.mkOption { + type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname; + default = null; + description = "Location to mount the subvolume to."; + }; + }; + })); default = { }; description = "Subvolumes to define for BTRFS."; }; @@ -30,22 +59,46 @@ internal = true; readOnly = true; type = lib.types.functionTo diskoLib.jsonType; - default = dev: - diskoLib.deepMergeMap (subvol: subvol._meta dev) (lib.attrValues config.subvolumes); + default = dev: { }; description = "Metadata"; }; _create = diskoLib.mkCreateOption { inherit config options; default = { dev }: '' mkfs.btrfs ${dev} ${toString config.extraArgs} - ${lib.concatMapStrings (subvol: subvol._create { inherit dev; }) (lib.attrValues config.subvolumes)} + ${lib.concatMapStrings (subvol: '' + MNTPOINT=$(mktemp -d) + ( + mount ${dev} "$MNTPOINT" -o subvol=/ + trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT + btrfs subvolume create "$MNTPOINT"/${subvol.name} ${toString subvol.extraArgs} + ) + '') (lib.attrValues config.subvolumes)} ''; }; _mount = diskoLib.mkMountOption { inherit config options; default = { dev }: let - subvolMounts = diskoLib.deepMergeMap (subvol: subvol._mount { inherit dev; parent = config.mountpoint; }) (lib.attrValues config.subvolumes); + subvolMounts = lib.concatMapAttrs + (_: subvol: + let + mountpoint = + if (subvol.mountpoint != null) then subvol.mountpoint + else if (config.mountpoint == null) then subvol.name + else null; + in + lib.optionalAttrs (mountpoint != null) { + fs.${mountpoint} = '' + if ! findmnt ${dev} "${rootMountPoint}${mountpoint}" > /dev/null 2>&1; then + mount ${dev} "${rootMountPoint}${mountpoint}" \ + ${lib.concatMapStringsSep " " (opt: "-o ${opt}") (subvol.mountOptions ++ [ "subvol=${subvol.name}" ])} \ + -o X-mount.mkdir + fi + ''; + } + ) + config.subvolumes; in { fs = subvolMounts.fs // lib.optionalAttrs (config.mountpoint != null) { @@ -63,7 +116,23 @@ internal = true; readOnly = true; default = dev: [ - (map (subvol: subvol._config dev config.mountpoint) (lib.attrValues config.subvolumes)) + (map + (subvol: + let + mountpoint = + if (subvol.mountpoint != null) then subvol.mountpoint + else if (config.mountpoint == null) then subvol.name + else null; + in + lib.optional (mountpoint != null) { + fileSystems.${mountpoint} = { + device = dev; + fsType = "btrfs"; + options = subvol.mountOptions ++ [ "subvol=${subvol.name}" ]; + }; + } + ) + (lib.attrValues config.subvolumes)) (lib.optional (config.mountpoint != null) { fileSystems.${config.mountpoint} = { device = dev; @@ -79,7 +148,7 @@ readOnly = true; type = lib.types.functionTo (lib.types.listOf lib.types.package); default = pkgs: - [ pkgs.btrfs-progs ] ++ lib.flatten (map (subvolume: subvolume._pkgs pkgs) (lib.attrValues config.subvolumes)); + [ pkgs.btrfs-progs pkgs.coreutils ]; description = "Packages"; }; }; diff --git a/lib/types/btrfs_subvol.nix b/lib/types/btrfs_subvol.nix deleted file mode 100644 index 461f5a2..0000000 --- a/lib/types/btrfs_subvol.nix +++ /dev/null @@ -1,94 +0,0 @@ -{ config, options, diskoLib, lib, rootMountPoint, ... }: -{ - options = { - name = lib.mkOption { - type = lib.types.str; - default = config._module.args.name; - description = "Name of the BTRFS subvolume."; - }; - type = lib.mkOption { - type = lib.types.enum [ "btrfs_subvol" ]; - default = "btrfs_subvol"; - internal = true; - description = "Type"; - }; - extraArgs = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - description = "Extra arguments"; - }; - mountOptions = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ "defaults" ]; - description = "Options to pass to mount"; - }; - mountpoint = lib.mkOption { - type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname; - default = null; - description = "Location to mount the subvolume to."; - }; - _meta = lib.mkOption { - internal = true; - readOnly = true; - type = lib.types.functionTo diskoLib.jsonType; - default = dev: { }; - description = "Metadata"; - }; - _create = diskoLib.mkCreateOption { - inherit config options; - default = { dev }: '' - MNTPOINT=$(mktemp -d) - ( - mount ${dev} "$MNTPOINT" -o subvol=/ - trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT - btrfs subvolume create "$MNTPOINT"/${config.name} ${toString config.extraArgs} - ) - ''; - }; - _mount = diskoLib.mkMountOption { - inherit config options; - default = { dev, parent }: - let - mountpoint = - if (config.mountpoint != null) then config.mountpoint - else if (parent == null) then config.name - else null; - in - lib.optionalAttrs (mountpoint != null) { - fs.${mountpoint} = '' - if ! findmnt ${dev} "${rootMountPoint}${mountpoint}" > /dev/null 2>&1; then - mount ${dev} "${rootMountPoint}${mountpoint}" \ - ${lib.concatMapStringsSep " " (opt: "-o ${opt}") (config.mountOptions ++ [ "subvol=${config.name}" ])} \ - -o X-mount.mkdir - fi - ''; - }; - }; - _config = lib.mkOption { - internal = true; - readOnly = true; - default = dev: parent: - let - mountpoint = - if (config.mountpoint != null) then config.mountpoint - else if (parent == null) then config.name - else null; - in - lib.optional (mountpoint != null) { - fileSystems.${mountpoint} = { - device = dev; - fsType = "btrfs"; - options = config.mountOptions ++ [ "subvol=${config.name}" ]; - }; - }; - description = "NixOS configuration"; - }; - _pkgs = lib.mkOption { - internal = true; - readOnly = true; - type = lib.types.functionTo (lib.types.listOf lib.types.package); - default = pkgs: [ pkgs.coreutils ]; - description = "Packages"; - }; - }; -} diff --git a/lib/types/table.nix b/lib/types/table.nix index a366685..6d5e390 100644 --- a/lib/types/table.nix +++ b/lib/types/table.nix @@ -38,12 +38,6 @@ default = "100%"; description = "End of the partition"; }; - index = lib.mkOption { - type = lib.types.int; - # TODO find a better way to get the index - default = lib.toInt (lib.head (builtins.match ".*entry ([[:digit:]]+)]" config._module.args.name)); - description = "Index of the partition"; - }; flags = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ];