Packages
oaspec
0.10.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.0
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.1
0.58.0
0.57.0
0.56.0
0.55.0
0.54.0
0.53.0
0.52.0
0.51.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.3
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.1.3
Generate Gleam code from OpenAPI 3.x specifications
Current section
Files
Jump to
Current section
Files
src/oaspec@cli.erl
-module(oaspec@cli).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/cli.gleam").
-export([app/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.
-file("src/oaspec/cli.gleam", 402).
-spec unique_id() -> binary().
unique_id() ->
N = erlang:unique_integer(),
case N < 0 of
true ->
<<"n"/utf8, (erlang:integer_to_binary(- N))/binary>>;
false ->
erlang:integer_to_binary(N)
end.
-file("src/oaspec/cli.gleam", 354).
?DOC(
" Format resolved content by writing to a temp directory, running gleam format,\n"
" and reading back the formatted content.\n"
).
-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(E) -> erlang:element(2, E) end
),
Formatted_resolved = case oaspec@formatter:format_files(Temp_paths) of
{ok, nil} ->
gleam@list:map(
Temp_entries,
fun(Entry@1) ->
{Original_path@1, Temp_path@1} = Entry@1,
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/cli.gleam", 458).
?DOC(" Pure config loading and validation pipeline.\n").
-spec load_config(
binary(),
gleam@option:option(binary()),
gleam@option:option(binary())
) -> {ok, oaspec@config:config()} | {error, binary()}.
load_config(Config_path, Mode_opt, Output_opt) ->
gleam@result:'try'(
begin
_pipe = oaspec@config:load(Config_path),
gleam@result:map_error(_pipe, fun oaspec@config:error_to_string/1)
end,
fun(Cfg) -> gleam@result:'try'(case Mode_opt of
none ->
{ok, Cfg};
{some, Mode_str} ->
_pipe@1 = oaspec@config:parse_mode(Mode_str),
_pipe@2 = gleam@result:map(
_pipe@1,
fun(M) -> oaspec@config:with_mode(Cfg, M) end
),
gleam@result:map_error(
_pipe@2,
fun oaspec@config:error_to_string/1
)
end, fun(Cfg@1) ->
Cfg@2 = case Output_opt of
none ->
Cfg@1;
{some, Path} ->
oaspec@config:with_output(Cfg@1, {some, Path})
end,
_pipe@3 = oaspec@config:validate_output_package_match(Cfg@2),
_pipe@4 = gleam@result:map(_pipe@3, fun(_) -> Cfg@2 end),
gleam@result:map_error(
_pipe@4,
fun oaspec@config:error_to_string/1
)
end) end
).
-file("src/oaspec/cli.gleam", 152).
?DOC(" Create a config file template.\n").
-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
# Output settings (optional).
# output:
# dir: ./gen # Base output directory (default: ./gen)
# server: ./gen/api # Override server output path
# client: ./gen_client/api # Override client output path
"/utf8>>,
case simplifile_erl:is_file(Path) of
{ok, true} ->
gleam_stdlib:println(
<<<<"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, _} ->
gleam_stdlib:println(
<<"Error: failed to write "/utf8, Path/binary>>
),
erlang:halt(1)
end
end.
-file("src/oaspec/cli.gleam", 131).
?DOC(" The init command definition.\n").
-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 = case Output_path(Flags) of
{ok, P} ->
P;
{error, _} ->
<<"./oaspec.yaml"/utf8>>
end,
run_init(Path)
end
)
end
)
end
)
end.
-file("src/oaspec/cli.gleam", 289).
?DOC(
" Check that generated code matches existing files on disk.\n"
" Exits 0 if all files match, exits 1 if any differ, missing, or orphaned.\n"
).
-spec run_check(
list(oaspec@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 = gleam@list:map(
Resolved,
fun(Entry) -> erlang:element(1, Entry) end
),
Resolved@1 = format_resolved_content(Resolved),
Diffs = gleam@list:filter_map(
Resolved@1,
fun(Entry@1) ->
{Path, Content} = Entry@1,
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(<<""/utf8>>),
gleam@list:each(
All_issues,
fun(D) -> gleam_stdlib:println(<<" "/utf8, D/binary>>) end
),
gleam_stdlib:println(
<<<<"\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/cli.gleam", 194).
?DOC(
" Execute the generation pipeline.\n"
" Handles IO (printing, exit codes) while delegating pure logic to\n"
" generate.generate and load_config.\n"
).
-spec run_generate(
binary(),
gleam@option:option(binary()),
gleam@option:option(binary()),
boolean(),
boolean()
) -> nil.
run_generate(Config_path, Mode_opt, Output_opt, Check_mode, Fail_on_warnings) ->
gleam_stdlib:println(<<"oaspec v"/utf8, (<<"0.10.0"/utf8>>)/binary>>),
gleam_stdlib:println(<<"Loading config from: "/utf8, Config_path/binary>>),
case load_config(Config_path, Mode_opt, Output_opt) of
{error, Msg} ->
gleam_stdlib:println(<<"Error: "/utf8, Msg/binary>>),
erlang:halt(1);
{ok, Cfg} ->
gleam_stdlib:println(
<<"Parsing OpenAPI spec: "/utf8,
(erlang:element(2, Cfg))/binary>>
),
case oaspec@openapi@parser:parse_file(erlang:element(2, Cfg)) of
{error, E} ->
gleam_stdlib:println(
<<"Error: "/utf8,
(oaspec@openapi@parser:parse_error_to_string(E))/binary>>
),
erlang:halt(1);
{ok, Spec} ->
case oaspec@generate:generate(Spec, Cfg) of
{error, {validation_errors, Errors}} ->
gleam_stdlib:println(
<<"Error: OpenAPI spec contains unsupported features:"/utf8>>
),
gleam@list:each(
Errors,
fun(E@1) ->
gleam_stdlib:println(
<<" - "/utf8,
(oaspec@openapi@diagnostic:to_string(
E@1
))/binary>>
)
end
),
erlang:halt(1);
{ok, Summary} ->
gleam_stdlib:println(
<<"Spec loaded: "/utf8,
(erlang:element(3, Summary))/binary>>
),
Has_warnings = erlang:element(4, Summary) /= [],
case erlang:element(4, Summary) of
[] ->
nil;
Warnings ->
gleam_stdlib:println(<<"Warnings:"/utf8>>),
gleam@list:each(
Warnings,
fun(W) ->
gleam_stdlib:println(
<<" - "/utf8,
(oaspec@openapi@diagnostic:to_string(
W
))/binary>>
)
end
)
end,
case Fail_on_warnings andalso Has_warnings of
true ->
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<"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 ->
gleam_stdlib:println(
<<"Generating code..."/utf8>>
),
case oaspec@codegen@writer:write_all(
erlang:element(2, Summary),
Cfg,
fun(Path) ->
gleam_stdlib:println(
<<" Generated: "/utf8,
Path/binary>>
)
end
) of
{ok, Written} ->
Output_dirs = oaspec@codegen@writer:output_dirs(
Cfg
),
case oaspec@formatter:format_files(
Output_dirs
) of
{ok, nil} ->
gleam_stdlib:println(
<<" Formatted generated code"/utf8>>
);
{error, E@2} ->
gleam_stdlib:println(
<<"Error: "/utf8,
(oaspec@formatter:error_to_string(
E@2
))/binary>>
),
erlang:halt(1)
end,
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(
<<<<"Successfully generated "/utf8,
(erlang:integer_to_binary(
erlang:length(
Written
)
))/binary>>/binary,
" files"/utf8>>
);
{error, E@3} ->
gleam_stdlib:println(
<<"Error: "/utf8,
(oaspec@codegen@writer:error_to_string(
E@3
))/binary>>
),
erlang:halt(1)
end
end
end
end
end.
-file("src/oaspec/cli.gleam", 30).
?DOC(" The generate command definition.\n").
-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:command_help(
<<"Generate Gleam code from an OpenAPI specification"/utf8>>,
fun() ->
glint:command(
fun(_, _, Flags) ->
Config_path@1 = begin
_pipe@10 = Config_path(
Flags
),
gleam@result:unwrap(
_pipe@10,
<<"./oaspec.yaml"/utf8>>
)
end,
Mode_opt = case begin
_pipe@11 = Mode(
Flags
),
gleam@result:unwrap(
_pipe@11,
<<""/utf8>>
)
end of
<<""/utf8>> ->
none;
S ->
{some,
S}
end,
Output_opt = case begin
_pipe@12 = Output(
Flags
),
gleam@result:unwrap(
_pipe@12,
<<""/utf8>>
)
end of
<<""/utf8>> ->
none;
S@1 ->
{some,
S@1}
end,
Check_mode = begin
_pipe@13 = Check(
Flags
),
gleam@result:unwrap(
_pipe@13,
false
)
end,
Fail_on_warnings_mode = begin
_pipe@14 = Fail_on_warnings(
Flags
),
gleam@result:unwrap(
_pipe@14,
false
)
end,
run_generate(
Config_path@1,
Mode_opt,
Output_opt,
Check_mode,
Fail_on_warnings_mode
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end.
-file("src/oaspec/cli.gleam", 411).
?DOC(" Execute the validation-only pipeline.\n").
-spec run_validate(binary(), gleam@option:option(binary())) -> nil.
run_validate(Config_path, Mode_opt) ->
gleam_stdlib:println(<<"oaspec v"/utf8, (<<"0.10.0"/utf8>>)/binary>>),
gleam_stdlib:println(<<"Loading config from: "/utf8, Config_path/binary>>),
case load_config(Config_path, Mode_opt, none) of
{error, Msg} ->
gleam_stdlib:println(<<"Error: "/utf8, Msg/binary>>),
erlang:halt(1);
{ok, Cfg} ->
gleam_stdlib:println(
<<"Parsing OpenAPI spec: "/utf8,
(erlang:element(2, Cfg))/binary>>
),
case oaspec@openapi@parser:parse_file(erlang:element(2, Cfg)) of
{error, E} ->
gleam_stdlib:println(
<<"Error: "/utf8,
(oaspec@openapi@parser:parse_error_to_string(E))/binary>>
),
erlang:halt(1);
{ok, Spec} ->
case oaspec@generate:validate_only(Spec, Cfg) of
{error, {validation_errors, Errors}} ->
gleam_stdlib:println(
<<"Error: OpenAPI spec contains unsupported features:"/utf8>>
),
gleam@list:each(
Errors,
fun(E@1) ->
gleam_stdlib:println(
<<" - "/utf8,
(oaspec@openapi@diagnostic:to_string(
E@1
))/binary>>
)
end
),
erlang:halt(1);
{ok, Summary} ->
gleam_stdlib:println(
<<"Spec loaded: "/utf8,
(erlang:element(2, Summary))/binary>>
),
case erlang:element(3, Summary) of
[] ->
nil;
Warnings ->
gleam_stdlib:println(<<"Warnings:"/utf8>>),
gleam@list:each(
Warnings,
fun(W) ->
gleam_stdlib:println(
<<" - "/utf8,
(oaspec@openapi@diagnostic:to_string(
W
))/binary>>
)
end
)
end,
gleam_stdlib:println(<<""/utf8>>),
gleam_stdlib:println(<<"Validation passed."/utf8>>)
end
end
end.
-file("src/oaspec/cli.gleam", 97).
?DOC(" The validate command definition.\n").
-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/cli.gleam", 19).
?DOC(" Set up the CLI application.\n").
-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"/utf8>>
),
_pipe@3 = glint:pretty_help(_pipe@2, glint:default_pretty_help()),
_pipe@4 = glint:add(_pipe@3, [<<"generate"/utf8>>], generate_command()),
_pipe@5 = glint:add(_pipe@4, [<<"validate"/utf8>>], validate_command()),
glint:add(_pipe@5, [<<"init"/utf8>>], init_command()).