mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-25 16:33:15 +00:00
pkgs-lib: Implement settings format for Elixir
This commit is contained in:
parent
ea84cd6895
commit
6a96ddb675
@ -66,6 +66,45 @@ have a predefined type and string generator already declared under
|
||||
and returning a set with TOML-specific attributes `type` and
|
||||
`generate` as specified [below](#pkgs-formats-result).
|
||||
|
||||
`pkgs.formats.elixirConf { elixir ? pkgs.elixir }`
|
||||
|
||||
: A function taking an attribute set with values
|
||||
|
||||
`elixir`
|
||||
|
||||
: The Elixir package which will be used to format the generated output
|
||||
|
||||
It returns a set with Elixir-Config-specific attributes `type`, `lib`, and
|
||||
`generate` as specified [below](#pkgs-formats-result).
|
||||
|
||||
The `lib` attribute contains functions to be used in settings, for
|
||||
generating special Elixir values:
|
||||
|
||||
`mkRaw elixirCode`
|
||||
|
||||
: Outputs the given string as raw Elixir code
|
||||
|
||||
`mkGetEnv { envVariable, fallback ? null }`
|
||||
|
||||
: Makes the configuration fetch an environment variable at runtime
|
||||
|
||||
`mkAtom atom`
|
||||
|
||||
: Outputs the given string as an Elixir atom, instead of the default
|
||||
Elixir binary string. Note: lowercase atoms still needs to be prefixed
|
||||
with `:`
|
||||
|
||||
`mkTuple array`
|
||||
|
||||
: Outputs the given array as an Elixir tuple, instead of the default
|
||||
Elixir list
|
||||
|
||||
`mkMap attrset`
|
||||
|
||||
: Outputs the given attribute set as an Elixir map, instead of the
|
||||
default Elixir keyword list
|
||||
|
||||
|
||||
::: {#pkgs-formats-result}
|
||||
These functions all return an attribute set with these values:
|
||||
:::
|
||||
@ -74,6 +113,12 @@ These functions all return an attribute set with these values:
|
||||
|
||||
: A module system type representing a value of the format
|
||||
|
||||
`lib`
|
||||
|
||||
: Utility functions for convenience, or special interactions with the format.
|
||||
This attribute is optional. It may contain inside a `types` attribute
|
||||
containing types specific to this format.
|
||||
|
||||
`generate` *`filename jsonValue`*
|
||||
|
||||
: A function that can render a value of the format to a file. Returns
|
||||
|
@ -137,6 +137,97 @@
|
||||
</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:
|
||||
@ -152,6 +243,19 @@
|
||||
</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>
|
||||
|
@ -14,6 +14,15 @@ rec {
|
||||
# The description needs to be overwritten for recursive types
|
||||
type = ...;
|
||||
|
||||
# Utility functions for convenience, or special interactions with the
|
||||
# format (optional)
|
||||
lib = {
|
||||
exampleFunction = ...
|
||||
# Types specific to the format (optional)
|
||||
types = { ... };
|
||||
...
|
||||
};
|
||||
|
||||
# generate :: Name -> Value -> Path
|
||||
# A function for generating a file with a value of such a type
|
||||
generate = ...;
|
||||
@ -147,4 +156,202 @@ rec {
|
||||
'';
|
||||
|
||||
};
|
||||
|
||||
/* For configurations of Elixir project, like config.exs or runtime.exs
|
||||
|
||||
Most Elixir project are configured using the [Config] Elixir DSL
|
||||
|
||||
Since Elixir has more types than Nix, we need a way to map Nix types to
|
||||
more than 1 Elixir type. To that end, this format provides its own library,
|
||||
and its own set of types.
|
||||
|
||||
To be more detailed, a Nix attribute set could correspond in Elixir to a
|
||||
[Keyword list] (the more common type), or it could correspond to a [Map].
|
||||
|
||||
A Nix string could correspond in Elixir to a [String] (also called
|
||||
"binary"), an [Atom], or a list of chars (usually discouraged).
|
||||
|
||||
A Nix array could correspond in Elixir to a [List] or a [Tuple].
|
||||
|
||||
Some more types exists, like records, regexes, but since they are less used,
|
||||
we can leave the `mkRaw` function as an escape hatch.
|
||||
|
||||
For more information on how to use this format in modules, please refer to
|
||||
the Elixir section of the Nixos documentation.
|
||||
|
||||
TODO: special Elixir values doesn't show up nicely in the documentation
|
||||
|
||||
[Config]: <https://hexdocs.pm/elixir/Config.html>
|
||||
[Keyword list]: <https://hexdocs.pm/elixir/Keyword.html>
|
||||
[Map]: <https://hexdocs.pm/elixir/Map.html>
|
||||
[String]: <https://hexdocs.pm/elixir/String.html>
|
||||
[Atom]: <https://hexdocs.pm/elixir/Atom.html>
|
||||
[List]: <https://hexdocs.pm/elixir/List.html>
|
||||
[Tuple]: <https://hexdocs.pm/elixir/Tuple.html>
|
||||
*/
|
||||
elixirConf = { elixir ? pkgs.elixir }:
|
||||
with lib; let
|
||||
toElixir = value: with builtins;
|
||||
if value == null then "nil" else
|
||||
if value == true then "true" else
|
||||
if value == false then "false" else
|
||||
if isInt value || isFloat value then toString value else
|
||||
if isString value then string value else
|
||||
if isAttrs value then attrs value else
|
||||
if isList value then list value else
|
||||
abort "formats.elixirConf: should never happen (value = ${value})";
|
||||
|
||||
escapeElixir = escape [ "\\" "#" "\"" ];
|
||||
string = value: "\"${escapeElixir value}\"";
|
||||
|
||||
attrs = set:
|
||||
if set ? _elixirType then specialType set
|
||||
else
|
||||
let
|
||||
toKeyword = name: value: "${name}: ${toElixir value}";
|
||||
keywordList = concatStringsSep ", " (mapAttrsToList toKeyword set);
|
||||
in
|
||||
"[" + keywordList + "]";
|
||||
|
||||
listContent = values: concatStringsSep ", " (map toElixir values);
|
||||
|
||||
list = values: "[" + (listContent values) + "]";
|
||||
|
||||
specialType = { value, _elixirType }:
|
||||
if _elixirType == "raw" then value else
|
||||
if _elixirType == "atom" then value else
|
||||
if _elixirType == "map" then elixirMap value else
|
||||
if _elixirType == "tuple" then tuple value else
|
||||
abort "formats.elixirConf: should never happen (_elixirType = ${_elixirType})";
|
||||
|
||||
elixirMap = set:
|
||||
let
|
||||
toEntry = name: value: "${toElixir name} => ${toElixir value}";
|
||||
entries = concatStringsSep ", " (mapAttrsToList toEntry set);
|
||||
in
|
||||
"%{${entries}}";
|
||||
|
||||
tuple = values: "{${listContent values}}";
|
||||
|
||||
toConf = values:
|
||||
let
|
||||
keyConfig = rootKey: key: value:
|
||||
"config ${rootKey}, ${key}, ${toElixir value}";
|
||||
keyConfigs = rootKey: values: mapAttrsToList (keyConfig rootKey) values;
|
||||
rootConfigs = flatten (mapAttrsToList keyConfigs values);
|
||||
in
|
||||
''
|
||||
import Config
|
||||
|
||||
${concatStringsSep "\n" rootConfigs}
|
||||
'';
|
||||
in
|
||||
{
|
||||
type = with lib.types; let
|
||||
valueType = nullOr
|
||||
(oneOf [
|
||||
bool
|
||||
int
|
||||
float
|
||||
str
|
||||
(attrsOf valueType)
|
||||
(listOf valueType)
|
||||
]) // {
|
||||
description = "Elixir value";
|
||||
};
|
||||
in
|
||||
attrsOf (attrsOf (valueType));
|
||||
|
||||
lib =
|
||||
let
|
||||
mkRaw = value: {
|
||||
inherit value;
|
||||
_elixirType = "raw";
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
inherit mkRaw;
|
||||
|
||||
/* Fetch an environment variable at runtime, with optional fallback
|
||||
*/
|
||||
mkGetEnv = { envVariable, fallback ? null }:
|
||||
mkRaw "System.get_env(${toElixir envVariable}, ${toElixir fallback})";
|
||||
|
||||
/* Make an Elixir atom.
|
||||
|
||||
Note: lowercase atoms still need to be prefixed by ':'
|
||||
*/
|
||||
mkAtom = value: {
|
||||
inherit value;
|
||||
_elixirType = "atom";
|
||||
};
|
||||
|
||||
/* Make an Elixir tuple out of a list.
|
||||
*/
|
||||
mkTuple = value: {
|
||||
inherit value;
|
||||
_elixirType = "tuple";
|
||||
};
|
||||
|
||||
/* Make an Elixir map out of an attribute set.
|
||||
*/
|
||||
mkMap = value: {
|
||||
inherit value;
|
||||
_elixirType = "map";
|
||||
};
|
||||
|
||||
/* Contains Elixir types. Every type it exports can also be replaced
|
||||
by raw Elixir code (i.e. every type is `either type rawElixir`).
|
||||
|
||||
It also reexports standard types, wrapping them so that they can
|
||||
also be raw Elixir.
|
||||
*/
|
||||
types = with lib.types; let
|
||||
isElixirType = type: x: (x._elixirType or "") == type;
|
||||
|
||||
rawElixir = mkOptionType {
|
||||
name = "rawElixir";
|
||||
description = "raw elixir";
|
||||
check = isElixirType "raw";
|
||||
};
|
||||
|
||||
elixirOr = other: either other rawElixir;
|
||||
in
|
||||
{
|
||||
inherit rawElixir elixirOr;
|
||||
|
||||
atom = elixirOr (mkOptionType {
|
||||
name = "elixirAtom";
|
||||
description = "elixir atom";
|
||||
check = isElixirType "atom";
|
||||
});
|
||||
|
||||
tuple = elixirOr (mkOptionType {
|
||||
name = "elixirTuple";
|
||||
description = "elixir tuple";
|
||||
check = isElixirType "tuple";
|
||||
});
|
||||
|
||||
map = elixirOr (mkOptionType {
|
||||
name = "elixirMap";
|
||||
description = "elixir map";
|
||||
check = isElixirType "map";
|
||||
});
|
||||
# Wrap standard types, since anything in the Elixir configuration
|
||||
# can be raw Elixir
|
||||
} // lib.mapAttrs (_name: type: elixirOr type) lib.types;
|
||||
};
|
||||
|
||||
generate = name: value: pkgs.runCommandNoCC name
|
||||
{
|
||||
value = toConf value;
|
||||
passAsFile = [ "value" ];
|
||||
nativeBuildInputs = [ elixir ];
|
||||
} ''
|
||||
cp "$valuePath" "$out"
|
||||
mix format "$out"
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user