Current section

Files

Jump to
oaspec src oaspec@internal@cli.erl
Raw

src/oaspec@internal@cli.erl

-module(oaspec@internal@cli).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/internal/cli.gleam").
-export([resolve_path_from_cwd/2, resolved_path_entries/3, app/0]).
-export_type([duplicate_output_path_error/0, load_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.
?MODULEDOC(false).
-type duplicate_output_path_error() :: {duplicate_output_path, binary()}.
-type load_config_error() :: {config_load_error, oaspec@config:config_error()} |
{mode_parse_error, oaspec@config:config_error()} |
{output_validation_error, oaspec@config:config_error()}.
-file("src/oaspec/internal/cli.gleam", 30).
?DOC(false).
-spec maybe_pretty_help(glint:glint(YNT)) -> glint:glint(YNT).
maybe_pretty_help(Glint) ->
case {oaspec_ffi:is_stdout_tty(), oaspec_ffi:no_color_set()} of
{true, false} ->
glint:pretty_help(Glint, glint:default_pretty_help());
{_, _} ->
Glint
end.
-file("src/oaspec/internal/cli.gleam", 60).
?DOC(false).
-spec version_command() -> glint:command(nil).
version_command() ->
glint:command_help(
<<"Print the oaspec version and exit"/utf8>>,
fun() ->
glint:command(
fun(_, _, _) ->
gleam_stdlib:println(
<<"oaspec v"/utf8, (<<"0.56.0"/utf8>>)/binary>>
)
end
)
end
).
-file("src/oaspec/internal/cli.gleam", 469).
?DOC(false).
-spec active_output_paths(oaspec@config:config()) -> list(binary()).
active_output_paths(Cfg) ->
case oaspec@config:mode(Cfg) of
server ->
[oaspec@config:output_server(Cfg)];
client ->
[oaspec@config:output_client(Cfg)];
both ->
Server_path = oaspec@config:output_server(Cfg),
Client_path = oaspec@config:output_client(Cfg),
case Server_path =:= Client_path of
true ->
[Server_path];
false ->
[Server_path, Client_path]
end
end.
-file("src/oaspec/internal/cli.gleam", 502).
?DOC(false).
-spec find_duplicate_path(list(binary()), list(binary())) -> {ok, nil} |
{error, duplicate_output_path_error()}.
find_duplicate_path(Remaining, Seen) ->
case Remaining of
[] ->
{ok, nil};
[Path | Rest] ->
case gleam@list:contains(Seen, Path) of
true ->
{error, {duplicate_output_path, Path}};
false ->
find_duplicate_path(Rest, [Path | Seen])
end
end.
-file("src/oaspec/internal/cli.gleam", 451).
?DOC(false).
-spec validate_no_target_overlap(list(oaspec@config:config())) -> {ok, nil} |
{error, load_config_error()}.
validate_no_target_overlap(Cfgs) ->
Paths = begin
_pipe = Cfgs,
gleam@list:flat_map(_pipe, fun(Cfg) -> active_output_paths(Cfg) end)
end,
_pipe@1 = find_duplicate_path(Paths, []),
gleam@result:map_error(
_pipe@1,
fun(Err) ->
{duplicate_output_path, Path} = Err,
{output_validation_error,
{invalid_value,
<<"targets"/utf8>>,
<<<<"two targets resolve to the same output directory '"/utf8,
Path/binary>>/binary,
"' — give them distinct package or output paths so generated files do not clobber each other"/utf8>>}}
end
).
-file("src/oaspec/internal/cli.gleam", 581).
?DOC(false).
-spec is_ascii_letter(binary()) -> boolean().
is_ascii_letter(Value) ->
case string:lowercase(Value) of
<<"a"/utf8>> ->
true;
<<"b"/utf8>> ->
true;
<<"c"/utf8>> ->
true;
<<"d"/utf8>> ->
true;
<<"e"/utf8>> ->
true;
<<"f"/utf8>> ->
true;
<<"g"/utf8>> ->
true;
<<"h"/utf8>> ->
true;
<<"i"/utf8>> ->
true;
<<"j"/utf8>> ->
true;
<<"k"/utf8>> ->
true;
<<"l"/utf8>> ->
true;
<<"m"/utf8>> ->
true;
<<"n"/utf8>> ->
true;
<<"o"/utf8>> ->
true;
<<"p"/utf8>> ->
true;
<<"q"/utf8>> ->
true;
<<"r"/utf8>> ->
true;
<<"s"/utf8>> ->
true;
<<"t"/utf8>> ->
true;
<<"u"/utf8>> ->
true;
<<"v"/utf8>> ->
true;
<<"w"/utf8>> ->
true;
<<"x"/utf8>> ->
true;
<<"y"/utf8>> ->
true;
<<"z"/utf8>> ->
true;
_ ->
false
end.
-file("src/oaspec/internal/cli.gleam", 572).
?DOC(false).
-spec is_windows_drive_absolute(binary()) -> boolean().
is_windows_drive_absolute(Path) ->
Prefix = gleam@string:slice(Path, 0, 3),
case gleam@string:to_graphemes(Prefix) of
[Drive, <<":"/utf8>>, Slash] ->
is_ascii_letter(Drive) andalso ((Slash =:= <<"/"/utf8>>) orelse (Slash
=:= <<"\\"/utf8>>));
_ ->
false
end.
-file("src/oaspec/internal/cli.gleam", 565).
?DOC(false).
-spec path_is_absolute(binary()) -> boolean().
path_is_absolute(Path) ->
((filepath:is_absolute(Path) orelse gleam_stdlib:string_starts_with(
Path,
<<"\\\\"/utf8>>
))
orelse gleam_stdlib:string_starts_with(Path, <<"//"/utf8>>))
orelse is_windows_drive_absolute(Path).
-file("src/oaspec/internal/cli.gleam", 557).
?DOC(false).
-spec resolve_path_from_cwd(binary(), binary()) -> binary().
resolve_path_from_cwd(Path, Cwd) ->
Candidate = case path_is_absolute(Path) of
true ->
Path;
false ->
filepath:join(Cwd, Path)
end,
_pipe = filepath:expand(Candidate),
gleam@result:unwrap(_pipe, Candidate).
-file("src/oaspec/internal/cli.gleam", 416).
?DOC(false).
-spec print_target_outputs(oaspec@config:config(), binary(), binary()) -> nil.
print_target_outputs(Cfg, Cwd, Prefix) ->
case oaspec@config:mode(Cfg) of
server ->
gleam_stdlib:println(
<<<<Prefix/binary, "output.server: "/utf8>>/binary,
(resolve_path_from_cwd(
oaspec@config:output_server(Cfg),
Cwd
))/binary>>
);
client ->
gleam_stdlib:println(
<<<<Prefix/binary, "output.client: "/utf8>>/binary,
(resolve_path_from_cwd(
oaspec@config:output_client(Cfg),
Cwd
))/binary>>
);
both ->
gleam_stdlib:println(
<<<<Prefix/binary, "output.server: "/utf8>>/binary,
(resolve_path_from_cwd(
oaspec@config:output_server(Cfg),
Cwd
))/binary>>
),
gleam_stdlib:println(
<<<<Prefix/binary, "output.client: "/utf8>>/binary,
(resolve_path_from_cwd(
oaspec@config:output_client(Cfg),
Cwd
))/binary>>
)
end.
-file("src/oaspec/internal/cli.gleam", 532).
?DOC(false).
-spec resolved_path_entries(binary(), oaspec@config:config(), binary()) -> list({binary(),
binary()}).
resolved_path_entries(Config_path, Cfg, Cwd) ->
Base_entries = [{<<"config"/utf8>>, resolve_path_from_cwd(Config_path, Cwd)},
{<<"input"/utf8>>, resolve_path_from_cwd(oaspec@config:input(Cfg), Cwd)}],
Output_entries = case oaspec@config:mode(Cfg) of
server ->
[{<<"output.server"/utf8>>,
resolve_path_from_cwd(oaspec@config:output_server(Cfg), Cwd)}];
client ->
[{<<"output.client"/utf8>>,
resolve_path_from_cwd(oaspec@config:output_client(Cfg), Cwd)}];
both ->
[{<<"output.server"/utf8>>,
resolve_path_from_cwd(oaspec@config:output_server(Cfg), Cwd)},
{<<"output.client"/utf8>>,
resolve_path_from_cwd(oaspec@config:output_client(Cfg), Cwd)}]
end,
lists:append(Base_entries, Output_entries).
-file("src/oaspec/internal/cli.gleam", 516).
?DOC(false).
-spec print_resolved_paths(binary(), oaspec@config:config()) -> nil.
print_resolved_paths(Config_path, Cfg) ->
case simplifile:current_directory() of
{ok, Cwd} ->
gleam_stdlib:println(<<"Resolved paths:"/utf8>>),
_pipe = resolved_path_entries(Config_path, Cfg, Cwd),
gleam@list:each(
_pipe,
fun(Entry) ->
{Label, Path} = Entry,
gleam_stdlib:println(
<<<<<<" "/utf8, Label/binary>>/binary, ": "/utf8>>/binary,
Path/binary>>
)
end
);
{error, _} ->
nil
end.
-file("src/oaspec/internal/cli.gleam", 386).
?DOC(false).
-spec print_resolved_paths_for_all(binary(), list(oaspec@config:config())) -> nil.
print_resolved_paths_for_all(Config_path, Cfgs) ->
case Cfgs of
[Single] ->
print_resolved_paths(Config_path, Single);
Multiple ->
case simplifile:current_directory() of
{ok, Cwd} ->
gleam_stdlib:println(<<"Resolved paths:"/utf8>>),
gleam_stdlib:println(
<<" config: "/utf8,
(resolve_path_from_cwd(Config_path, Cwd))/binary>>
),
Inputs = begin
_pipe = Multiple,
_pipe@1 = gleam@list:map(
_pipe,
fun(C) -> oaspec@config:input(C) end
),
gleam@list:unique(_pipe@1)
end,
gleam@list:each(
Inputs,
fun(P) ->
gleam_stdlib:println(
<<" input: "/utf8,
(resolve_path_from_cwd(P, Cwd))/binary>>
)
end
),
gleam@list:each(
Multiple,
fun(Cfg) ->
gleam_stdlib:println(
<<<<" target ["/utf8,
(oaspec@config:package(Cfg))/binary>>/binary,
"]:"/utf8>>
),
print_target_outputs(Cfg, Cwd, <<" "/utf8>>)
end
);
{error, _} ->
nil
end
end.
-file("src/oaspec/internal/cli.gleam", 641).
?DOC(false).
-spec print_dep_hints(list(oaspec@internal@codegen@context:generated_file())) -> nil.
print_dep_hints(Files) ->
Needs_regexp = gleam@list:any(
Files,
fun(F) ->
gleam_stdlib:contains_string(
erlang:element(3, F),
<<"import gleam/regexp"/utf8>>
)
end
),
case Needs_regexp of
true ->
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<"Note: generated code imports gleam/regexp for pattern validation."/utf8>>
),
gleam_stdlib:println(
<<" Run 'gleam add gleam_regexp' in your project before 'gleam build'."/utf8>>
);
false ->
nil
end.
-file("src/oaspec/internal/cli.gleam", 784).
?DOC(false).
-spec unique_id() -> binary().
unique_id() ->
Integer_value = erlang:unique_integer(),
case Integer_value < 0 of
true ->
<<"n"/utf8, (erlang:integer_to_binary(- Integer_value))/binary>>;
false ->
erlang:integer_to_binary(Integer_value)
end.
-file("src/oaspec/internal/cli.gleam", 732).
?DOC(false).
-spec format_resolved_content(list({binary(), binary()})) -> list({binary(),
binary()}).
format_resolved_content(Resolved) ->
Temp_dir = <<"/tmp/oaspec_check_"/utf8, (unique_id())/binary>>,
case simplifile:create_directory_all(Temp_dir) of
{error, _} ->
Resolved;
{ok, nil} ->
Temp_entries = gleam@list:index_map(
Resolved,
fun(Entry, Idx) ->
{Original_path, Content} = Entry,
Temp_path = <<<<<<Temp_dir/binary, "/"/utf8>>/binary,
(erlang:integer_to_binary(Idx))/binary>>/binary,
".gleam"/utf8>>,
_ = simplifile:write(Temp_path, Content),
{Original_path, Temp_path}
end
),
Temp_paths = gleam@list:map(
Temp_entries,
fun(Entry@1) -> erlang:element(2, Entry@1) end
),
Formatted_resolved = case oaspec@internal@formatter:format_files(
Temp_paths
) of
{ok, nil} ->
gleam@list:map(
Temp_entries,
fun(Entry@2) ->
{Original_path@1, Temp_path@1} = Entry@2,
case simplifile:read(Temp_path@1) of
{ok, Formatted} ->
{Original_path@1, Formatted};
{error, _} ->
Content@1 = begin
_pipe = gleam@list:find(
Resolved,
fun(R) ->
erlang:element(1, R) =:= Original_path@1
end
),
_pipe@1 = gleam@result:map(
_pipe,
fun(R@1) ->
erlang:element(2, R@1)
end
),
gleam@result:unwrap(
_pipe@1,
<<""/utf8>>
)
end,
{Original_path@1, Content@1}
end
end
);
{error, _} ->
Resolved
end,
_ = simplifile_erl:delete(Temp_dir),
Formatted_resolved
end.
-file("src/oaspec/internal/cli.gleam", 855).
?DOC(false).
-spec load_config_error_to_string(load_config_error()) -> binary().
load_config_error_to_string(Error) ->
Detail = case Error of
{config_load_error, Error@1} ->
oaspec@config:error_to_string(Error@1);
{mode_parse_error, Error@2} ->
oaspec@config:error_to_string(Error@2);
{output_validation_error, Error@3} ->
oaspec@config:error_to_string(Error@3)
end,
<<"Error: "/utf8, Detail/binary>>.
-file("src/oaspec/internal/cli.gleam", 917).
?DOC(false).
-spec validate_target_paths(oaspec@config:config()) -> {ok, nil} |
{error, load_config_error()}.
validate_target_paths(Cfg) ->
gleam@result:'try'(
begin
_pipe = oaspec@config:validate_output_package_match(Cfg),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {output_validation_error, Field@0} end
)
end,
fun(_) -> _pipe@1 = oaspec@config:validate_output_dir_layout(Cfg),
gleam@result:map_error(
_pipe@1,
fun(Field@0) -> {output_validation_error, Field@0} end
) end
).
-file("src/oaspec/internal/cli.gleam", 870).
?DOC(false).
-spec load_configs(
binary(),
gleam@option:option(binary()),
gleam@option:option(binary())
) -> {ok, list(oaspec@config:config())} | {error, load_config_error()}.
load_configs(Config_path, Mode_opt, Output_opt) ->
gleam@result:'try'(
begin
_pipe = oaspec@config:load_all(Config_path),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {config_load_error, Field@0} end
)
end,
fun(Cfgs) -> gleam@result:'try'(case Mode_opt of
none ->
{ok, Cfgs};
{some, Mode_str} ->
_pipe@1 = oaspec@config:parse_mode(Mode_str),
_pipe@2 = gleam@result:map(
_pipe@1,
fun(Parsed) ->
gleam@list:map(
Cfgs,
fun(C) ->
oaspec@config:with_mode(C, Parsed)
end
)
end
),
gleam@result:map_error(
_pipe@2,
fun(Field@0) -> {mode_parse_error, Field@0} end
)
end, fun(Cfgs@1) ->
gleam@result:'try'(case {Output_opt, Cfgs@1} of
{none, _} ->
{ok, Cfgs@1};
{{some, Path}, [Single]} ->
{ok,
[oaspec@config:with_output(
Single,
{some, Path}
)]};
{{some, _}, [_, _ | _]} ->
{error,
{output_validation_error,
{invalid_value,
<<"--output"/utf8>>,
<<"cannot override the output directory for a multi-target config; each target already declares its own output (set per-target output: in oaspec.yaml or drop the --output flag)"/utf8>>}}};
{{some, _}, []} ->
{error,
{output_validation_error,
{invalid_value,
<<"targets"/utf8>>,
<<"must declare at least one target"/utf8>>}}}
end, fun(Cfgs@2) ->
gleam@result:'try'(
begin
_pipe@3 = Cfgs@2,
_pipe@4 = gleam@list:try_map(
_pipe@3,
fun(Cfg) ->
validate_target_paths(Cfg)
end
),
gleam@result:map(_pipe@4, fun(_) -> nil end)
end,
fun(_) -> {ok, Cfgs@2} end
)
end)
end) end
).
-file("src/oaspec/internal/cli.gleam", 948).
?DOC(false).
-spec format_generate_error_for(binary()) -> fun((oaspec@generate:generate_error()) -> binary()).
format_generate_error_for(File_path) ->
fun(Err) ->
{validation_errors, Errors} = Err,
File_opt = case File_path of
<<""/utf8>> ->
none;
_ ->
{some, File_path}
end,
<<"Error: OpenAPI spec contains unsupported features:\n"/utf8,
(gleam@string:join(
gleam@list:map(
Errors,
fun(Validation_error) ->
<<" - "/utf8,
(case erlang:element(7, Validation_error) of
{source_loc, _, _} ->
oaspec@openapi@diagnostic:render(
Validation_error,
File_opt
);
no_source_loc ->
oaspec@openapi@diagnostic:to_short_string(
Validation_error
)
end)/binary>>
end
),
<<"\n"/utf8>>
))/binary>>
end.
-file("src/oaspec/internal/cli.gleam", 975).
?DOC(false).
-spec print_warnings_for(binary(), list(oaspec@openapi@diagnostic:diagnostic())) -> nil.
print_warnings_for(File_path, Warnings) ->
File_opt = case File_path of
<<""/utf8>> ->
none;
_ ->
{some, File_path}
end,
case Warnings of
[] ->
nil;
_ ->
gleam_stdlib:println_error(<<"Warnings:"/utf8>>),
gleam@list:each(
Warnings,
fun(W) ->
Rendered = case erlang:element(7, W) of
{source_loc, _, _} ->
oaspec@openapi@diagnostic:render(W, File_opt);
no_source_loc ->
oaspec@openapi@diagnostic:to_short_string(W)
end,
gleam_stdlib:println_error(<<" - "/utf8, Rendered/binary>>)
end
)
end.
-file("src/oaspec/internal/cli.gleam", 198).
?DOC(false).
-spec run_init(binary()) -> nil.
run_init(Path) ->
Template = <<"# oaspec configuration file
# See https://github.com/nao1215/oaspec for documentation.
# Path to your OpenAPI 3.x specification (YAML or JSON).
input: openapi.yaml
# Gleam module namespace for generated code.
# Generated imports will be `import <package>/types`, etc.
package: api
# Generation mode: server, client, or both (default: both).
# mode: both
# Enable guard validation in generated server/client code.
# Default: true for mode: server / both (fail-closed: server handlers
# should not receive schema-invalid input by default), false for
# mode: client (clients pre-validating before sending is nice but
# optional). When enabled, generated routers validate request bodies
# against schema constraints and return 422 on failure. Generated
# clients validate request bodies before sending. Set explicitly to
# override the mode-dependent default.
# validate: true
# Output settings (optional).
# output:
# dir: ./gen # Base directory; default paths are
# # server -> <dir>/<package>
# # client -> <dir>/<package>_client
# # so a single `gleam build` rooted at <dir>
# # picks up both.
# server: ./gen/api # Override server output path
# client: ./gen/api_client # Override client output path
# Operation filter (optional, Issue #387). When set, codegen only
# emits operations whose tag list intersects `tags` OR whose path
# matches one of `paths` (the two lists are unioned, not
# intersected). Path patterns ending in `/**` match any path that
# extends the prefix with a `/<rest>` segment. Both lists empty /
# both keys omitted = no filter.
# include:
# tags:
# - issues
# paths:
# - \"/users/{username}\"
# - \"/repos/**\"
# Multi-target codegen (optional, Issue #387). When `targets:` is
# set, the same input spec is generated once per entry, each with
# its own `package`, `output`, and `include`. The top-level
# `input`, `mode`, and `validate` are shared across every target.
# Targets whose output paths overlap are rejected at config-load
# time. `--output` cannot be used with multi-target configs.
# targets:
# - package: my_app/issues
# output:
# dir: ./src
# include:
# tags: [issues]
# - package: my_app/repos
# output:
# dir: ./src
# include:
# paths: [\"/repos/**\"]
"/utf8>>,
case simplifile_erl:is_file(Path) of
{ok, true} ->
gleam_stdlib:println_error(
<<<<"Error: "/utf8, Path/binary>>/binary,
" already exists"/utf8>>
),
erlang:halt(1);
_ ->
case simplifile:write(Path, Template) of
{ok, _} ->
gleam_stdlib:println(<<"Created "/utf8, Path/binary>>);
{error, Write_err} ->
gleam_stdlib:println_error(
<<<<<<"Error: failed to write "/utf8, Path/binary>>/binary,
": "/utf8>>/binary,
(simplifile:describe_error(Write_err))/binary>>
),
erlang:halt(1)
end
end.
-file("src/oaspec/internal/cli.gleam", 180).
?DOC(false).
-spec init_command() -> glint:command(nil).
init_command() ->
begin
glint:flag(
begin
_pipe = glint:string_flag(<<"output"/utf8>>),
_pipe@1 = glint:flag_default(_pipe, <<"./oaspec.yaml"/utf8>>),
glint:flag_help(
_pipe@1,
<<"Output path for the config file"/utf8>>
)
end,
fun(Output_path) ->
glint:command_help(
<<"Create a oaspec.yaml config file"/utf8>>,
fun() ->
glint:command(
fun(_, _, Flags) ->
Path = begin
_pipe@2 = Output_path(Flags),
gleam@result:unwrap(
_pipe@2,
<<"./oaspec.yaml"/utf8>>
)
end,
run_init(Path)
end
)
end
)
end
)
end.
-file("src/oaspec/internal/cli.gleam", 665).
?DOC(false).
-spec run_check(
list(oaspec@internal@codegen@context:generated_file()),
oaspec@config:config()
) -> nil.
run_check(Files, Cfg) ->
gleam_stdlib:println(
<<"Checking generated code against existing files..."/utf8>>
),
Resolved = oaspec@codegen@writer:resolve_paths(Files, Cfg),
Expected_paths = oaspec@codegen@writer:expected_paths(Files, Cfg),
Resolved@1 = format_resolved_content(Resolved),
Diffs = gleam@list:filter_map(
Resolved@1,
fun(Entry) ->
{Path, Content} = Entry,
case simplifile:read(Path) of
{error, _} ->
{ok, <<Path/binary, " (missing)"/utf8>>};
{ok, Existing} ->
case gleam@string:compare(Existing, Content) of
eq ->
{error, nil};
_ ->
{ok, <<Path/binary, " (differs)"/utf8>>}
end
end
end
),
Output_dirs = oaspec@codegen@writer:output_dirs(Cfg),
Orphans = gleam@list:flat_map(
Output_dirs,
fun(Dir) -> case simplifile_erl:read_directory(Dir) of
{error, _} ->
[];
{ok, Entries} ->
gleam@list:filter_map(
Entries,
fun(Name) ->
case gleam_stdlib:string_ends_with(
Name,
<<".gleam"/utf8>>
) of
false ->
{error, nil};
true ->
Full_path = <<<<Dir/binary, "/"/utf8>>/binary,
Name/binary>>,
case gleam@list:contains(
Expected_paths,
Full_path
) of
true ->
{error, nil};
false ->
{ok,
<<Full_path/binary,
" (orphaned)"/utf8>>}
end
end
end
)
end end
),
All_issues = lists:append(Diffs, Orphans),
case All_issues of
[] ->
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(<<"All files up to date, check passed."/utf8>>);
_ ->
gleam_stdlib:println_error(<<""/utf8>>),
gleam@list:each(
All_issues,
fun(D) ->
gleam_stdlib:println_error(<<" "/utf8, D/binary>>)
end
),
gleam_stdlib:println_error(
<<<<"\n"/utf8,
(erlang:integer_to_binary(erlang:length(All_issues)))/binary>>/binary,
" file(s) out of date. Run 'oaspec generate' to update."/utf8>>
),
erlang:halt(1)
end.
-file("src/oaspec/internal/cli.gleam", 928).
?DOC(false).
-spec require(
{ok, YPA} | {error, YPB},
fun((YPB) -> binary()),
fun((YPA) -> nil)
) -> nil.
require(Result, To_message, Continue) ->
case Result of
{ok, Value} ->
Continue(Value);
{error, Err} ->
gleam_stdlib:println_error(To_message(Err)),
erlang:halt(1)
end.
-file("src/oaspec/internal/cli.gleam", 614).
?DOC(false).
-spec write_files(
list(oaspec@internal@codegen@context:generated_file()),
oaspec@config:config()
) -> nil.
write_files(Files, Cfg) ->
gleam_stdlib:println(<<"Generating code..."/utf8>>),
require(
oaspec@codegen@writer:write_all(
Files,
Cfg,
fun(Path) ->
gleam_stdlib:println(<<" Generated: "/utf8, Path/binary>>)
end
),
fun(E) ->
<<"Error: "/utf8,
(oaspec@codegen@writer:error_to_string(E))/binary>>
end,
fun(Written) ->
require(
oaspec@internal@formatter:format_files(
oaspec@codegen@writer:output_dirs(Cfg)
),
fun(E@1) ->
<<"Error: "/utf8,
(oaspec@internal@formatter:error_to_string(E@1))/binary>>
end,
fun(_) ->
gleam_stdlib:println(<<" Formatted generated code"/utf8>>),
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<<<"Successfully generated "/utf8,
(erlang:integer_to_binary(
erlang:length(Written)
))/binary>>/binary,
" files"/utf8>>
),
print_dep_hints(Files)
end
)
end
).
-file("src/oaspec/internal/cli.gleam", 290).
?DOC(false).
-spec run_generate(
binary(),
gleam@option:option(binary()),
gleam@option:option(binary()),
boolean(),
boolean(),
boolean()
) -> nil.
run_generate(
Config_path,
Mode_opt,
Output_opt,
Check_mode,
Fail_on_warnings,
Validate_mode
) ->
gleam_stdlib:println(<<"oaspec v"/utf8, (<<"0.56.0"/utf8>>)/binary>>),
gleam_stdlib:println(<<"Loading config from: "/utf8, Config_path/binary>>),
require(
load_configs(Config_path, Mode_opt, Output_opt),
fun load_config_error_to_string/1,
fun(Cfgs) ->
Cfgs@1 = gleam@list:map(Cfgs, fun(C) -> case Validate_mode of
true ->
oaspec@config:with_validate(C, true);
false ->
C
end end),
require(
validate_no_target_overlap(Cfgs@1),
fun load_config_error_to_string/1,
fun(_) ->
print_resolved_paths_for_all(Config_path, Cfgs@1),
First_cfg@1 = case Cfgs@1 of
[First_cfg | _] -> First_cfg;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"oaspec/internal/cli"/utf8>>,
function => <<"run_generate"/utf8>>,
line => 330,
value => _assert_fail,
start => 10860,
'end' => 10893,
pattern_start => 10871,
pattern_end => 10886})
end,
Shared_input = oaspec@config:input(First_cfg@1),
gleam_stdlib:println(
<<"Parsing OpenAPI spec: "/utf8, Shared_input/binary>>
),
Reporter = oaspec@internal@progress:stdout_with_elapsed(),
require(
oaspec@openapi@parser:parse_file_with_progress_and_locations(
Shared_input,
Reporter
),
fun(E) ->
<<"Error: "/utf8,
(oaspec@openapi@parser:parse_error_to_string(E))/binary>>
end,
fun(Parsed) ->
{Spec, Locations} = Parsed,
Multi_target = case Cfgs@1 of
[_, _ | _] ->
true;
_ ->
false
end,
gleam@list:each(
Cfgs@1,
fun(Cfg) ->
case Multi_target of
true ->
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<<<"[target: "/utf8,
(oaspec@config:package(
Cfg
))/binary>>/binary,
"]"/utf8>>
);
false ->
nil
end,
require(
oaspec@generate:generate_with_progress_and_locations(
Spec,
Locations,
Cfg,
Reporter
),
format_generate_error_for(Shared_input),
fun(Summary) ->
gleam_stdlib:println(
<<"Spec loaded: "/utf8,
(erlang:element(3, Summary))/binary>>
),
print_warnings_for(
Shared_input,
erlang:element(4, Summary)
),
case Fail_on_warnings andalso (erlang:element(
4,
Summary
)
/= []) of
true ->
gleam_stdlib:println_error(
<<""/utf8>>
),
gleam_stdlib:println_error(
<<"Error: warnings present and --fail-on-warnings is set."/utf8>>
),
erlang:halt(1);
false ->
nil
end,
case Check_mode of
true ->
run_check(
erlang:element(
2,
Summary
),
Cfg
);
false ->
write_files(
erlang:element(
2,
Summary
),
Cfg
)
end
end
)
end
)
end
)
end
)
end
).
-file("src/oaspec/internal/cli.gleam", 69).
?DOC(false).
-spec generate_command() -> glint:command(nil).
generate_command() ->
begin
glint:flag(
begin
_pipe = glint:string_flag(<<"config"/utf8>>),
_pipe@1 = glint:flag_default(_pipe, <<"./oaspec.yaml"/utf8>>),
glint:flag_help(_pipe@1, <<"Path to config file"/utf8>>)
end,
fun(Config_path) ->
glint:flag(
begin
_pipe@2 = glint:string_flag(<<"mode"/utf8>>),
_pipe@3 = glint:flag_default(_pipe@2, <<""/utf8>>),
glint:flag_help(
_pipe@3,
<<"Generation mode: server, client, or both (overrides config)"/utf8>>
)
end,
fun(Mode) ->
glint:flag(
begin
_pipe@4 = glint:string_flag(<<"output"/utf8>>),
_pipe@5 = glint:flag_default(
_pipe@4,
<<""/utf8>>
),
glint:flag_help(
_pipe@5,
<<"Output directory override"/utf8>>
)
end,
fun(Output) ->
glint:flag(
begin
_pipe@6 = glint:bool_flag(
<<"check"/utf8>>
),
_pipe@7 = glint:flag_default(
_pipe@6,
false
),
glint:flag_help(
_pipe@7,
<<"Check that generated code matches existing files without writing"/utf8>>
)
end,
fun(Check) ->
glint:flag(
begin
_pipe@8 = glint:bool_flag(
<<"fail-on-warnings"/utf8>>
),
_pipe@9 = glint:flag_default(
_pipe@8,
false
),
glint:flag_help(
_pipe@9,
<<"Treat warnings as errors"/utf8>>
)
end,
fun(Fail_on_warnings) ->
glint:flag(
begin
_pipe@10 = glint:bool_flag(
<<"validate"/utf8>>
),
_pipe@11 = glint:flag_default(
_pipe@10,
false
),
glint:flag_help(
_pipe@11,
<<"Force-enable guard validation in generated server/client code. One-way override: cannot disable validation set by oaspec.yaml. Set validate: false in the config to turn it off."/utf8>>
)
end,
fun(Validate) ->
glint:command_help(
<<"Generate Gleam code from an OpenAPI specification"/utf8>>,
fun() ->
glint:command(
fun(
_,
_,
Flags
) ->
Config_path@1 = begin
_pipe@12 = Config_path(
Flags
),
gleam@result:unwrap(
_pipe@12,
<<"./oaspec.yaml"/utf8>>
)
end,
Mode_opt = case begin
_pipe@13 = Mode(
Flags
),
gleam@result:unwrap(
_pipe@13,
<<""/utf8>>
)
end of
<<""/utf8>> ->
none;
S ->
{some,
S}
end,
Output_opt = case begin
_pipe@14 = Output(
Flags
),
gleam@result:unwrap(
_pipe@14,
<<""/utf8>>
)
end of
<<""/utf8>> ->
none;
S@1 ->
{some,
S@1}
end,
Check_mode = begin
_pipe@15 = Check(
Flags
),
gleam@result:unwrap(
_pipe@15,
false
)
end,
Fail_on_warnings_mode = begin
_pipe@16 = Fail_on_warnings(
Flags
),
gleam@result:unwrap(
_pipe@16,
false
)
end,
Validate_mode = begin
_pipe@17 = Validate(
Flags
),
gleam@result:unwrap(
_pipe@17,
false
)
end,
run_generate(
Config_path@1,
Mode_opt,
Output_opt,
Check_mode,
Fail_on_warnings_mode,
Validate_mode
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end.
-file("src/oaspec/internal/cli.gleam", 795).
?DOC(false).
-spec run_validate(binary(), gleam@option:option(binary())) -> nil.
run_validate(Config_path, Mode_opt) ->
gleam_stdlib:println(<<"oaspec v"/utf8, (<<"0.56.0"/utf8>>)/binary>>),
gleam_stdlib:println(<<"Loading config from: "/utf8, Config_path/binary>>),
require(
load_configs(Config_path, Mode_opt, none),
fun load_config_error_to_string/1,
fun(Cfgs) ->
First_cfg@1 = case Cfgs of
[First_cfg | _] -> First_cfg;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"oaspec/internal/cli"/utf8>>,
function => <<"run_validate"/utf8>>,
line => 807,
value => _assert_fail,
start => 26537,
'end' => 26570,
pattern_start => 26548,
pattern_end => 26563})
end,
Shared_input = oaspec@config:input(First_cfg@1),
gleam_stdlib:println(
<<"Parsing OpenAPI spec: "/utf8, Shared_input/binary>>
),
Reporter = oaspec@internal@progress:stdout_with_elapsed(),
require(
oaspec@openapi@parser:parse_file_with_progress_and_locations(
Shared_input,
Reporter
),
fun(E) ->
<<"Error: "/utf8,
(oaspec@openapi@parser:parse_error_to_string(E))/binary>>
end,
fun(Parsed) ->
{Spec, Locations} = Parsed,
Multi_target = case Cfgs of
[_, _ | _] ->
true;
_ ->
false
end,
gleam@list:each(
Cfgs,
fun(Cfg) ->
case Multi_target of
true ->
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<<<"[target: "/utf8,
(oaspec@config:package(Cfg))/binary>>/binary,
"]"/utf8>>
);
false ->
nil
end,
require(
oaspec@generate:validate_only_with_progress_and_locations(
Spec,
Locations,
Cfg,
Reporter
),
format_generate_error_for(Shared_input),
fun(Summary) ->
gleam_stdlib:println(
<<"Spec loaded: "/utf8,
(erlang:element(2, Summary))/binary>>
),
print_warnings_for(
Shared_input,
erlang:element(3, Summary)
)
end
)
end
),
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(<<"Validation passed."/utf8>>)
end
)
end
).
-file("src/oaspec/internal/cli.gleam", 146).
?DOC(false).
-spec validate_command() -> glint:command(nil).
validate_command() ->
begin
glint:flag(
begin
_pipe = glint:string_flag(<<"config"/utf8>>),
_pipe@1 = glint:flag_default(_pipe, <<"./oaspec.yaml"/utf8>>),
glint:flag_help(_pipe@1, <<"Path to config file"/utf8>>)
end,
fun(Config_path) ->
glint:flag(
begin
_pipe@2 = glint:string_flag(<<"mode"/utf8>>),
_pipe@3 = glint:flag_default(_pipe@2, <<""/utf8>>),
glint:flag_help(
_pipe@3,
<<"Generation mode: server, client, or both (overrides config)"/utf8>>
)
end,
fun(Mode) ->
glint:command_help(
<<"Validate an OpenAPI specification without generating code"/utf8>>,
fun() ->
glint:command(
fun(_, _, Flags) ->
Config_path@1 = begin
_pipe@4 = Config_path(Flags),
gleam@result:unwrap(
_pipe@4,
<<"./oaspec.yaml"/utf8>>
)
end,
Mode_opt = case begin
_pipe@5 = Mode(Flags),
gleam@result:unwrap(
_pipe@5,
<<""/utf8>>
)
end of
<<""/utf8>> ->
none;
S ->
{some, S}
end,
run_validate(Config_path@1, Mode_opt)
end
)
end
)
end
)
end
)
end.
-file("src/oaspec/internal/cli.gleam", 46).
?DOC(false).
-spec app() -> glint:glint(nil).
app() ->
_pipe = glint:new(),
_pipe@1 = glint:with_name(_pipe, <<"oaspec"/utf8>>),
_pipe@2 = glint:global_help(
_pipe@1,
<<"Generate Gleam code from OpenAPI 3.x specifications\n\nCommands:\n init Create a default oaspec.yaml config file\n generate Generate Gleam code from an OpenAPI spec\n validate Validate an OpenAPI spec without generating code\n version Print the oaspec version and exit\n\nRun 'oaspec <command> --help' for more information.\nUse 'oaspec --version' for a flag-style version check."/utf8>>
),
_pipe@3 = maybe_pretty_help(_pipe@2),
_pipe@4 = glint:add(_pipe@3, [<<"init"/utf8>>], init_command()),
_pipe@5 = glint:add(_pipe@4, [<<"generate"/utf8>>], generate_command()),
_pipe@6 = glint:add(_pipe@5, [<<"validate"/utf8>>], validate_command()),
glint:add(_pipe@6, [<<"version"/utf8>>], version_command()).