<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-settings-options">
  <title>Options for Program Settings</title>
  <para>
    Many programs have configuration files where program-specific
    settings can be declared. File formats can be separated into two
    categories:
  </para>
  <itemizedlist>
    <listitem>
      <para>
        Nix-representable ones: These can trivially be mapped to a
        subset of Nix syntax. E.g. JSON is an example, since its values
        like <literal>{&quot;foo&quot;:{&quot;bar&quot;:10}}</literal>
        can be mapped directly to Nix:
        <literal>{ foo = { bar = 10; }; }</literal>. Other examples are
        INI, YAML and TOML. The following section explains the
        convention for these settings.
      </para>
    </listitem>
    <listitem>
      <para>
        Non-nix-representable ones: These can't be trivially mapped to a
        subset of Nix syntax. Most generic programming languages are in
        this group, e.g. bash, since the statement
        <literal>if true; then echo hi; fi</literal> doesn't have a
        trivial representation in Nix.
      </para>
      <para>
        Currently there are no fixed conventions for these, but it is
        common to have a <literal>configFile</literal> option for
        setting the configuration file path directly. The default value
        of <literal>configFile</literal> can be an auto-generated file,
        with convenient options for controlling the contents. For
        example an option of type <literal>attrsOf str</literal> can be
        used for representing environment variables which generates a
        section like <literal>export FOO=&quot;foo&quot;</literal>.
        Often it can also be useful to also include an
        <literal>extraConfig</literal> option of type
        <literal>lines</literal> to allow arbitrary text after the
        autogenerated part of the file.
      </para>
    </listitem>
  </itemizedlist>
  <section xml:id="sec-settings-nix-representable">
    <title>Nix-representable Formats (JSON, YAML, TOML, INI,
    ...)</title>
    <para>
      By convention, formats like this are handled with a generic
      <literal>settings</literal> option, representing the full program
      configuration as a Nix value. The type of this option should
      represent the format. The most common formats have a predefined
      type and string generator already declared under
      <literal>pkgs.formats</literal>:
    </para>
    <variablelist>
      <varlistentry>
        <term>
          <literal>pkgs.formats.javaProperties</literal> {
          <emphasis><literal>comment</literal></emphasis> ?
          <literal>&quot;Generated with Nix&quot;</literal> }
        </term>
        <listitem>
          <para>
            A function taking an attribute set with values
          </para>
          <variablelist>
            <varlistentry>
              <term>
                <literal>comment</literal>
              </term>
              <listitem>
                <para>
                  A string to put at the start of the file in a comment.
                  It can have multiple lines.
                </para>
              </listitem>
            </varlistentry>
          </variablelist>
          <para>
            It returns the <literal>type</literal>:
            <literal>attrsOf str</literal> and a function
            <literal>generate</literal> to build a Java
            <literal>.properties</literal> file, taking care of the
            correct escaping, etc.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>pkgs.formats.json</literal> { }
        </term>
        <listitem>
          <para>
            A function taking an empty attribute set (for future
            extensibility) and returning a set with JSON-specific
            attributes <literal>type</literal> and
            <literal>generate</literal> as specified
            <link linkend="pkgs-formats-result">below</link>.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>pkgs.formats.yaml</literal> { }
        </term>
        <listitem>
          <para>
            A function taking an empty attribute set (for future
            extensibility) and returning a set with YAML-specific
            attributes <literal>type</literal> and
            <literal>generate</literal> as specified
            <link linkend="pkgs-formats-result">below</link>.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>pkgs.formats.ini</literal> {
          <emphasis><literal>listsAsDuplicateKeys</literal></emphasis> ?
          false, <emphasis><literal>listToValue</literal></emphasis> ?
          null, ... }
        </term>
        <listitem>
          <para>
            A function taking an attribute set with values
          </para>
          <variablelist>
            <varlistentry>
              <term>
                <literal>listsAsDuplicateKeys</literal>
              </term>
              <listitem>
                <para>
                  A boolean for controlling whether list values can be
                  used to represent duplicate INI keys
                </para>
              </listitem>
            </varlistentry>
            <varlistentry>
              <term>
                <literal>listToValue</literal>
              </term>
              <listitem>
                <para>
                  A function for turning a list of values into a single
                  value.
                </para>
              </listitem>
            </varlistentry>
          </variablelist>
          <para>
            It returns a set with INI-specific attributes
            <literal>type</literal> and <literal>generate</literal> as
            specified <link linkend="pkgs-formats-result">below</link>.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>pkgs.formats.toml</literal> { }
        </term>
        <listitem>
          <para>
            A function taking an empty attribute set (for future
            extensibility) and returning a set with TOML-specific
            attributes <literal>type</literal> and
            <literal>generate</literal> as specified
            <link linkend="pkgs-formats-result">below</link>.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>pkgs.formats.elixirConf { elixir ? pkgs.elixir }</literal>
        </term>
        <listitem>
          <para>
            A function taking an attribute set with values
          </para>
          <variablelist>
            <varlistentry>
              <term>
                <literal>elixir</literal>
              </term>
              <listitem>
                <para>
                  The Elixir package which will be used to format the
                  generated output
                </para>
              </listitem>
            </varlistentry>
          </variablelist>
          <para>
            It returns a set with Elixir-Config-specific attributes
            <literal>type</literal>, <literal>lib</literal>, and
            <literal>generate</literal> as specified
            <link linkend="pkgs-formats-result">below</link>.
          </para>
          <para>
            The <literal>lib</literal> attribute contains functions to
            be used in settings, for generating special Elixir values:
          </para>
          <variablelist>
            <varlistentry>
              <term>
                <literal>mkRaw elixirCode</literal>
              </term>
              <listitem>
                <para>
                  Outputs the given string as raw Elixir code
                </para>
              </listitem>
            </varlistentry>
            <varlistentry>
              <term>
                <literal>mkGetEnv { envVariable, fallback ? null }</literal>
              </term>
              <listitem>
                <para>
                  Makes the configuration fetch an environment variable
                  at runtime
                </para>
              </listitem>
            </varlistentry>
            <varlistentry>
              <term>
                <literal>mkAtom atom</literal>
              </term>
              <listitem>
                <para>
                  Outputs the given string as an Elixir atom, instead of
                  the default Elixir binary string. Note: lowercase
                  atoms still needs to be prefixed with
                  <literal>:</literal>
                </para>
              </listitem>
            </varlistentry>
            <varlistentry>
              <term>
                <literal>mkTuple array</literal>
              </term>
              <listitem>
                <para>
                  Outputs the given array as an Elixir tuple, instead of
                  the default Elixir list
                </para>
              </listitem>
            </varlistentry>
            <varlistentry>
              <term>
                <literal>mkMap attrset</literal>
              </term>
              <listitem>
                <para>
                  Outputs the given attribute set as an Elixir map,
                  instead of the default Elixir keyword list
                </para>
              </listitem>
            </varlistentry>
          </variablelist>
        </listitem>
      </varlistentry>
    </variablelist>
    <para xml:id="pkgs-formats-result">
      These functions all return an attribute set with these values:
    </para>
    <variablelist>
      <varlistentry>
        <term>
          <literal>type</literal>
        </term>
        <listitem>
          <para>
            A module system type representing a value of the format
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>lib</literal>
        </term>
        <listitem>
          <para>
            Utility functions for convenience, or special interactions
            with the format. This attribute is optional. It may contain
            inside a <literal>types</literal> attribute containing types
            specific to this format.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>generate</literal>
          <emphasis><literal>filename jsonValue</literal></emphasis>
        </term>
        <listitem>
          <para>
            A function that can render a value of the format to a file.
            Returns a file path.
          </para>
          <note>
            <para>
              This function puts the value contents in the Nix store. So
              this should be avoided for secrets.
            </para>
          </note>
        </listitem>
      </varlistentry>
    </variablelist>
    <anchor xml:id="ex-settings-nix-representable" />
    <para>
      <emphasis role="strong">Example: Module with conventional
      <literal>settings</literal> option</emphasis>
    </para>
    <para>
      The following shows a module for an example program that uses a
      JSON configuration file. It demonstrates how above values can be
      used, along with some other related best practices. See the
      comments for explanations.
    </para>
    <programlisting language="bash">
{ options, config, lib, pkgs, ... }:
let
  cfg = config.services.foo;
  # Define the settings format used for this program
  settingsFormat = pkgs.formats.json {};
in {

  options.services.foo = {
    enable = lib.mkEnableOption &quot;foo service&quot;;

    settings = lib.mkOption {
      # Setting this type allows for correct merging behavior
      type = settingsFormat.type;
      default = {};
      description = ''
        Configuration for foo, see
        &lt;link xlink:href=&quot;https://example.com/docs/foo&quot;/&gt;
        for supported settings.
      '';
    };
  };

  config = lib.mkIf cfg.enable {
    # We can assign some default settings here to make the service work by just
    # enabling it. We use `mkDefault` for values that can be changed without
    # problems
    services.foo.settings = {
      # Fails at runtime without any value set
      log_level = lib.mkDefault &quot;WARN&quot;;

      # We assume systemd's `StateDirectory` is used, so we require this value,
      # therefore no mkDefault
      data_path = &quot;/var/lib/foo&quot;;

      # Since we use this to create a user we need to know the default value at
      # eval time
      user = lib.mkDefault &quot;foo&quot;;
    };

    environment.etc.&quot;foo.json&quot;.source =
      # The formats generator function takes a filename and the Nix value
      # representing the format value and produces a filepath with that value
      # rendered in the format
      settingsFormat.generate &quot;foo-config.json&quot; cfg.settings;

    # We know that the `user` attribute exists because we set a default value
    # for it above, allowing us to use it without worries here
    users.users.${cfg.settings.user} = { isSystemUser = true; };

    # ...
  };
}
</programlisting>
    <section xml:id="sec-settings-attrs-options">
      <title>Option declarations for attributes</title>
      <para>
        Some <literal>settings</literal> attributes may deserve some
        extra care. They may need a different type, default or merging
        behavior, or they are essential options that should show their
        documentation in the manual. This can be done using
        <xref linkend="sec-freeform-modules" />.
      </para>
      <para>
        We extend above example using freeform modules to declare an
        option for the port, which will enforce it to be a valid integer
        and make it show up in the manual.
      </para>
      <anchor xml:id="ex-settings-typed-attrs" />
      <para>
        <emphasis role="strong">Example: Declaring a type-checked
        <literal>settings</literal> attribute</emphasis>
      </para>
      <programlisting language="bash">
settings = lib.mkOption {
  type = lib.types.submodule {

    freeformType = settingsFormat.type;

    # Declare an option for the port such that the type is checked and this option
    # is shown in the manual.
    options.port = lib.mkOption {
      type = lib.types.port;
      default = 8080;
      description = ''
        Which port this service should listen on.
      '';
    };

  };
  default = {};
  description = ''
    Configuration for Foo, see
    &lt;link xlink:href=&quot;https://example.com/docs/foo&quot;/&gt;
    for supported values.
  '';
};
</programlisting>
    </section>
  </section>
</section>
