-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).