Packages

A type-safe configuration loader for Gleam that supports JSON, YAML, and TOML with automatic format detection, environment variable resolution, and profile-based configuration.

Current section

Files

Jump to
yodel src yodel.erl
Raw

src/yodel.erl

-module(yodel).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yodel.gleam").
-export([get_string/2, get_string_or/3, parse_string/2, get_int/2, get_int_or/3, parse_int/2, get_float/2, get_float_or/3, parse_float/2, get_bool/2, get_bool_or/3, parse_bool/2, default_options/0, with_format/2, with_resolve_enabled/2, resolve_enabled/1, resolve_disabled/1, with_resolve_mode/2, with_config_base_name/2, with_profiles/2, with_profile_env_var/2, describe_config_error/1, as_auto/1, as_json/1, as_toml/1, as_yaml/1, load_with_options/2, load/1, with_resolve_strict/1, with_resolve_lenient/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Yodel is a type-safe configuration loader for Gleam that handles JSON,\n"
" YAML, and TOML configs with automatic format detection, environment variable\n"
" resolution, profile-based configuration, and an intuitive dot-notation API.\n"
"\n"
" # Quick Start\n"
"\n"
" ```gleam\n"
" import yodel\n"
"\n"
" let assert Ok(ctx) = yodel.load(\"config.toml\")\n"
" yodel.get_string(ctx, \"database.host\") // \"localhost\"\n"
" ```\n"
"\n"
" # Key Features\n"
"\n"
" ## Multiple Format Support\n"
"\n"
" Load JSON, YAML, or TOML configuration files with automatic format detection:\n"
"\n"
" ```gleam\n"
" yodel.load(\"config.yaml\") // Auto-detects YAML\n"
" yodel.load(\"config.toml\") // Auto-detects TOML\n"
" yodel.load(\"config.json\") // Auto-detects JSON\n"
" ```\n"
"\n"
" ## Environment Variable Resolution\n"
"\n"
" Use placeholders to inject environment variables into your configuration:\n"
"\n"
" ```bash\n"
" export DATABASE_HOST=\"prod.db.example.com\"\n"
" export API_KEY=\"secret-key-123\"\n"
" ```\n"
"\n"
" ```yaml\n"
" # config.yaml\n"
" database:\n"
" host: ${DATABASE_HOST}\n"
" password: ${DB_PASSWORD:default-password}\n"
" api:\n"
" key: ${API_KEY}\n"
" ```\n"
"\n"
" Placeholders support:\n"
" - Simple substitution: `${VAR_NAME}`\n"
" - Default values: `${VAR_NAME:default}`\n"
" - Nested placeholders: `${VAR1:${VAR2:fallback}}`\n"
"\n"
" ## Profile-Based Configuration\n"
"\n"
" Manage environment-specific configurations with profiles that automatically\n"
" merge over your base configuration:\n"
"\n"
" ```text\n"
" config/\n"
" ├── config.yaml # Base configuration (all environments)\n"
" ├── config-dev.yaml # Development overrides\n"
" ├── config-staging.yaml # Staging overrides\n"
" └── config-prod.yaml # Production overrides\n"
" ```\n"
"\n"
" Activate profiles via environment variable:\n"
"\n"
" ```bash\n"
" export YODEL_PROFILES=dev,local\n"
" ```\n"
"\n"
" ```gleam\n"
" // Loads config.yaml, then config-dev.yaml, then config-local.yaml\n"
" // Later profiles override earlier ones\n"
" let assert Ok(ctx) = yodel.load(\"./config\")\n"
" ```\n"
"\n"
" Or programmatically:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.with_profiles([\"dev\", \"local\"])\n"
" |> yodel.load_with_options(\"./config\")\n"
" ```\n"
"\n"
" **Note:** The `YODEL_PROFILES` environment variable takes precedence over\n"
" programmatically set profiles, allowing runtime override without code changes.\n"
" This makes it easy to change environments via deployment configuration.\n"
"\n"
" ## Type-Safe Value Access\n"
"\n"
" Access configuration values with type safety and helpful error messages:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) = yodel.load(\"config.yaml\")\n"
"\n"
" // Get values with type checking\n"
" let assert Ok(host) = yodel.get_string(ctx, \"database.host\")\n"
" let assert Ok(port) = yodel.get_int(ctx, \"database.port\")\n"
" let assert Ok(timeout) = yodel.get_float(ctx, \"api.timeout\")\n"
" let assert Ok(enabled) = yodel.get_bool(ctx, \"features.new_ui\")\n"
"\n"
" // Provide defaults for optional values\n"
" let host = yodel.get_string_or(ctx, \"cache.host\", \"localhost\")\n"
" let port = yodel.get_int_or(ctx, \"cache.port\", 6379)\n"
"\n"
" // Parse values from strings when needed\n"
" let assert Ok(port) = yodel.parse_int(ctx, \"port\") // \"8080\" → 8080\n"
" ```\n"
).
-file("src/yodel.gleam", 310).
?DOC(
" Get a string value from the configuration.\n"
" If the value is not a string, an error will be returned.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" case yodel.get_string(ctx, \"foo\") {\n"
" Ok(value) -> value // \"bar\"\n"
" Error(e) -> Error(e)\n"
" }\n"
" ```\n"
).
-spec get_string(yodel@internal@context:context(), binary()) -> {ok, binary()} |
{error, yodel@errors:properties_error()}.
get_string(Ctx, Key) ->
yodel@internal@context:get_string(Ctx, Key).
-file("src/yodel.gleam", 321).
?DOC(
" Get a string value from the configuration, or a default value if the key is not found.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let value = yodel.get_string_or(ctx, \"foo\", \"default\")\n"
" ```\n"
).
-spec get_string_or(yodel@internal@context:context(), binary(), binary()) -> binary().
get_string_or(Ctx, Key, Default) ->
yodel@internal@context:get_string_or(Ctx, Key, Default).
-file("src/yodel.gleam", 339).
?DOC(
" Parse a string value from the configuration.\n"
"\n"
" If the value is not a string, it will be converted to a string.\n"
" An error will be returned if the value is not a string or cannot be\n"
" converted to a string.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" case yodel.parse_string(ctx, \"foo\") {\n"
" Ok(value) -> value // \"42\"\n"
" Error(e) -> Error(e)\n"
" }\n"
" ```\n"
).
-spec parse_string(yodel@internal@context:context(), binary()) -> {ok, binary()} |
{error, yodel@errors:properties_error()}.
parse_string(Ctx, Key) ->
yodel@internal@context:parse_string(Ctx, Key).
-file("src/yodel.gleam", 357).
?DOC(
" Get an integer value from the configuration.\n"
" If the value is not an integer, an error will be returned.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" case yodel.get_int(ctx, \"foo\") {\n"
" Ok(value) -> value // 42\n"
" Error(e) -> Error(e)\n"
" }\n"
" ```\n"
).
-spec get_int(yodel@internal@context:context(), binary()) -> {ok, integer()} |
{error, yodel@errors:properties_error()}.
get_int(Ctx, Key) ->
yodel@internal@context:get_int(Ctx, Key).
-file("src/yodel.gleam", 368).
?DOC(
" Get an integer value from the configuration, or a default value if the key is not found.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let value = yodel.get_int_or(ctx, \"foo\", 42)\n"
" ```\n"
).
-spec get_int_or(yodel@internal@context:context(), binary(), integer()) -> integer().
get_int_or(Ctx, Key, Default) ->
yodel@internal@context:get_int_or(Ctx, Key, Default).
-file("src/yodel.gleam", 386).
?DOC(
" Parse an integer value from the configuration.\n"
"\n"
" If the value is not an integer, it will be converted to an integer.\n"
" An error will be returned if the value is not an integer or cannot be\n"
" converted to an integer.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" case yodel.parse_int(ctx, \"foo\") {\n"
" Ok(value) -> value // 42\n"
" Error(e) -> Error(e)\n"
" }\n"
" ```\n"
).
-spec parse_int(yodel@internal@context:context(), binary()) -> {ok, integer()} |
{error, yodel@errors:properties_error()}.
parse_int(Ctx, Key) ->
yodel@internal@context:parse_int(Ctx, Key).
-file("src/yodel.gleam", 401).
?DOC(
" Get a float value from the configuration.\n"
" If the value is not a float, an error will be returned.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" case yodel.get_float(ctx, \"foo\") {\n"
" Ok(value) -> value // 42.0\n"
" Error(e) -> Error(e)\n"
" }\n"
" ```\n"
).
-spec get_float(yodel@internal@context:context(), binary()) -> {ok, float()} |
{error, yodel@errors:properties_error()}.
get_float(Ctx, Key) ->
yodel@internal@context:get_float(Ctx, Key).
-file("src/yodel.gleam", 412).
?DOC(
" Get a float value from the configuration, or a default value if the key is not found.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let value = yodel.get_float_or(ctx, \"foo\", 42.0)\n"
" ```\n"
).
-spec get_float_or(yodel@internal@context:context(), binary(), float()) -> float().
get_float_or(Ctx, Key, Default) ->
yodel@internal@context:get_float_or(Ctx, Key, Default).
-file("src/yodel.gleam", 430).
?DOC(
" Parse a float value from the configuration.\n"
"\n"
" If the value is not a float, it will be converted to a float.\n"
" An error will be returned if the value is not a float or cannot be\n"
" converted to a float.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" case yodel.parse_float(ctx, \"foo\") {\n"
" Ok(value) -> value // 99.999\n"
" Error(e) -> Error(e)\n"
" }\n"
" ```\n"
).
-spec parse_float(yodel@internal@context:context(), binary()) -> {ok, float()} |
{error, yodel@errors:properties_error()}.
parse_float(Ctx, Key) ->
yodel@internal@context:parse_float(Ctx, Key).
-file("src/yodel.gleam", 445).
?DOC(
" Get a boolean value from the configuration.\n"
" If the value is not a boolean, an error will be returned.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" case yodel.get_bool(ctx, \"foo\") {\n"
" Ok(value) -> value // True\n"
" Error(e) -> Error(e)\n"
" }\n"
" ```\n"
).
-spec get_bool(yodel@internal@context:context(), binary()) -> {ok, boolean()} |
{error, yodel@errors:properties_error()}.
get_bool(Ctx, Key) ->
yodel@internal@context:get_bool(Ctx, Key).
-file("src/yodel.gleam", 456).
?DOC(
" Get a boolean value from the configuration, or a default value if the key is not found.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let value = yodel.get_bool_or(ctx, \"foo\", False)\n"
" ```\n"
).
-spec get_bool_or(yodel@internal@context:context(), binary(), boolean()) -> boolean().
get_bool_or(Ctx, Key, Default) ->
yodel@internal@context:get_bool_or(Ctx, Key, Default).
-file("src/yodel.gleam", 474).
?DOC(
" Parse a bool value from the configuration.\n"
"\n"
" If the value is not a bool, it will be converted to a bool.\n"
" An error will be returned if the value is not a bool or cannot be\n"
" converted to a bool.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" case yodel.parse_bool(ctx, \"foo\") {\n"
" Ok(value) -> value // True\n"
" Error(e) -> Error(e)\n"
" }\n"
" ```\n"
).
-spec parse_bool(yodel@internal@context:context(), binary()) -> {ok, boolean()} |
{error, yodel@errors:properties_error()}.
parse_bool(Ctx, Key) ->
yodel@internal@context:parse_bool(Ctx, Key).
-file("src/yodel.gleam", 495).
?DOC(
" The default options for loading a configuration file.\n"
"\n"
" Default Options:\n"
"\n"
" - Format: `format_auto`\n"
" - Resolve Enabled: `True`\n"
" - Resolve Mode: `resolve_lenient`\n"
" - Config Base Name: `\"config\"`\n"
" - Profile Environment Variable: `\"YODEL_PROFILES\"`\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.load_with_options(\"config.toml\")\n"
" ```\n"
).
-spec default_options() -> yodel@options:options().
default_options() ->
yodel@options:default().
-file("src/yodel.gleam", 509).
?DOC(
" Set the format of the configuration file.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.with_format(yodel.format_json)\n"
" |> yodel.load_with_options(\"config.json\")\n"
" ```\n"
).
-spec with_format(yodel@options:options(), yodel@options:format()) -> yodel@options:options().
with_format(Options, Format) ->
yodel@options:with_format(Options, Format).
-file("src/yodel.gleam", 587).
?DOC(
" Enable or disable placeholder resolution.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.with_resolve_enabled(False)\n"
" |> yodel.load_with_options(\"config.yaml\")\n"
" ```\n"
).
-spec with_resolve_enabled(yodel@options:options(), boolean()) -> yodel@options:options().
with_resolve_enabled(Options, Resolve_enabled) ->
yodel@options:with_resolve_enabled(Options, Resolve_enabled).
-file("src/yodel.gleam", 604).
?DOC(
" Enable placeholder resolution.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.resolve_enabled()\n"
" |> yodel.load_with_options(\"config.yaml\")\n"
" ```\n"
).
-spec resolve_enabled(yodel@options:options()) -> yodel@options:options().
resolve_enabled(Options) ->
with_resolve_enabled(Options, true).
-file("src/yodel.gleam", 618).
?DOC(
" Disable placeholder resolution.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.resolve_disabled()\n"
" |> yodel.load_with_options(\"config.yaml\")\n"
" ```\n"
).
-spec resolve_disabled(yodel@options:options()) -> yodel@options:options().
resolve_disabled(Options) ->
with_resolve_enabled(Options, false).
-file("src/yodel.gleam", 632).
?DOC(
" Set the resolve mode.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.with_resolve_mode(yodel.resolve_strict)\n"
" |> yodel.load_with_options(\"config.json\")\n"
" ```\n"
).
-spec with_resolve_mode(yodel@options:options(), yodel@options:resolve_mode()) -> yodel@options:options().
with_resolve_mode(Options, Resolve_mode) ->
yodel@options:with_resolve_mode(Options, Resolve_mode).
-file("src/yodel.gleam", 686).
?DOC(
" Set the base name for configuration files when loading from a directory.\n"
"\n"
" The base name is used to identify configuration files matching the pattern\n"
" `{base_name}[-{profile}].{ext}`. For example, with base name `\"settings\"`:\n"
"\n"
" - `settings.yaml` → base config\n"
" - `settings-dev.yaml` → dev profile\n"
" - `settings-prod.toml` → prod profile\n"
"\n"
" **Default:** `\"config\"`\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.with_config_base_name(\"settings\")\n"
" |> yodel.load_with_options(\"./config-dir\")\n"
" // Looks for: settings.yaml, settings-dev.yaml, etc.\n"
" ```\n"
).
-spec with_config_base_name(yodel@options:options(), binary()) -> yodel@options:options().
with_config_base_name(Options, Config_base_name) ->
yodel@options:with_config_base_name(Options, Config_base_name).
-file("src/yodel.gleam", 713).
?DOC(
" Set the active configuration profiles to load.\n"
"\n"
" Profiles allow environment-specific configuration overrides.\n"
" Profile configs are merged in the order specified, with later profiles\n"
" overriding earlier ones.\n"
"\n"
" **Note:** The `YODEL_PROFILES` environment variable takes precedence over\n"
" programmatically set profiles.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.with_profiles([\"dev\", \"local\"])\n"
" |> yodel.load_with_options(\"./config-dir\")\n"
" // Loads: config.yaml, config-dev.yaml, config-local.yaml\n"
" // Values in config-local.yaml override config-dev.yaml\n"
" // Values in config-dev.yaml override config.yaml\n"
" ```\n"
).
-spec with_profiles(yodel@options:options(), list(binary())) -> yodel@options:options().
with_profiles(Options, Profiles) ->
yodel@options:with_profiles(Options, Profiles).
-file("src/yodel.gleam", 736).
?DOC(
" Set the environment variable name used to read active profiles.\n"
"\n"
" By default, Yodel reads the `YODEL_PROFILES` environment variable.\n"
" Use this to customize the environment variable name.\n"
"\n"
" **Default:** `\"YODEL_PROFILES\"`\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.with_profile_env_var(\"APP_PROFILES\")\n"
" |> yodel.load_with_options(\"./config\")\n"
" // Now reads from APP_PROFILES instead of YODEL_PROFILES\n"
" ```\n"
).
-spec with_profile_env_var(yodel@options:options(), binary()) -> yodel@options:options().
with_profile_env_var(Options, Profile_env_var) ->
yodel@options:with_profile_env_var(Options, Profile_env_var).
-file("src/yodel.gleam", 744).
?DOC(" Format a `ConfigError` into a human-readable string.\n").
-spec describe_config_error(yodel@errors:config_error()) -> binary().
describe_config_error(Error) ->
yodel@errors:format_config_error(Error).
-file("src/yodel.gleam", 787).
-spec read(
binary(),
fun((binary()) -> {ok, yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()})
) -> {ok, yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()}.
read(Input, Next) ->
_pipe = case yodel@internal@input:get_content(Input) of
{ok, Content} ->
{ok, Content};
{error, E} ->
{error, E}
end,
gleam@result:'try'(_pipe, Next).
-file("src/yodel.gleam", 818).
-spec resolve(
binary(),
yodel@options:options(),
fun((binary()) -> {ok, yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()})
) -> {ok, yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()}.
resolve(Input, Options, Next) ->
_pipe@1 = case yodel@options:is_resolve_enabled(Options) of
true ->
yodel@internal@resolver:resolve_placeholders(Input, Options);
false ->
_pipe = Input,
{ok, _pipe}
end,
gleam@result:'try'(_pipe@1, Next).
-file("src/yodel.gleam", 830).
-spec parse(
binary(),
yodel@options:format(),
fun((yodel@internal@properties:properties()) -> {ok,
yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()})
) -> {ok, yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()}.
parse(Input, Format, Next) ->
_pipe = yodel@internal@parser:parse(Input, Format),
gleam@result:'try'(_pipe, Next).
-file("src/yodel.gleam", 839).
-spec validate(
yodel@internal@properties:properties(),
fun((yodel@internal@properties:properties()) -> {ok,
yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()})
) -> {ok, yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()}.
validate(Props, Handler) ->
_pipe = yodel@internal@validator:validate_properties(Props),
gleam@result:'try'(_pipe, Handler).
-file("src/yodel.gleam", 847).
-spec discover(
binary(),
binary(),
yodel@options:options(),
fun((list(yodel@internal@profiles:config_file())) -> {ok,
yodel@internal@context:context()} |
{error, yodel@errors:config_error()})
) -> {ok, yodel@internal@context:context()} |
{error, yodel@errors:config_error()}.
discover(Directory, Base_name, Options, Next) ->
_pipe = yodel@internal@profiles:discover_configs(
Directory,
Base_name,
Options
),
gleam@result:'try'(_pipe, Next).
-file("src/yodel.gleam", 866).
-spec merge(
list(yodel@internal@properties:properties()),
fun((yodel@internal@properties:properties()) -> {ok,
yodel@internal@context:context()} |
{error, yodel@errors:config_error()})
) -> {ok, yodel@internal@context:context()} |
{error, yodel@errors:config_error()}.
merge(Properties_list, Next) ->
_pipe = gleam@list:fold(
Properties_list,
yodel@internal@properties:new(),
fun yodel@internal@properties:merge/2
),
Next(_pipe).
-file("src/yodel.gleam", 573).
?DOC(
" Attempt to automatically detect the format of the configuration file.\n"
"\n"
" If the input is a file, we first try to detect the format from the file extension.\n"
" If that fails, we try to detect the format from the content of the file.\n"
"\n"
" If the input is a string, we try to detect the format from the content.\n"
"\n"
" If Auto Detection fails, an error will be returned because we can't safely proceed.\n"
" If this happens, try specifying the format using `as_json`, `as_toml`, `as_yaml`, or `with_format`.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.as_auto()\n"
" |> yodel.load_with_options(\"config.yaml\")\n"
" ```\n"
).
-spec as_auto(yodel@options:options()) -> yodel@options:options().
as_auto(Options) ->
with_format(Options, auto).
-file("src/yodel.gleam", 523).
?DOC(
" Set the format of the configuration file to JSON.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.as_json()\n"
" |> yodel.load_with_options(config_content)\n"
" ```\n"
).
-spec as_json(yodel@options:options()) -> yodel@options:options().
as_json(Options) ->
with_format(Options, json).
-file("src/yodel.gleam", 537).
?DOC(
" Set the format of the configuration file to TOML.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.as_toml()\n"
" |> yodel.load_with_options(\"config.toml\")\n"
" ```\n"
).
-spec as_toml(yodel@options:options()) -> yodel@options:options().
as_toml(Options) ->
with_format(Options, toml).
-file("src/yodel.gleam", 551).
?DOC(
" Set the format of the configuration file to YAML.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.as_yaml()\n"
" |> yodel.load_with_options(\"config.json\")\n"
" ```\n"
).
-spec as_yaml(yodel@options:options()) -> yodel@options:options().
as_yaml(Options) ->
with_format(Options, yaml).
-file("src/yodel.gleam", 798).
-spec select(
binary(),
binary(),
yodel@options:options(),
fun((yodel@options:format()) -> {ok, yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()})
) -> {ok, yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()}.
select(Input, Content, Options, Next) ->
Formats = [{format_detector,
<<"toml"/utf8>>,
fun yodel@internal@parsers@toml:detect/1},
{format_detector,
<<"json/yaml"/utf8>>,
fun yodel@internal@parsers@yaml:detect/1}],
_pipe = case yodel@internal@format:get_format(
Input,
Content,
Options,
Formats
) of
json ->
json;
toml ->
toml;
yaml ->
yaml;
auto ->
auto
end,
_pipe@1 = {ok, _pipe},
gleam@result:'try'(_pipe@1, Next).
-file("src/yodel.gleam", 757).
?DOC(" Loads a single content source into Properties.\n").
-spec load_to_properties(binary(), yodel@options:options()) -> {ok,
yodel@internal@properties:properties()} |
{error, yodel@errors:config_error()}.
load_to_properties(Input, Options) ->
read(
Input,
fun(Content) ->
select(
Input,
Content,
Options,
fun(Format) ->
resolve(
Content,
Options,
fun(Resolved) ->
parse(
Resolved,
Format,
fun(Parsed) ->
validate(
Parsed,
fun(Validated) -> {ok, Validated} end
)
end
)
end
)
end
)
end
).
-file("src/yodel.gleam", 749).
?DOC(" Loads a single configuration file or string content.\n").
-spec load_single_file(binary(), yodel@options:options()) -> {ok,
yodel@internal@context:context()} |
{error, yodel@errors:config_error()}.
load_single_file(Input, Options) ->
_pipe = load_to_properties(Input, Options),
gleam@result:map(_pipe, fun yodel@internal@context:new/1).
-file("src/yodel.gleam", 857).
-spec load_dirs(
list(yodel@internal@profiles:config_file()),
yodel@options:options(),
fun((list(yodel@internal@properties:properties())) -> {ok,
yodel@internal@context:context()} |
{error, yodel@errors:config_error()})
) -> {ok, yodel@internal@context:context()} |
{error, yodel@errors:config_error()}.
load_dirs(Config_files, Options, Next) ->
_pipe = gleam@list:try_map(
Config_files,
fun(Cf) -> load_to_properties(erlang:element(2, Cf), Options) end
),
gleam@result:'try'(_pipe, Next).
-file("src/yodel.gleam", 773).
?DOC(
" Loads multiple configuration files from a directory.\n"
"\n"
" Discovers and loads files in order: base config, then active profile configs.\n"
" Later configs override values from earlier ones.\n"
).
-spec load_from_directory(binary(), yodel@options:options()) -> {ok,
yodel@internal@context:context()} |
{error, yodel@errors:config_error()}.
load_from_directory(Directory, Options) ->
Base_name = yodel@options:get_config_base_name(Options),
discover(
Directory,
Base_name,
Options,
fun(Config_files) ->
load_dirs(
Config_files,
Options,
fun(Properties_list) ->
merge(
Properties_list,
fun(Merged) ->
gleam@result:'try'(
yodel@internal@validator:validate_properties(
Merged
),
fun(Validated) ->
{ok, yodel@internal@context:new(Validated)}
end
)
end
)
end
)
end
).
-file("src/yodel.gleam", 289).
?DOC(
" Load a configuration file with options.\n"
"\n"
" This function will use the provided options to read and parse the config content,\n"
" returning a `Context` if successful.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.as_yaml()\n"
" |> yodel.with_resolve_strict()\n"
" |> yodel.load_with_options(\"config.yaml\")\n"
" ```\n"
).
-spec load_with_options(yodel@options:options(), binary()) -> {ok,
yodel@internal@context:context()} |
{error, yodel@errors:config_error()}.
load_with_options(Options, Input) ->
case yodel@internal@input:detect_input(Input) of
{directory, Dir} ->
load_from_directory(Dir, Options);
_ ->
load_single_file(Input, Options)
end.
-file("src/yodel.gleam", 271).
?DOC(
" Load a configuration file.\n"
"\n"
" This function will read the config content, detect the format,\n"
" resolve the placeholders, parse the config content, returning a `Context` if successful.\n"
"\n"
" `input` can be a file path or a string containing the configuration content.\n"
"\n"
" Example with file path:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) = yodel.load(\"config.toml\")\n"
" ```\n"
"\n"
" Example with string content:\n"
"\n"
" ```gleam\n"
" let yaml_content = \"database:\\n host: localhost\"\n"
" case yodel.load(yaml_content) {\n"
" Ok(ctx) -> ctx\n"
" Error(e) -> {\n"
" // Handle error appropriately\n"
" panic as yodel.describe_config_error(e)\n"
" }\n"
" }\n"
" ```\n"
).
-spec load(binary()) -> {ok, yodel@internal@context:context()} |
{error, yodel@errors:config_error()}.
load(Input) ->
load_with_options(default_options(), Input).
-file("src/yodel.gleam", 648).
?DOC(
" Set the resolve mode to strict.\n"
"\n"
" Example:\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.with_resolve_strict()\n"
" |> yodel.load_with_options(config_content)\n"
" ```\n"
).
-spec with_resolve_strict(yodel@options:options()) -> yodel@options:options().
with_resolve_strict(Options) ->
with_resolve_mode(Options, strict).
-file("src/yodel.gleam", 662).
?DOC(
" Set the resolve mode to lenient.\n"
"\n"
" Example:\n"
"\n"
" ```gleam\n"
" let assert Ok(ctx) =\n"
" yodel.default_options()\n"
" |> yodel.with_resolve_lenient()\n"
" |> yodel.load_with_options(config_content)\n"
" ```\n"
).
-spec with_resolve_lenient(yodel@options:options()) -> yodel@options:options().
with_resolve_lenient(Options) ->
with_resolve_mode(Options, lenient).