Option creation with variable

Hello im trying to create a different method to create an option and configure it with a variable in the samespace here is a template for the module in question:
say that the file is in the path: modules/example/module.nix

{ lib, config, ... }: let
# module = "modules.example.module";
  module = modules.example.module;
in {
  options.${module}.enable =
    lib.mkEnableOption "enable module";
  config = lib.mkIf config.${module}.enable {
    # ...
  };
}

this module would be imported and enabled like this:

{
  imports = [ ./modules/example/module.nix ];
  config = {
    modules.example.module.enable = true;
  };
}

is this possible?

If the value of the module variable contains dots (let’s say it’s "foo.bar"), don’t expect options.${module}.enable to be the same attribute path as options.foo.bar.enable. Instead, it’s the same attribute path as options."foo.bar".enable.

If you would like to create a value that looks like

{ foo = { bar = { enable = whatever; }; }; }

but the foo and bar part is dynamically determined from a dot-separated string, perhaps you want this:

lib.setAttrByPath (lib.splitString "." module) whatever

Similarly, lib.getAttrFromPath may be of interest for getting the value of config.foo.bar.enable.

2 Likes

i just want a variable that would skip the work of writing that attribute path all the time in the file.
dont matter if its a dot separated string or whatever

There is no native first-class abstraction in the language for an attribute path (or subunits thereof larger than a single attribute string), though there are library functions like the ones I’ve pointed out that use lists of strings as an ersatz representation of such.

I have taken a look at setAttrbByPath that you mentioned and i think i can work that out to do what i need. thanks

You don’t have to?

{ lib, config, ... }:
let
  cfg = config.modules.example.module;
in
{
  options.modules.example.module = {
    enable = lib.mkEnableOption "enable module";
    extraConfig = lib.mkOption {
      type = lib.types.lines;
      default = "";
    };
  };

  config = lib.mkIf cfg.enable {
    environment.etc."example-module.conf".text = cfg.extraConfig;
  };
}

You only have to write it twice, rather than over and over. This is pretty standard practice. Messing around with setAttrbByPath just seems like unnecessary complexity and effort.

1 Like

Oh that’s it, i was trying to have it both on the options and config, but at least reducing to twice on those modules that i need to write many times over is worth it.
thanks

adding to this, is there any way to fix this error without having to write the attset a third time?

{ lib, config, ... }:
let
  cfg = config.modules.nixvim;
in 
{
  imports = [
    ./options.nix
    ./keys.nix
    ./ui
    ./theme
    ./utils
    ./language
  ];

  options.modules.nixvim.enable = 
    lib.mkEnableOption "enable all nixvim modules" 
    // {default = true;};

  cfg = lib.mkIf cfg.enable {
    ui.enable = lib.mkDefault true;
    language.enable = lib.mkDefault true;
    utils.enable = lib.mkDefault true;
    theme.enable = lib.mkDefault true;
    keys.enable = lib.mkDefault true;
    options.enable = lib.mkDefault true;
  };
}

the error:
error: Module `/nix/store/dmf2j3whlc2jhxfvgiw55zmxchl3bw6r-source/config' has an unsupported attribute `cfg'. This is caused by introducing a top-level `config' or `options' attribute. Add configuration attributes immediately on the top level instead, or move all of them (namely: cfg) into the explicit `config' attribute.

I guess you could go back to the setAttrbByPath thing, but I really think you’re trying to micro-optimize something that really doesn’t matter, and you’re only going to make things more complicated.

2 Likes

maybe you’re right, i will take what i can, doing the cfg seems fine, at most seems like i have to repeat the attbset trice. so i guess its something.