-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([app/0]). -export_type([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 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", 29). ?DOC(false). -spec maybe_pretty_help(glint:glint(XKS)) -> glint:glint(XKS). 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", 59). ?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.37.0"/utf8>>)/binary>> ) end ) end ). -file("src/oaspec/internal/cli.gleam", 334). ?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", 477). ?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", 425). ?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 = <<<<<>/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", 523). ?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", 533). ?DOC(false). -spec load_config( binary(), gleam@option:option(binary()), gleam@option:option(binary()) ) -> {ok, oaspec@config:config()} | {error, load_config_error()}. load_config(Config_path, Mode_opt, Output_opt) -> gleam@result:'try'( begin _pipe = oaspec@config:load(Config_path), gleam@result:map_error( _pipe, fun(Field@0) -> {config_load_error, Field@0} end ) 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(Parsed) -> oaspec@config:with_mode(Cfg, Parsed) end ), gleam@result:map_error( _pipe@2, fun(Field@0) -> {mode_parse_error, Field@0} end ) end, fun(Cfg@1) -> Cfg@2 = case Output_opt of none -> Cfg@1; {some, Path} -> oaspec@config:with_output(Cfg@1, {some, Path}) end, gleam@result:'try'( begin _pipe@3 = oaspec@config:validate_output_package_match( Cfg@2 ), gleam@result:map_error( _pipe@3, fun(Field@0) -> {output_validation_error, Field@0} end ) end, fun(_) -> _pipe@4 = oaspec@config:validate_output_dir_layout( Cfg@2 ), _pipe@5 = gleam@result:map( _pipe@4, fun(_) -> Cfg@2 end ), gleam@result:map_error( _pipe@5, fun(Field@0) -> {output_validation_error, Field@0} end ) end ) end) end ). -file("src/oaspec/internal/cli.gleam", 579). ?DOC(false). -spec format_generate_error(oaspec@generate:generate_error()) -> binary(). format_generate_error(Err) -> {validation_errors, Errors} = Err, <<"Error: OpenAPI spec contains unsupported features:\n"/utf8, (gleam@string:join( gleam@list:map( Errors, fun(Validation_error) -> <<" - "/utf8, (oaspec@openapi@diagnostic:to_short_string( Validation_error ))/binary>> end ), <<"\n"/utf8>> ))/binary>>. -file("src/oaspec/internal/cli.gleam", 592). ?DOC(false). -spec print_warnings(list(oaspec@openapi@diagnostic:diagnostic())) -> nil. print_warnings(Warnings) -> case Warnings of [] -> nil; _ -> gleam_stdlib:println_error(<<"Warnings:"/utf8>>), gleam@list:each( Warnings, fun(W) -> gleam_stdlib:println_error( <<" - "/utf8, (oaspec@openapi@diagnostic:to_short_string(W))/binary>> ) end ) end. -file("src/oaspec/internal/cli.gleam", 197). ?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 /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 -> / # # client -> /_client # # so a single `gleam build` rooted at # # picks up both. # server: ./gen/api # Override server output path # client: ./gen/api_client # Override client output path "/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", 179). ?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", 358). ?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, <>}; {ok, Existing} -> case gleam@string:compare(Existing, Content) of eq -> {error, nil}; _ -> {ok, <>} 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 = <<<>/binary, Name/binary>>, case gleam@list:contains( Expected_paths, Full_path ) of true -> {error, nil}; false -> {ok, <>} 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", 564). ?DOC(false). -spec require( {ok, XLM} | {error, XLN}, fun((XLN) -> binary()), fun((XLM) -> 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", 307). ?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", 258). ?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.37.0"/utf8>>)/binary>>), gleam_stdlib:println(<<"Loading config from: "/utf8, Config_path/binary>>), require( load_config(Config_path, Mode_opt, Output_opt), fun load_config_error_to_string/1, fun(Cfg) -> Cfg@1 = case Validate_mode of true -> oaspec@config:with_validate(Cfg, true); false -> Cfg end, gleam_stdlib:println( <<"Parsing OpenAPI spec: "/utf8, (oaspec@config:input(Cfg@1))/binary>> ), Reporter = oaspec@internal@progress:stdout_with_elapsed(), require( oaspec@openapi@parser:parse_file_with_progress( oaspec@config:input(Cfg@1), Reporter ), fun(E) -> <<"Error: "/utf8, (oaspec@openapi@parser:parse_error_to_string(E))/binary>> end, fun(Spec) -> require( oaspec@generate:generate_with_progress( Spec, Cfg@1, Reporter ), fun format_generate_error/1, fun(Summary) -> gleam_stdlib:println( <<"Spec loaded: "/utf8, (erlang:element(3, Summary))/binary>> ), print_warnings(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@1); false -> write_files( erlang:element(2, Summary), Cfg@1 ) end end ) end ) end ). -file("src/oaspec/internal/cli.gleam", 68). ?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, <<"Enable guard validation in generated server/client code"/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", 486). ?DOC(false). -spec run_validate(binary(), gleam@option:option(binary())) -> nil. run_validate(Config_path, Mode_opt) -> gleam_stdlib:println(<<"oaspec v"/utf8, (<<"0.37.0"/utf8>>)/binary>>), gleam_stdlib:println(<<"Loading config from: "/utf8, Config_path/binary>>), require( load_config(Config_path, Mode_opt, none), fun load_config_error_to_string/1, fun(Cfg) -> gleam_stdlib:println( <<"Parsing OpenAPI spec: "/utf8, (oaspec@config:input(Cfg))/binary>> ), Reporter = oaspec@internal@progress:stdout_with_elapsed(), require( oaspec@openapi@parser:parse_file_with_progress( oaspec@config:input(Cfg), Reporter ), fun(E) -> <<"Error: "/utf8, (oaspec@openapi@parser:parse_error_to_string(E))/binary>> end, fun(Spec) -> require( oaspec@generate:validate_only_with_progress( Spec, Cfg, Reporter ), fun format_generate_error/1, fun(Summary) -> gleam_stdlib:println( <<"Spec loaded: "/utf8, (erlang:element(2, Summary))/binary>> ), print_warnings(erlang:element(3, Summary)), gleam_stdlib:println(<<""/utf8>>), gleam_stdlib:println(<<"Validation passed."/utf8>>) end ) end ) end ). -file("src/oaspec/internal/cli.gleam", 145). ?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", 45). ?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 --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()).