Current section

Files

Jump to
oaspec src oaspec@config.erl
Raw

src/oaspec@config.erl

-module(oaspec@config).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/config.gleam").
-export([new/6, input/1, output_server/1, output_client/1, package/1, mode/1, validate/1, parse_mode/1, with_mode/2, with_validate/2, with_output/2, validate_output_package_match/1, error_to_string/1, load/1]).
-export_type([config/0, generate_mode/0, config_error/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.
-opaque config() :: {config,
binary(),
binary(),
binary(),
binary(),
generate_mode(),
boolean()}.
-type generate_mode() :: server | client | both.
-type config_error() :: {file_not_found, binary()} |
{file_read_error, binary(), binary()} |
{parse_error, binary()} |
{missing_field, binary()} |
{invalid_value, binary(), binary()}.
-file("src/oaspec/config.gleam", 28).
?DOC(
" Construct a new `Config` from its six fields. Prefer `load/1` in\n"
" production code; `new/6` is primarily for tests and ad-hoc tooling\n"
" that assembles a config in memory.\n"
).
-spec new(binary(), binary(), binary(), binary(), generate_mode(), boolean()) -> config().
new(Input, Output_server, Output_client, Package, Mode, Validate) ->
{config, Input, Output_server, Output_client, Package, Mode, Validate}.
-file("src/oaspec/config.gleam", 40).
?DOC(" Path to the OpenAPI spec this config was built for.\n").
-spec input(config()) -> binary().
input(Cfg) ->
erlang:element(2, Cfg).
-file("src/oaspec/config.gleam", 45).
?DOC(" Output directory for server-side generated files.\n").
-spec output_server(config()) -> binary().
output_server(Cfg) ->
erlang:element(3, Cfg).
-file("src/oaspec/config.gleam", 50).
?DOC(" Output directory for client-side generated files.\n").
-spec output_client(config()) -> binary().
output_client(Cfg) ->
erlang:element(4, Cfg).
-file("src/oaspec/config.gleam", 55).
?DOC(" Gleam package name (module prefix) for generated files.\n").
-spec package(config()) -> binary().
package(Cfg) ->
erlang:element(5, Cfg).
-file("src/oaspec/config.gleam", 60).
?DOC(" Generation mode: server, client, or both.\n").
-spec mode(config()) -> generate_mode().
mode(Cfg) ->
erlang:element(6, Cfg).
-file("src/oaspec/config.gleam", 65).
?DOC(" Whether guard-based runtime validation is enabled.\n").
-spec validate(config()) -> boolean().
validate(Cfg) ->
erlang:element(7, Cfg).
-file("src/oaspec/config.gleam", 86).
?DOC(" Parse a mode string into GenerateMode.\n").
-spec parse_mode(binary()) -> {ok, generate_mode()} | {error, config_error()}.
parse_mode(Mode) ->
case Mode of
<<"server"/utf8>> ->
{ok, server};
<<"client"/utf8>> ->
{ok, client};
<<"both"/utf8>> ->
{ok, both};
_ ->
{error,
{invalid_value,
<<"mode"/utf8>>,
<<"must be one of: server, client, both"/utf8>>}}
end.
-file("src/oaspec/config.gleam", 184).
?DOC(" Apply CLI overrides to a config.\n").
-spec with_mode(config(), generate_mode()) -> config().
with_mode(Config, Mode) ->
{config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
Mode,
erlang:element(7, Config)}.
-file("src/oaspec/config.gleam", 189).
?DOC(" Apply validation mode override.\n").
-spec with_validate(config(), boolean()) -> config().
with_validate(Config, Validate) ->
{config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
Validate}.
-file("src/oaspec/config.gleam", 195).
?DOC(
" Apply output base directory override.\n"
" Derives server/client paths as <dir>/<package> and <dir>_client/<package>.\n"
).
-spec with_output(config(), gleam@option:option(binary())) -> config().
with_output(Config, Output) ->
case Output of
{some, Dir} ->
{config,
erlang:element(2, Config),
<<<<Dir/binary, "/"/utf8>>/binary,
(erlang:element(5, Config))/binary>>,
<<<<Dir/binary, "_client/"/utf8>>/binary,
(erlang:element(5, Config))/binary>>,
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config)};
none ->
Config
end.
-file("src/oaspec/config.gleam", 247).
?DOC(" Get the basename of a path (last segment after /).\n").
-spec basename(binary()) -> binary().
basename(Path) ->
_pipe = Path,
_pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>),
_pipe@2 = gleam@list:last(_pipe@1),
gleam@result:unwrap(_pipe@2, <<""/utf8>>).
-file("src/oaspec/config.gleam", 209).
?DOC(
" Validate that output directory basenames match the package name.\n"
" Gleam imports require `import <package>/types`, so the directory must match.\n"
).
-spec validate_output_package_match(config()) -> {ok, nil} |
{error, config_error()}.
validate_output_package_match(Config) ->
_pipe = case erlang:element(6, Config) of
server ->
case basename(erlang:element(3, Config)) =:= erlang:element(
5,
Config
) of
true ->
{ok, nil};
false ->
{error,
{invalid_value,
<<"output.server"/utf8>>,
<<<<<<<<"Directory basename '"/utf8,
(basename(erlang:element(3, Config)))/binary>>/binary,
"' must match package '"/utf8>>/binary,
(erlang:element(5, Config))/binary>>/binary,
"'"/utf8>>}}
end;
both ->
case basename(erlang:element(3, Config)) =:= erlang:element(
5,
Config
) of
true ->
{ok, nil};
false ->
{error,
{invalid_value,
<<"output.server"/utf8>>,
<<<<<<<<"Directory basename '"/utf8,
(basename(erlang:element(3, Config)))/binary>>/binary,
"' must match package '"/utf8>>/binary,
(erlang:element(5, Config))/binary>>/binary,
"'"/utf8>>}}
end;
client ->
{ok, nil}
end,
gleam@result:'try'(_pipe, fun(_) -> case erlang:element(6, Config) of
client ->
case basename(erlang:element(4, Config)) =:= erlang:element(
5,
Config
) of
true ->
{ok, nil};
false ->
{error,
{invalid_value,
<<"output.client"/utf8>>,
<<<<<<<<"Directory basename '"/utf8,
(basename(
erlang:element(
4,
Config
)
))/binary>>/binary,
"' must match package '"/utf8>>/binary,
(erlang:element(5, Config))/binary>>/binary,
"'"/utf8>>}}
end;
both ->
case basename(erlang:element(4, Config)) =:= erlang:element(
5,
Config
) of
true ->
{ok, nil};
false ->
{error,
{invalid_value,
<<"output.client"/utf8>>,
<<<<<<<<"Directory basename '"/utf8,
(basename(
erlang:element(
4,
Config
)
))/binary>>/binary,
"' must match package '"/utf8>>/binary,
(erlang:element(5, Config))/binary>>/binary,
"'"/utf8>>}}
end;
server ->
{ok, nil}
end end).
-file("src/oaspec/config.gleam", 255).
?DOC(" Convert config error to a human-readable string.\n").
-spec error_to_string(config_error()) -> binary().
error_to_string(Error) ->
case Error of
{file_not_found, Path} ->
<<"Config file not found: "/utf8, Path/binary>>;
{file_read_error, Path@1, Detail} ->
<<<<<<"Error reading config file "/utf8, Path@1/binary>>/binary,
": "/utf8>>/binary,
Detail/binary>>;
{parse_error, Detail@1} ->
<<"Config parse error: "/utf8, Detail@1/binary>>;
{missing_field, Field} ->
<<"Missing required config field: "/utf8, Field/binary>>;
{invalid_value, Field@1, Detail@2} ->
<<<<<<"Invalid value for "/utf8, Field@1/binary>>/binary,
": "/utf8>>/binary,
Detail@2/binary>>
end.
-file("src/oaspec/config.gleam", 268).
?DOC(" Extract a nested string value from YAML like output.server.\n").
-spec extract_nested_string(yay:node_(), binary(), binary()) -> gleam@option:option(binary()).
extract_nested_string(Root, Key1, Key2) ->
case yay:select_sugar(
Root,
<<<<Key1/binary, "."/utf8>>/binary, Key2/binary>>
) of
{ok, {node_str, Value}} ->
{some, Value};
_ ->
none
end.
-file("src/oaspec/config.gleam", 280).
?DOC(" Convert a yay YAML error to string.\n").
-spec yaml_error_to_string(yay:yaml_error()) -> binary().
yaml_error_to_string(Error) ->
case Error of
unexpected_parsing_error ->
<<"Unexpected parsing error"/utf8>>;
{parsing_error, Msg, _} ->
Msg
end.
-file("src/oaspec/config.gleam", 100).
?DOC(" Load config from a YAML file.\n").
-spec load(binary()) -> {ok, config()} | {error, config_error()}.
load(Path) ->
gleam@result:'try'(
begin
_pipe = simplifile:read(Path),
gleam@result:map_error(_pipe, fun(E) -> case E of
enoent ->
{file_not_found, Path};
_ ->
{file_read_error,
Path,
<<"Failed to read file: "/utf8,
(simplifile:describe_error(E))/binary>>}
end end)
end,
fun(Content) ->
gleam@result:'try'(
begin
_pipe@1 = yaml_ffi:parse_string(Content),
gleam@result:map_error(
_pipe@1,
fun(E@1) ->
{parse_error,
<<"YAML parse error: "/utf8,
(yaml_error_to_string(E@1))/binary>>}
end
)
end,
fun(Docs) -> gleam@result:'try'(case Docs of
[First | _] ->
{ok, First};
[] ->
{error,
{parse_error,
<<"Empty YAML document"/utf8>>}}
end, fun(Doc) ->
Root = yay:document_root(Doc),
gleam@result:'try'(
begin
_pipe@2 = yay:extract_string(
Root,
<<"input"/utf8>>
),
gleam@result:map_error(
_pipe@2,
fun(_) ->
{missing_field, <<"input"/utf8>>}
end
)
end,
fun(Input) ->
Package = begin
_pipe@3 = yay:extract_optional_string(
Root,
<<"package"/utf8>>
),
_pipe@4 = gleam@result:unwrap(
_pipe@3,
none
),
gleam@option:unwrap(
_pipe@4,
<<"api"/utf8>>
)
end,
Output_dir = begin
_pipe@5 = extract_nested_string(
Root,
<<"output"/utf8>>,
<<"dir"/utf8>>
),
gleam@option:unwrap(
_pipe@5,
<<"./gen"/utf8>>
)
end,
Output_server = begin
_pipe@6 = extract_nested_string(
Root,
<<"output"/utf8>>,
<<"server"/utf8>>
),
gleam@option:unwrap(
_pipe@6,
<<<<Output_dir/binary, "/"/utf8>>/binary,
Package/binary>>
)
end,
Output_client = begin
_pipe@7 = extract_nested_string(
Root,
<<"output"/utf8>>,
<<"client"/utf8>>
),
gleam@option:unwrap(
_pipe@7,
<<<<Output_dir/binary,
"_client/"/utf8>>/binary,
Package/binary>>
)
end,
gleam@result:'try'(
case begin
_pipe@8 = yay:extract_optional_string(
Root,
<<"mode"/utf8>>
),
gleam@result:unwrap(_pipe@8, none)
end of
{some, <<"server"/utf8>>} ->
{ok, server};
{some, <<"client"/utf8>>} ->
{ok, client};
{some, <<"both"/utf8>>} ->
{ok, both};
none ->
{ok, both};
{some, Other} ->
{error,
{invalid_value,
<<"mode"/utf8>>,
<<<<"must be one of: server, client, both (got: "/utf8,
Other/binary>>/binary,
")"/utf8>>}}
end,
fun(Mode) ->
gleam@result:'try'(
case yay:select_sugar(
Root,
<<"validate"/utf8>>
) of
{ok, {node_bool, true}} ->
{ok, true};
{ok,
{node_str,
<<"true"/utf8>>}} ->
{ok, true};
{ok, {node_bool, false}} ->
{ok, false};
{ok,
{node_str,
<<"false"/utf8>>}} ->
{ok, false};
{error, _} ->
{ok, false};
{ok, _} ->
{error,
{invalid_value,
<<"validate"/utf8>>,
<<"must be a boolean (true or false)"/utf8>>}}
end,
fun(Validate) ->
{ok,
{config,
Input,
Output_server,
Output_client,
Package,
Mode,
Validate}}
end
)
end
)
end
)
end) end
)
end
).