summary refs log tree commit diff
path: root/nixos/doc/manual/from_md/development/freeform-modules.section.xml
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/doc/manual/from_md/development/freeform-modules.section.xml')
-rw-r--r--nixos/doc/manual/from_md/development/freeform-modules.section.xml87
1 files changed, 87 insertions, 0 deletions
diff --git a/nixos/doc/manual/from_md/development/freeform-modules.section.xml b/nixos/doc/manual/from_md/development/freeform-modules.section.xml
new file mode 100644
index 00000000000..86a9cf3140d
--- /dev/null
+++ b/nixos/doc/manual/from_md/development/freeform-modules.section.xml
@@ -0,0 +1,87 @@
+<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-freeform-modules">
+  <title>Freeform modules</title>
+  <para>
+    Freeform modules allow you to define values for option paths that
+    have not been declared explicitly. This can be used to add
+    attribute-specific types to what would otherwise have to be
+    <literal>attrsOf</literal> options in order to accept all attribute
+    names.
+  </para>
+  <para>
+    This feature can be enabled by using the attribute
+    <literal>freeformType</literal> to define a freeform type. By doing
+    this, all assignments without an associated option will be merged
+    using the freeform type and combined into the resulting
+    <literal>config</literal> set. Since this feature nullifies name
+    checking for entire option trees, it is only recommended for use in
+    submodules.
+  </para>
+  <anchor xml:id="ex-freeform-module" />
+  <para>
+    <emphasis role="strong">Example: Freeform submodule</emphasis>
+  </para>
+  <para>
+    The following shows a submodule assigning a freeform type that
+    allows arbitrary attributes with <literal>str</literal> values below
+    <literal>settings</literal>, but also declares an option for the
+    <literal>settings.port</literal> attribute to have it type-checked
+    and assign a default value. See
+    <link linkend="ex-settings-typed-attrs">Example: Declaring a
+    type-checked <literal>settings</literal> attribute</link> for a more
+    complete example.
+  </para>
+  <programlisting language="bash">
+{ lib, config, ... }: {
+
+  options.settings = lib.mkOption {
+    type = lib.types.submodule {
+
+      freeformType = with lib.types; attrsOf str;
+
+      # We want this attribute to be checked for the correct type
+      options.port = lib.mkOption {
+        type = lib.types.port;
+        # Declaring the option also allows defining a default value
+        default = 8080;
+      };
+
+    };
+  };
+}
+</programlisting>
+  <para>
+    And the following shows what such a module then allows
+  </para>
+  <programlisting language="bash">
+{
+  # Not a declared option, but the freeform type allows this
+  settings.logLevel = &quot;debug&quot;;
+
+  # Not allowed because the the freeform type only allows strings
+  # settings.enable = true;
+
+  # Allowed because there is a port option declared
+  settings.port = 80;
+
+  # Not allowed because the port option doesn't allow strings
+  # settings.port = &quot;443&quot;;
+}
+</programlisting>
+  <note>
+    <para>
+      Freeform attributes cannot depend on other attributes of the same
+      set without infinite recursion:
+    </para>
+    <programlisting language="bash">
+{
+  # This throws infinite recursion encountered
+  settings.logLevel = lib.mkIf (config.settings.port == 80) &quot;debug&quot;;
+}
+</programlisting>
+    <para>
+      To prevent this, declare options for all attributes that need to
+      depend on others. For above example this means to declare
+      <literal>logLevel</literal> to be an option.
+    </para>
+  </note>
+</section>