Open roberth opened 2 years ago
I feel like this is a bit too unintuitive. How about just
let
settingsFormat = pkgs.formats.json {};
configFile = settingsFormat.generate "foo-config.json" cfg.settings;
in
{
options = {
settings = lib.mkOption {
type = lib.types.submodule {
# Declare that the settings option supports arbitrary format values, json here
freeformType = settingsFormat.type;
};
};
};
config.environment.etc."foo.json".source = configFile;
}
That's just the "status quo" example and then putting generate
in a binding, right?
It's definitely a step in the right direction, but not that much of a difference.
I would argue that putting everything about the file in a single binding is actually more intuitive, but that just goes to show that "intuitive" is a subjective, ill-defined concept.
What I was actually going for is, considering that we now know how these interfaces are used, we could unify things a bit. I was inspired by this newcomer https://github.com/NixOS/nixpkgs/pull/139075#discussion_r739765668.
config.environment.etc."foo.json".source = configFile;
This is nice. We could set outPath
on the "file" attrset. I'll update my example and proof of concept (in the details section).
Done. It is a bit radical, I admit.
But radically intuitive, because it's what a newcomer may expect.
Something that can also be done is instead of using .source, .text could be used
let
config = format.output cfg.settings; # .output would need to be added
in
{
environment.etc."bla.json".text = config;
}
instead of using .source, .text could be used
This would require that the final text can be generated at evaluation time and this is how lib.generators
operates. While possible for JSON and some simple formats, this isn't always convenient, because we don't get to use remarshal for example. It also precludes validation using other existing tools.
So while it is a valid thing to do, it doesn't make much sense for formats
, which is separate from lib.generators
for these reasons.
lib.generators
does have a reason to exist besides continuity, which is for working with configuration that contains secrets in tools that can work with values without writing them to the store, such as NixOps and nix-instantiate --read-only
.
Another proposal:
let
settingsFormat = pkgs.formats.json {};
in
{
imports=[(mkModuleFiles settingsFormat ["services" "foo" "settings"] "bla.json" config.services.foo.enable)];
}
It could even be shortned to
{
imports=[(mkSettingsModule settingsFormat ["services" "foo"] "bla.json")];
}
for the common case of options called enable
and settings
Describe the challenge
Dealing with types and format functions separately is slightly cumbersome.
Not much of a challenge really, but let me present a potential solution using laziness / a technique called "circular programming".
Status quo
The relevant bits from the example from Detailed design in RFC 42.
New situation
All arguments are specified in one place, which simplifies the api.
This should be possible to implement, thanks to laziness. However, the above formulation does create a dependency from some items in the
options
tree topkgs
, which is defined inconfig
. This may work, becausepkgs
itself isn't defined throughpkgs.fileFormats
, but perhaps there's something I'm not seeing. An alternative may be as follows:While it is easier to see that this can be implemented without causing too much strictness, it makes
lib
depend on the interface ofpkgs
, which is not desirable.Proof of concept
I've tested the idea in a single file; see details below.
Notify maintainers
@roberth @Infinisil
Metadata
Maintainer information: