Announcing autodocodec-nix and Nix integration for opt-env-conf

This post announces autodocodec-nix, a new companion library for autodocodec that lets you generate a NixOS module options from any Codec.

Every one of my products comes with some sort of Configuration type that has a HasCodec instance. The autodocodec library lets me easily generate documentation for this type. However, I then write a nix/nixos-module.nix that a NixOS option and type that contains mostly the same information. As of today, the autodocodec-nix library lets you generate these options, and the opt-env-conf library lets you output the options for your settings parser as well.

The new autodocodec-nix library

I often use Nix to produce a configuration file that will be parsed by Haskell code with autodocodec. This means that the Nix code has to produce values with the right shape. There is a nice mechanism in nixpkgs' NixOS modules for this: lib.types.

The new autodocodec-nix library lets you generate those options from a Codec. Here is an example codec:

-- | A complex example type
data Example = Example
  { exampleText :: !Text,
    exampleBool :: !Bool
  }
  deriving (Show, Eq, Generic)

instance HasCodec Example where
  codec = object "Example" objectCodec $
    Example
      <$> requiredField "text" "a text" .= exampleText
      <*> requiredField "bool" "a bool" .= exampleBool

And here are the options that autodocodec-nix generates:

{ lib }:
{
  bool = lib.mkOption {
    default = null;
    description = "a bool";
    type = lib.types.nullOr lib.types.bool;
  };
  text = lib.mkOption {
    default = null;
    description = "a text";
    type = lib.types.nullOr lib.types.str;
  };
}

New opt-env-conf integration

The opt-env-conf library also just got an upgrade. It now integrates with autodocodec-nix to produce Nix options for the configuration file that it parses.

The example code in the opt-env-conf announcement blogpost now lets you output the following with the hidden --render-nix-options command:

{ lib }:
{
  log-level = lib.mkOption {
    default = null;
    description = "minimal severity of log messages";
    type = lib.types.nullOr lib.types.str;
  };
  payment = lib.mkOption {
    default = {};
    type = lib.types.submodule {
      options = {
        currency = lib.mkOption {
          default = null;
          description = "Currency";
          type = lib.types.nullOr lib.types.str;
        };
        public-key = lib.mkOption {
          default = null;
          description = "Public key";
          type = lib.types.nullOr lib.types.str;
        };
        secret-key = lib.mkOption {
          default = null;
          description = "Secret key";
          type = lib.types.nullOr lib.types.str;
        };
      };
    };
  };
}
Previous
How to get the String out of the IO String in Haskell

Start your Haskell project from a template

Haskell templates
Next
Announcing opt-env-conf