-module(embeds@env). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/embeds/env.gleam"). -export([string/1, generate/1, get_variables/3, list/2, default_parse/2, typed_parse/1, typed_parse_fallback/2, int/1, float/1, bool/1, main/0]). -export_type([options/0]). -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( " `embeds/env` generates a Gleam module containing constants and zero-argument\n" " functions based on environment variables.\n" "\n" " This module is intended to be run as a CLI tool during your build process:\n" " ```sh\n" " gleam run -m embeds/env -- --help\n" " ```\n" "\n" " The functions exposed in this module can also be used directly to support\n" " advanced use cases inside custom pre-build scripts.\n" ). -type options() :: {options, binary(), binary(), fun((binary(), binary()) -> embeds@generate:code()), binary(), gleam@option:option(gleam@regexp:regexp())}. -file("src/embeds/env.gleam", 402). -spec chop_prefix(binary(), binary()) -> {ok, binary()} | {error, nil}. chop_prefix(String, Prefix) -> case {gleam_stdlib:string_pop_grapheme(String), gleam_stdlib:string_pop_grapheme(Prefix)} of {{ok, {Ch1, String@1}}, {ok, {Ch2, Prefix@1}}} when Ch1 =:= Ch2 -> chop_prefix(String@1, Prefix@1); {_, {error, nil}} -> {ok, String}; {_, _} -> {error, nil} end. -file("src/embeds/env.gleam", 296). ?DOC(" A string parser, just passing through the value\n"). -spec string(binary()) -> embeds@generate:code(). string(Value) -> {constant, gleam@string:inspect(Value)}. -file("src/embeds/env.gleam", 85). ?DOC( " Generate Gleam modules, containing constants for all found variables.\n" " Exported for easier integration with other build tools.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import embeds/env\n" " import embeds/generate\n" " import gleam/option\n" " import gleam/io\n" "\n" " pub fn main() {\n" " let result = env.generate(env.Options(\n" " module: \"build_env\",\n" " header: generate.default_header,\n" " parse: env.default_parse,\n" " prefix: \"BUILD_\",\n" " regex: option.None,\n" " ))\n" "\n" " case result {\n" " Ok(Nil) -> Nil\n" " Error(errs) -> io.println_error(generate.describe_errors(errs))\n" " }\n" " }\n" " ```\n" ). -spec generate(options()) -> {ok, nil} | {error, list(embeds@generate:error())}. generate(Options) -> embeds@generate:guard_gleam_project( fun() -> {options, Module, Header, Parse, Prefix, Regex} = Options, Defs = get_variables(Module, Prefix, Regex), Wrapped_parse = fun(Name, Code) -> case Code of {text, Text} -> Parse(Name, Text); {binary, _} -> skip end end, embeds@generate:generate(Defs, Header, Wrapped_parse, true) end ). -file("src/embeds/env.gleam", 367). ?DOC( " Get all variables from the environment matching a prefix and a regex,\n" " and convert them into definitions.\n" ). -spec get_variables( binary(), binary(), gleam@option:option(gleam@regexp:regexp()) ) -> list(embeds@generate:definition()). get_variables(Module, Prefix, Regex) -> gleam@list:filter_map( maps:to_list(envoy_ffi:all()), fun(_use0) -> {Key, Value} = _use0, gleam@result:'try'( chop_prefix(Key, Prefix), fun(Var_name) -> gleam@result:'try'(case Regex of {some, Regex@1} -> case gleam@regexp:scan(Regex@1, Var_name) of [{match, _, [{some, Matched} | _]} | _] -> {ok, Matched}; [_ | _] -> {ok, Var_name}; [] -> {error, nil} end; none -> {ok, Var_name} end, fun(Var_name@1) -> Var_name@2 = embeds@generate:to_gleam_identifier( Var_name@1 ), {ok, {definition, Key, fun() -> {ok, {text, Value}} end, 0, Module, Var_name@2}} end) end ) end ). -file("src/embeds/env.gleam", 335). ?DOC( " A parser for lists.\n" " Common separators might be `:` or `,`\n" ). -spec list(fun((binary()) -> embeds@generate:code()), binary()) -> fun((binary()) -> embeds@generate:code()). list(Item_parser, Separator) -> fun(Value) -> List = gleam@string:split(Value, Separator), Result = begin gleam@list:try_fold( List, {[], true}, fun(_use0, Value@1) -> {Parts, Is_const} = _use0, case Item_parser(gleam@string:trim(Value@1)) of skip -> {ok, {Parts, Is_const}}; {generate_error, Error} -> {error, Error}; {constant, Code} -> {ok, {[Code | Parts], Is_const}}; {function_body, Code@1} -> {ok, {[Code@1 | Parts], false}} end end ) end, case Result of {ok, {Parts@1, Is_const@1}} -> Code@2 = gleam@string:inspect(lists:reverse(Parts@1)), case Is_const@1 of true -> {constant, Code@2}; false -> {function_body, Code@2} end; {error, Err} -> {generate_error, Err} end end. -file("src/embeds/env.gleam", 55). ?DOC( " A default parse function, converting environment variables into\n" " string constants.\n" ). -spec default_parse(binary(), binary()) -> embeds@generate:code(). default_parse(_, Value) -> {constant, gleam@string:inspect(Value)}. -file("src/embeds/env.gleam", 267). ?DOC( " A function taking a list of `(key, parser)` pairs, and turning them\n" " into a parse function to be used in `Options`.\n" " \n" " This allows you to generate other types of variables, instead of just strings.\n" " \n" " ## Example\n" " \n" " ```gleam\n" " let options = env.Options(\n" " module: \"build_env\",\n" " header: generate.default_header,\n" " prefix: \"BUILD_\",\n" " regex: option.None,\n" " parse: env.typed_parse([\n" " #(\"BUILD_ENV\", string),\n" " #(\"BUILD_REVISION\", int),\n" " #(\"BUILD_ENABLE_AWESOMENESS\", bool)\n" " ]),\n" " )\n" " ```\n" ). -spec typed_parse(list({binary(), fun((binary()) -> embeds@generate:code())})) -> fun((binary(), binary()) -> embeds@generate:code()). typed_parse(Definitions) -> fun(Key, Value) -> case gleam@list:key_find(Definitions, Key) of {ok, Parser} -> Parser(gleam@string:trim(Value)); {error, nil} -> skip end end. -file("src/embeds/env.gleam", 283). ?DOC( " A variant of `typed_parse` that takes an additional fallback function\n" " used when the variable is not found inside of `definitions`.\n" " \n" " Using `default_parse` as the fallback is useful to include all\n" " variables not found in `definitions` as simple string constants.\n" ). -spec typed_parse_fallback( list({binary(), fun((binary()) -> embeds@generate:code())}), fun((binary(), binary()) -> embeds@generate:code()) ) -> fun((binary(), binary()) -> embeds@generate:code()). typed_parse_fallback(Definitions, Fallback) -> fun(Key, Value) -> case gleam@list:key_find(Definitions, Key) of {ok, Parser} -> Parser(gleam@string:trim(Value)); {error, nil} -> Fallback(Key, Value) end end. -file("src/embeds/env.gleam", 301). ?DOC(" A parser for integers literals\n"). -spec int(binary()) -> embeds@generate:code(). int(Value) -> embeds@generate:result_to_const( begin gleam@result:map( begin _pipe = gleam_stdlib:parse_int(Value), gleam@result:replace_error( _pipe, <<"Invalid integer: "/utf8, Value/binary>> ) end, fun(N) -> gleam@string:inspect(N) end ) end ). -file("src/embeds/env.gleam", 313). ?DOC(" A parser for floating-point literals\n"). -spec float(binary()) -> embeds@generate:code(). float(Value) -> embeds@generate:result_to_const( begin gleam@result:map( begin _pipe = gleam_stdlib:parse_float(Value), gleam@result:replace_error( _pipe, <<"Invalid number: "/utf8, Value/binary>> ) end, fun(N) -> gleam@string:inspect(N) end ) end ). -file("src/embeds/env.gleam", 324). ?DOC(" A parser for boolean flags\n"). -spec bool(binary()) -> embeds@generate:code(). bool(Value) -> Sanitized = gleam@string:trim(string:lowercase(Value)), Bool = case Sanitized of <<"true"/utf8>> -> true; <<"yes"/utf8>> -> true; <<"on"/utf8>> -> true; <<"1"/utf8>> -> true; _ -> false end, {constant, gleam@string:inspect(Bool)}. -file("src/embeds/env.gleam", 191). -spec parse_definitions(list(binary())) -> {ok, list({binary(), fun((binary()) -> embeds@generate:code())})} | {error, list(embeds@generate:error())}. parse_definitions(Args) -> embeds@internal:traverse_map( Args, fun(Arg) -> case gleam@string:split(Arg, <<":"/utf8>>) of [Name, Type_name] -> Name@1 = gleam@string:trim(Name), case gleam@string:trim(string:lowercase(Type_name)) of <<"bool"/utf8>> -> {ok, {Name@1, fun bool/1}}; <<"int"/utf8>> -> {ok, {Name@1, fun int/1}}; <<"float"/utf8>> -> {ok, {Name@1, fun float/1}}; <<"string"/utf8>> -> {ok, {Name@1, fun string/1}}; _ -> {error, [{invalid_argument, <<""/utf8>>, Arg, <<"Invalid type name: "/utf8, Type_name/binary>>}]} end; _ -> {error, [{invalid_argument, <<""/utf8>>, Arg, <<"Invalid type definition"/utf8>>}]} end end ). -file("src/embeds/env.gleam", 120). -spec generate_cmd() -> glint:command(nil). generate_cmd() -> glint:command_help( <<<<<<<<"Generate Gleam modules containing String constants of whitelisted environment variables.\n\n\n"/utf8, "By default, environment variables starting with `BUILD_' will be included, and a module called build_env will be generated.\n\n"/utf8>>/binary, "The generted constants have this prefix stripped.\n\n\n"/utf8>>/binary, "You can optionally provide a list of NAME:TYPE pairs to generate constants of other types instead.\n\n"/utf8>>/binary, "Valid types names are: int, float, bool, string"/utf8>>, fun() -> glint:flag( begin _pipe = glint:string_flag(<<"module"/utf8>>), _pipe@1 = glint:flag_default(_pipe, <<"build_env"/utf8>>), glint:flag_help( _pipe@1, <<"Name of the generated Gleam module (default: build_env)"/utf8>> ) end, fun(Module_name) -> glint:flag( begin _pipe@2 = glint:string_flag(<<"prefix"/utf8>>), _pipe@3 = glint:flag_default( _pipe@2, <<"BUILD_"/utf8>> ), glint:flag_help( _pipe@3, <<"Only environment variables matching this prefix will be included, and the prefix will be stripped from their name. (default: BUILD_)"/utf8>> ) end, fun(Prefix) -> glint:flag( begin _pipe@4 = glint:string_flag( <<"filter"/utf8>> ), glint:flag_help( _pipe@4, <<"Use a regular expression to match environment variables. If a capture group exists, it will be used as the variable name. If both prefix and regex are given, the prefix will be stripped first."/utf8>> ) end, fun(Filter) -> glint:command( fun(_, Args, Flags) -> Module@1 = case Module_name(Flags) of {ok, Module} -> Module; _assert_fail -> erlang:error( #{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"embeds/env"/utf8>>, function => <<"generate_cmd"/utf8>>, line => 154, value => _assert_fail, start => 4797, 'end' => 4839, pattern_start => 4808, pattern_end => 4818} ) end, Prefix@2 = case Prefix(Flags) of {ok, Prefix@1} -> Prefix@1; _assert_fail@1 -> erlang:error( #{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"embeds/env"/utf8>>, function => <<"generate_cmd"/utf8>>, line => 155, value => _assert_fail@1, start => 4842, 'end' => 4879, pattern_start => 4853, pattern_end => 4863} ) end, Filter@1 = begin _pipe@5 = Filter(Flags), gleam@option:from_result( _pipe@5 ) end, Result = begin gleam@result:'try'( case Filter@1 of {some, Filter@2} -> case gleam@regexp:from_string( Filter@2 ) of {ok, Regex} -> {ok, {some, Regex}}; {error, {compile_error, Error, _}} -> {error, [{invalid_argument, <<"filter"/utf8>>, Filter@2, Error}]} end; none -> {ok, none} end, fun(Regex@1) -> gleam@result:'try'( parse_definitions( Args ), fun(Definitions) -> Options = {options, embeds@generate:to_gleam_module_name( Module@1 ), <<"//// Generated by `embeds`, manual edits are futile"/utf8>>, typed_parse_fallback( Definitions, fun default_parse/2 ), Prefix@2, Regex@1}, generate( Options ) end ) end ) end, case Result of {ok, nil} -> nil; {error, Errs} -> gleam_stdlib:println_error( embeds@generate:describe_errors( Errs ) ) end, nil end ) end ) end ) end ) end ). -file("src/embeds/env.gleam", 113). ?DOC(" runs generate using command line arguments to fill out the arguments.\n"). -spec main() -> nil. main() -> _pipe = glint:new(), _pipe@1 = glint:pretty_help(_pipe, glint:default_pretty_help()), _pipe@2 = glint:add(_pipe@1, [], generate_cmd()), glint:run(_pipe@2, erlang:element(4, argv:load())).