-module(oaspec@openapi@parser). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/oaspec/openapi/parser.gleam"). -export([default_limits/0, parse_error_to_string/1, parse_string_with_locations/1, parse_string/1, parse_string_with_limits/2, parse_json_string_with_locations/1, parse_file/1, parse_file_with_progress_and_locations/2, parse_json_string/1, parse_string_or_json_with_locations/1]). -export_type([parse_limits/0, placeholder_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. -type parse_limits() :: {parse_limits, integer(), integer(), integer(), integer(), integer(), integer()}. -type placeholder_error() :: unclosed | nested_brace | unmatched_close | {invalid_name, binary()}. -file("src/oaspec/openapi/parser.gleam", 146). -spec format_size(integer()) -> binary(). format_size(Bytes) -> case Bytes < 1024 of true -> <<(erlang:integer_to_binary(Bytes))/binary, " B"/utf8>>; false -> case Bytes < (1024 * 1024) of true -> <<(erlang:integer_to_binary(Bytes div 1024))/binary, " KiB"/utf8>>; false -> <<(erlang:integer_to_binary(case (1024 * 1024) of 0 -> 0; Gleam@denominator -> Bytes div Gleam@denominator end))/binary, " MiB"/utf8>> end end. -file("src/oaspec/openapi/parser.gleam", 162). ?DOC( " Normalize a path string for cycle detection. This is not a full\n" " canonicalization (no symlink resolution, no realpath call); it just\n" " collapses the `./` and duplicate-slash noise that `filepath.join`\n" " can leave behind so the same file reached via two equivalent\n" " relative paths compares equal.\n" ). -spec canonicalize_ref_path(binary()) -> binary(). canonicalize_ref_path(Path) -> Segments = begin _pipe = Path, _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), gleam@list:filter( _pipe@1, fun(Seg) -> (Seg /= <<""/utf8>>) andalso (Seg /= <<"."/utf8>>) end ) end, case gleam_stdlib:string_starts_with(Path, <<"/"/utf8>>) of true -> <<"/"/utf8, (gleam@string:join(Segments, <<"/"/utf8>>))/binary>>; false -> gleam@string:join(Segments, <<"/"/utf8>>) end. -file("src/oaspec/openapi/parser.gleam", 173). -spec cyclic_external_ref_diagnostic(binary(), list(binary())) -> oaspec@openapi@diagnostic:diagnostic(). cyclic_external_ref_diagnostic(Current, Visited) -> Chain = lists:reverse([Current | Visited]), oaspec@openapi@diagnostic:invalid_value( <<"external_ref"/utf8>>, <<<<"Cyclic external $ref graph detected: "/utf8, (gleam@string:join(Chain, <<" -> "/utf8>>))/binary>>/binary, ". oaspec requires external ref graphs to be acyclic."/utf8>>, no_source_loc ). -file("src/oaspec/openapi/parser.gleam", 231). ?DOC( " Project-default limits sized for real-world specs.\n" "\n" " - `max_input_bytes`: 16 MiB — Stripe's full OpenAPI is ~6 MB,\n" " GitHub's REST API is ~12 MB; 16 MiB clears both with headroom.\n" " - `max_schema_depth`: 100. Real specs rarely nest beyond ~12.\n" " - `max_allof_chain`: 32.\n" " - `max_external_ref_hops`: 16.\n" " - `max_paths`: 4096. Stripe (~1k operations), GitHub (~1k), and\n" " AsyncAPI (~50) all fit comfortably.\n" " - `max_parameters_per_op`: 64. The largest real-world operation\n" " the audit found has ~20 parameters.\n" ). -spec default_limits() -> parse_limits(). default_limits() -> {parse_limits, (16 * 1024) * 1024, 100, 32, 16, 4096, 64}. -file("src/oaspec/openapi/parser.gleam", 316). -spec enforce_input_byte_limit(binary(), parse_limits()) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. enforce_input_byte_limit(Content, Limits) -> Actual = erlang:byte_size(Content), case Actual > erlang:element(2, Limits) of false -> {ok, nil}; true -> {error, oaspec@openapi@diagnostic:parse_limit_exceeded( <<"max_input_bytes"/utf8>>, erlang:element(2, Limits), Actual )} end. -file("src/oaspec/openapi/parser.gleam", 396). -spec looks_like_json(binary()) -> boolean(). looks_like_json(Content) -> case gleam@string:trim_start(Content) of <<"{"/utf8, _/binary>> -> true; <<"["/utf8, _/binary>> -> true; _ -> false end. -file("src/oaspec/openapi/parser.gleam", 412). -spec parse_to_node(binary()) -> {ok, {yay:node_(), oaspec@internal@openapi@location_index:location_index()}} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_to_node(Content) -> gleam@result:'try'( begin _pipe = oaspec_yaml_safe_ffi:parse_string(Content), gleam@result:map_error(_pipe, fun(E) -> case E of {parsing_error, Msg, Loc} -> oaspec@openapi@diagnostic:yaml_error( Msg, {source_loc, erlang:element(2, Loc), erlang:element(3, Loc)} ); unexpected_parsing_error -> oaspec@openapi@diagnostic:yaml_error( <<"Unexpected parsing error"/utf8>>, no_source_loc ) end end) end, fun(Docs) -> gleam@result:'try'(case Docs of [First | _] -> {ok, First}; [] -> {error, oaspec@openapi@diagnostic:yaml_error( <<"Empty document"/utf8>>, no_source_loc )} end, fun(Doc) -> Root = yay:document_root(Doc), Index = oaspec@internal@openapi@location_index:build( Content ), {ok, {Root, Index}} end) end ). -file("src/oaspec/openapi/parser.gleam", 460). ?DOC( " Decode a JSON string into a yay.Node via the OTP `json` module.\n" " Wraps the FFI's `Result(List(Document), YamlError)` shape into a\n" " `Diagnostic` so it lines up with `parse_to_node`'s contract.\n" ). -spec decode_json_to_node(binary()) -> {ok, yay:node_()} | {error, oaspec@openapi@diagnostic:diagnostic()}. decode_json_to_node(Content) -> gleam@result:'try'( begin _pipe = oaspec_json_ffi:parse_string(Content), gleam@result:map_error(_pipe, fun(E) -> case E of {parsing_error, Msg, Loc} -> oaspec@openapi@diagnostic:yaml_error( Msg, case {erlang:element(2, Loc), erlang:element(3, Loc)} of {0, 0} -> no_source_loc; {_, _} -> {source_loc, erlang:element(2, Loc), erlang:element(3, Loc)} end ); unexpected_parsing_error -> oaspec@openapi@diagnostic:yaml_error( <<"Unexpected JSON parsing error"/utf8>>, no_source_loc ) end end) end, fun(Docs) -> case Docs of [First | _] -> {ok, yay:document_root(First)}; [] -> {error, oaspec@openapi@diagnostic:yaml_error( <<"Empty document"/utf8>>, no_source_loc )} end end ). -file("src/oaspec/openapi/parser.gleam", 486). -spec looks_like_json_path(binary()) -> boolean(). looks_like_json_path(Path) -> Lower = string:lowercase(Path), gleam_stdlib:string_ends_with(Lower, <<".json"/utf8>>). -file("src/oaspec/openapi/parser.gleam", 585). -spec is_supported_openapi_version(binary()) -> boolean(). is_supported_openapi_version(Version) -> case gleam@string:split(Version, <<"."/utf8>>) of [<<"3"/utf8>>, <<"0"/utf8>>, Patch] -> case gleam_stdlib:parse_int(Patch) of {ok, N} when N >= 0 -> true; _ -> false end; [<<"3"/utf8>>, <<"1"/utf8>>, Patch] -> case gleam_stdlib:parse_int(Patch) of {ok, N} when N >= 0 -> true; _ -> false end; [<<"3"/utf8>>, <<"0"/utf8>>] -> true; [<<"3"/utf8>>, <<"1"/utf8>>] -> true; _ -> false end. -file("src/oaspec/openapi/parser.gleam", 571). ?DOC( " Reject spec files whose `openapi` field is not in the supported 3.0.x /\n" " 3.1.x range. OpenAPI 3.x is what oaspec claims to generate from, so\n" " accepting 2.0 or 4.0.0 would let users feed specs that are either\n" " subtly or grossly incompatible with the generator.\n" ). -spec validate_openapi_version(binary(), oaspec@openapi@diagnostic:source_loc()) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. validate_openapi_version(Version, Loc) -> gleam@bool:guard( is_supported_openapi_version(Version), {ok, nil}, fun() -> {error, oaspec@openapi@diagnostic:invalid_value( <<"openapi"/utf8>>, <<<<"Unsupported OpenAPI version: '"/utf8, Version/binary>>/binary, "'. oaspec only supports OpenAPI 3.0.x and 3.1.x."/utf8>>, Loc )} end ). -file("src/oaspec/openapi/parser.gleam", 610). ?DOC( " Extract the root `openapi` field. The OAS 3.0 / 3.1 schema requires\n" " this field to be a string, and both parsers enforce that contract\n" " uniformly. The YAML caller used to fall back to `extract_float` so\n" " an unquoted `openapi: 3.0` (which yamerl resolves as a float) would\n" " coerce back to `\"3.0\"`; that lenient path was the YAML-side of the\n" " #580 asymmetry and #583 closes it. Authors who relied on unquoted\n" " version numbers must quote them (`openapi: '3.0'`).\n" ). -spec extract_openapi_field( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, binary()} | {error, oaspec@openapi@diagnostic:diagnostic()}. extract_openapi_field(Node, Index) -> Loc = oaspec@internal@openapi@location_index:lookup_field( Index, <<""/utf8>>, <<"openapi"/utf8>> ), _pipe = yay:extract_string(Node, <<"openapi"/utf8>>), gleam@result:map_error( _pipe, fun(_capture) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture, <<""/utf8>>, <<"openapi"/utf8>>, Loc ) end ). -file("src/oaspec/openapi/parser.gleam", 628). ?DOC( " Mirror of yay's private `node_type_name` for the diagnostic\n" " detail line. Kept local because yay does not export the helper\n" " and the alternative (re-pattern-matching at every call site) is\n" " noisier than a four-line wrapper.\n" ). -spec node_kind_name(yay:node_()) -> binary(). node_kind_name(Node) -> case Node of node_nil -> <<"nil"/utf8>>; {node_str, _} -> <<"string"/utf8>>; {node_bool, _} -> <<"bool"/utf8>>; {node_int, _} -> <<"int"/utf8>>; {node_float, _} -> <<"float"/utf8>>; {node_seq, _} -> <<"list"/utf8>>; {node_map, _} -> <<"map"/utf8>> end. -file("src/oaspec/openapi/parser.gleam", 645). ?DOC( " Whether the validated version string belongs to the OAS 3.0.x\n" " branch (where `paths` is required at the document root). Accepts\n" " both the three-segment `3.0.x` form and the two-segment `3.0`\n" " form that `is_supported_openapi_version` tolerates for\n" " yamerl-coerced floats.\n" ). -spec is_openapi_3_0(binary()) -> boolean(). is_openapi_3_0(Version) -> case gleam@string:split(Version, <<"."/utf8>>) of [<<"3"/utf8>>, <<"0"/utf8>>, _] -> true; [<<"3"/utf8>>, <<"0"/utf8>>] -> true; _ -> false end. -file("src/oaspec/openapi/parser.gleam", 655). ?DOC( " Verify the `paths` field exists at the document root. Used only\n" " for OAS 3.0; `parse_paths` itself stays lenient on the missing\n" " case so 3.1 documents continue to parse without `paths`.\n" ). -spec require_paths_present( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. require_paths_present(Root, Index) -> case yay:select_sugar(Root, <<"paths"/utf8>>) of {ok, _} -> {ok, nil}; {error, _} -> {error, oaspec@openapi@diagnostic:missing_field( <<""/utf8>>, <<"paths"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<""/utf8>>, <<"paths"/utf8>> ) )} end. -file("src/oaspec/openapi/parser.gleam", 716). -spec check_component_keys( gleam@dict:dict(binary(), any()), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. check_component_keys(M, Category, Index) -> Re@1 = case gleam@regexp:from_string(<<"^[a-zA-Z0-9._-]+$"/utf8>>) of {ok, Re} -> Re; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"oaspec/openapi/parser"/utf8>>, function => <<"check_component_keys"/utf8>>, line => 722, value => _assert_fail, start => 26997, 'end' => 27056, pattern_start => 27008, pattern_end => 27014}) end, _pipe = maps:keys(M), _pipe@1 = gleam@list:try_fold( _pipe, nil, fun(_, Key) -> case gleam@regexp:check(Re@1, Key) of true -> {ok, nil}; false -> {error, oaspec@openapi@diagnostic:invalid_value( <<"components."/utf8, Category/binary>>, <<<<"Component key '"/utf8, Key/binary>>/binary, "' must match the OAS regex '^[a-zA-Z0-9._-]+$'."/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"components"/utf8>>, Category ) )} end end ), gleam@result:map(_pipe@1, fun(_) -> nil end). -file("src/oaspec/openapi/parser.gleam", 676). ?DOC( " OAS 3.0 §4.7.7.1: keys of the maps inside `components` MUST match\n" " `^[a-zA-Z0-9._-]+$`. Pre-fix, arbitrary strings (empty, whitespace,\n" " `/`, `@`, non-ASCII) flowed into the codegen and produced Gleam\n" " identifiers the type system would reject. The check runs after the\n" " per-map parsers populate the Dicts and rejects the first\n" " non-conforming key. (#591)\n" ). -spec validate_component_keys( oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved()), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. validate_component_keys(Components, Index) -> gleam@result:'try'( check_component_keys( erlang:element(2, Components), <<"schemas"/utf8>>, Index ), fun(_) -> gleam@result:'try'( check_component_keys( erlang:element(5, Components), <<"responses"/utf8>>, Index ), fun(_) -> gleam@result:'try'( check_component_keys( erlang:element(3, Components), <<"parameters"/utf8>>, Index ), fun(_) -> gleam@result:'try'( check_component_keys( erlang:element(9, Components), <<"examples"/utf8>>, Index ), fun(_) -> gleam@result:'try'( check_component_keys( erlang:element(4, Components), <<"requestBodies"/utf8>>, Index ), fun(_) -> gleam@result:'try'( check_component_keys( erlang:element( 8, Components ), <<"headers"/utf8>>, Index ), fun(_) -> gleam@result:'try'( check_component_keys( erlang:element( 6, Components ), <<"securitySchemes"/utf8>>, Index ), fun(_) -> gleam@result:'try'( check_component_keys( erlang:element( 10, Components ), <<"links"/utf8>>, Index ), fun(_) -> gleam@result:'try'( check_component_keys( erlang:element( 11, Components ), <<"callbacks"/utf8>>, Index ), fun(_) -> {ok, nil} end ) end ) end ) end ) end ) end ) end ) end ) end ). -file("src/oaspec/openapi/parser.gleam", 853). -spec has_parameter_ref( list(oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:parameter(oaspec@internal@openapi@spec:unresolved()))) ) -> boolean(). has_parameter_ref(Params) -> gleam@list:any(Params, fun(P) -> case P of {ref, _} -> true; {value, _} -> false end end). -file("src/oaspec/openapi/parser.gleam", 862). -spec inline_path_param_names( list(oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:parameter(oaspec@internal@openapi@spec:unresolved()))) ) -> list(binary()). inline_path_param_names(Params) -> gleam@list:filter_map(Params, fun(P) -> case P of {value, Param} -> case erlang:element(3, Param) of in_path -> {ok, erlang:element(2, Param)}; _ -> {error, nil} end; {ref, _} -> {error, nil} end end). -file("src/oaspec/openapi/parser.gleam", 877). -spec deduplicate_strings(list(binary()), list(binary())) -> list(binary()). deduplicate_strings(Items, Acc) -> case Items of [] -> lists:reverse(Acc); [Head | Rest] -> case gleam@list:contains(Acc, Head) of true -> deduplicate_strings(Rest, Acc); false -> deduplicate_strings(Rest, [Head | Acc]) end end. -file("src/oaspec/openapi/parser.gleam", 888). -spec cross_check_path_vs_params( binary(), list(binary()), list(binary()), boolean(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. cross_check_path_vs_params(Path, Path_vars, Inline_param_names, Has_ref, Index) -> Missing_param = case Has_ref of true -> {error, nil}; false -> gleam@list:find( Path_vars, fun(V) -> not gleam@list:contains(Inline_param_names, V) end ) end, case Missing_param of {ok, V@1} -> {error, oaspec@openapi@diagnostic:invalid_value( <<"paths"/utf8>>, <<<<<<<<"Path '"/utf8, Path/binary>>/binary, "' declares template variable '{"/utf8>>/binary, V@1/binary>>/binary, "}' but no matching parameter entry exists in the operation or path-level parameters list (OAS 3.0 §4.7.12.1)."/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<""/utf8>>, <<"paths"/utf8>> ) )}; {error, nil} -> Dangling_param = gleam@list:find( Inline_param_names, fun(P) -> not gleam@list:contains(Path_vars, P) end ), case Dangling_param of {ok, P@1} -> {error, oaspec@openapi@diagnostic:invalid_value( <<"paths"/utf8>>, <<<<<<<<"Path '"/utf8, Path/binary>>/binary, "' has an `in: path` parameter '"/utf8>>/binary, P@1/binary>>/binary, "' that does not appear in the path template (OAS 3.0 §4.7.12.1)."/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<""/utf8>>, <<"paths"/utf8>> ) )}; {error, nil} -> {ok, nil} end end. -file("src/oaspec/openapi/parser.gleam", 804). -spec validate_path_item_params( binary(), list(binary()), oaspec@internal@openapi@spec:path_item(oaspec@internal@openapi@spec:unresolved()), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. validate_path_item_params(Path, Path_vars, Pi, Index) -> Path_level_path_names = inline_path_param_names(erlang:element(12, Pi)), Path_level_has_ref = has_parameter_ref(erlang:element(12, Pi)), Operations = begin _pipe = [erlang:element(4, Pi), erlang:element(6, Pi), erlang:element(5, Pi), erlang:element(7, Pi), erlang:element(10, Pi), erlang:element(9, Pi), erlang:element(8, Pi), erlang:element(11, Pi)], gleam@list:filter_map(_pipe, fun(Op) -> case Op of {some, O} -> {ok, O}; none -> {error, nil} end end) end, case Operations of [] -> cross_check_path_vs_params( Path, Path_vars, Path_level_path_names, Path_level_has_ref, Index ); Ops -> _pipe@2 = gleam@list:try_fold( Ops, nil, fun(_, Op@1) -> Op_path_names = inline_path_param_names( erlang:element(6, Op@1) ), Effective = begin _pipe@1 = lists:append( Path_level_path_names, Op_path_names ), deduplicate_strings(_pipe@1, []) end, Has_ref = Path_level_has_ref orelse has_parameter_ref( erlang:element(6, Op@1) ), cross_check_path_vs_params( Path, Path_vars, Effective, Has_ref, Index ) end ), gleam@result:map(_pipe@2, fun(_) -> nil end) end. -file("src/oaspec/openapi/parser.gleam", 977). -spec normalise_path_template_loop(binary(), binary(), boolean(), binary()) -> {ok, binary()} | {error, nil}. normalise_path_template_loop(Remaining, Acc, Inside, _) -> case gleam_stdlib:string_pop_grapheme(Remaining) of {error, nil} -> case Inside of true -> {error, nil}; false -> {ok, Acc} end; {ok, {<<"{"/utf8>>, Rest}} -> case Inside of true -> {error, nil}; false -> normalise_path_template_loop( Rest, <>, true, <<""/utf8>> ) end; {ok, {<<"}"/utf8>>, Rest@1}} -> case Inside of true -> normalise_path_template_loop( Rest@1, Acc, false, <<""/utf8>> ); false -> {error, nil} end; {ok, {Ch, Rest@2}} -> case Inside of true -> normalise_path_template_loop(Rest@2, Acc, true, Ch); false -> normalise_path_template_loop( Rest@2, <>, false, <<""/utf8>> ) end end. -file("src/oaspec/openapi/parser.gleam", 965). -spec normalise_path_template(binary()) -> binary(). normalise_path_template(Path) -> case normalise_path_template_loop(Path, <<""/utf8>>, false, <<""/utf8>>) of {ok, S} -> S; {error, nil} -> Path end. -file("src/oaspec/openapi/parser.gleam", 1007). -spec find_first_template_collision( list({binary(), binary()}), list({binary(), binary()}) ) -> gleam@option:option({binary(), binary(), binary()}). find_first_template_collision(Pairs, Seen) -> case Pairs of [] -> none; [{Template, Original} | Rest] -> case gleam@list:find( Seen, fun(S) -> erlang:element(1, S) =:= Template end ) of {ok, {_, Prior_original}} -> {some, {Template, Prior_original, Original}}; {error, nil} -> find_first_template_collision( Rest, [{Template, Original} | Seen] ) end end. -file("src/oaspec/openapi/parser.gleam", 941). ?DOC( " Reject `paths:` entries that share the same routing template after\n" " the placeholder names are erased. `/users/{id}` and `/users/{name}`\n" " produce the same `/users/{}` signature and therefore collapse onto\n" " one HTTP route. (#593)\n" ). -spec validate_unique_path_templates( gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:path_item(oaspec@internal@openapi@spec:unresolved()))), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. validate_unique_path_templates(Paths, Index) -> Template_pairs = begin _pipe = maps:keys(Paths), gleam@list:map(_pipe, fun(P) -> {normalise_path_template(P), P} end) end, case find_first_template_collision(Template_pairs, []) of none -> {ok, nil}; {some, {Template, Original_a, Original_b}} -> {error, oaspec@openapi@diagnostic:invalid_value( <<"paths"/utf8>>, <<<<<<<<<<<<"Paths '"/utf8, Original_a/binary>>/binary, "' and '"/utf8>>/binary, Original_b/binary>>/binary, "' share the same routing template '"/utf8>>/binary, Template/binary>>/binary, "' (OAS 3.0 §4.7.9.1: templated paths with the same hierarchy but different placeholder names are identical)."/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <<"paths"/utf8>> ) )} end. -file("src/oaspec/openapi/parser.gleam", 1022). -spec path_item_operation_ids( oaspec@internal@openapi@spec:path_item(oaspec@internal@openapi@spec:unresolved()) ) -> list(binary()). path_item_operation_ids(Pi) -> _pipe = [erlang:element(4, Pi), erlang:element(6, Pi), erlang:element(5, Pi), erlang:element(7, Pi), erlang:element(10, Pi), erlang:element(9, Pi), erlang:element(8, Pi), erlang:element(11, Pi)], gleam@list:filter_map(_pipe, fun(Op) -> case Op of {some, O} -> case erlang:element(2, O) of {some, Id} -> {ok, Id}; none -> {error, nil} end; none -> {error, nil} end end). -file("src/oaspec/openapi/parser.gleam", 1288). -spec placeholder_error_detail(placeholder_error()) -> binary(). placeholder_error_detail(Err) -> case Err of unclosed -> <<"unclosed '{' placeholder"/utf8>>; nested_brace -> <<"'{' nested inside another placeholder"/utf8>>; unmatched_close -> <<"'}' without a matching '{'"/utf8>>; {invalid_name, Name} -> <<<<"placeholder name '"/utf8, Name/binary>>/binary, "' must match [A-Za-z0-9_-]+"/utf8>> end. -file("src/oaspec/openapi/parser.gleam", 1332). -spec is_valid_placeholder_name(binary()) -> boolean(). is_valid_placeholder_name(Name) -> Re@1 = case gleam@regexp:from_string(<<"^[A-Za-z0-9_-]+$"/utf8>>) of {ok, Re} -> Re; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"oaspec/openapi/parser"/utf8>>, function => <<"is_valid_placeholder_name"/utf8>>, line => 1334, value => _assert_fail, start => 46509, 'end' => 46567, pattern_start => 46520, pattern_end => 46526}) end, gleam@regexp:check(Re@1, Name). -file("src/oaspec/openapi/parser.gleam", 1298). -spec extract_placeholder_names(binary(), binary(), boolean(), list(binary())) -> {ok, list(binary())} | {error, placeholder_error()}. extract_placeholder_names(Remaining, Current, Inside, Acc) -> case gleam_stdlib:string_pop_grapheme(Remaining) of {error, nil} -> case Inside of true -> {error, unclosed}; false -> {ok, lists:reverse(Acc)} end; {ok, {<<"{"/utf8>>, Rest}} -> case Inside of true -> {error, nested_brace}; false -> extract_placeholder_names(Rest, <<""/utf8>>, true, Acc) end; {ok, {<<"}"/utf8>>, Rest@1}} -> case Inside of false -> {error, unmatched_close}; true -> case is_valid_placeholder_name(Current) of false -> {error, {invalid_name, Current}}; true -> extract_placeholder_names( Rest@1, <<""/utf8>>, false, [Current | Acc] ) end end; {ok, {Ch, Rest@2}} -> case Inside of true -> extract_placeholder_names( Rest@2, <>, true, Acc ); false -> extract_placeholder_names(Rest@2, Current, false, Acc) end end. -file("src/oaspec/openapi/parser.gleam", 780). ?DOC( " Cross-validate every path's templated variables against the\n" " `in: path` parameters of every operation in that path item, plus\n" " the path-level parameter list. Each direction is required by OAS:\n" "\n" " - every `{var}` in the path MUST have a corresponding parameter,\n" " - every `in: path` parameter MUST reference a `{var}` in the path.\n" "\n" " Path-level parameters are inherited by every operation, so the\n" " effective parameter set per operation is the path-level list plus\n" " the operation's own list. Inline parameters only; `Ref` entries\n" " are skipped because the resolved (name, in) is invisible here.\n" " (#594)\n" ). -spec validate_path_template_params( gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:path_item(oaspec@internal@openapi@spec:unresolved()))), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. validate_path_template_params(Paths, Index) -> _pipe = maps:to_list(Paths), _pipe@1 = gleam@list:try_fold( _pipe, nil, fun(_, Entry) -> {Path, Ref_or_pi} = Entry, case Ref_or_pi of {ref, _} -> {ok, nil}; {value, Pi} -> case extract_placeholder_names(Path, <<""/utf8>>, false, []) of {error, _} -> {ok, nil}; {ok, Path_vars} -> validate_path_item_params( Path, Path_vars, Pi, Index ) end end end ), gleam@result:map(_pipe@1, fun(_) -> nil end). -file("src/oaspec/openapi/parser.gleam", 1338). -spec has_duplicate_name(list(binary()), list(binary())) -> gleam@option:option(binary()). has_duplicate_name(Names, Seen) -> case Names of [] -> none; [Head | Rest] -> case gleam@list:contains(Seen, Head) of true -> {some, Head}; false -> has_duplicate_name(Rest, [Head | Seen]) end end. -file("src/oaspec/openapi/parser.gleam", 1273). ?DOC( " Walk the path string and verify that every `{...}` placeholder\n" " matches `[A-Za-z0-9_-]+` and that no placeholder name repeats.\n" " Returns the same `Result(Nil, Diagnostic)` as the caller's\n" " `reject` continuation so the diagnostic path / location stays\n" " consistent.\n" ). -spec validate_path_placeholders( binary(), fun((binary()) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}) ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. validate_path_placeholders(Path, Reject) -> case extract_placeholder_names(Path, <<""/utf8>>, false, []) of {error, Err} -> Reject(placeholder_error_detail(Err)); {ok, Names} -> case has_duplicate_name(Names, []) of {some, Dup} -> Reject( <<<<"placeholder '{"/utf8, Dup/binary>>/binary, "}' appears more than once"/utf8>> ); none -> {ok, nil} end end. -file("src/oaspec/openapi/parser.gleam", 1218). ?DOC( " Validate that a `paths:` key conforms to the OAS 3.0 §4.7.9.1 path\n" " templating grammar. The parser used to forward any string to the\n" " codegen pipeline, which then emitted routes the HTTP layer cannot\n" " serve (no leading slash, embedded `?` / `#`, whitespace, malformed\n" " `{var}` placeholders, ...). The checks here cover the deviations\n" " listed in #588:\n" "\n" " - non-empty,\n" " - starts with `/`,\n" " - no consecutive `//`,\n" " - no `?` (query) or `#` (fragment),\n" " - no unencoded whitespace (the OAS path grammar inherits URL path\n" " character rules and excludes space / tab in the literal segment),\n" " - balanced `{` / `}` with each placeholder matching `[A-Za-z0-9_-]+`,\n" " - each placeholder name unique within the path.\n" ). -spec validate_path_template_key( binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. validate_path_template_key(Path, Index) -> Reject = fun(Detail) -> {error, oaspec@openapi@diagnostic:invalid_value( <<"paths"/utf8>>, <<<<<<"Invalid path key '"/utf8, Path/binary>>/binary, "': "/utf8>>/binary, Detail/binary>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<""/utf8>>, <<"paths"/utf8>> ) )} end, gleam@bool:guard( Path =:= <<""/utf8>>, Reject(<<"must not be empty"/utf8>>), fun() -> gleam@bool:guard( not gleam_stdlib:string_starts_with(Path, <<"/"/utf8>>), Reject(<<"must start with '/'"/utf8>>), fun() -> gleam@bool:guard( gleam_stdlib:contains_string(Path, <<"//"/utf8>>), Reject(<<"must not contain '//'"/utf8>>), fun() -> gleam@bool:guard( gleam_stdlib:contains_string(Path, <<"?"/utf8>>), Reject( <<"must not contain a query string"/utf8>> ), fun() -> gleam@bool:guard( gleam_stdlib:contains_string( Path, <<"#"/utf8>> ), Reject( <<"must not contain a URL fragment"/utf8>> ), fun() -> gleam@bool:guard( gleam_stdlib:contains_string( Path, <<" "/utf8>> ), Reject( <<"must not contain spaces"/utf8>> ), fun() -> gleam@bool:guard( gleam_stdlib:contains_string( Path, <<"\t"/utf8>> ), Reject( <<"must not contain tab characters"/utf8>> ), fun() -> validate_path_placeholders( Path, Reject ) end ) end ) end ) end ) end ) end ) end ). -file("src/oaspec/openapi/parser.gleam", 1603). ?DOC( " Reject parameter names the OAS path-template grammar and the\n" " URL query/header word grammar both refuse — empty strings,\n" " strings made entirely of whitespace, strings containing a space\n" " or tab. Lets through the conservative `[A-Za-z0-9_.-]+` set the\n" " HTTP grammars share. (#590)\n" ). -spec validate_parameter_name( binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. validate_parameter_name(Name, Index) -> gleam@bool:guard( Name =:= <<""/utf8>>, {error, oaspec@openapi@diagnostic:invalid_value( <<"parameter"/utf8>>, <<"Parameter name must not be empty."/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"parameter"/utf8>>, <<"name"/utf8>> ) )}, fun() -> gleam@bool:guard( gleam@string:trim(Name) =:= <<""/utf8>>, {error, oaspec@openapi@diagnostic:invalid_value( <<"parameter"/utf8>>, <<"Parameter name must not be whitespace-only."/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"parameter"/utf8>>, <<"name"/utf8>> ) )}, fun() -> gleam@bool:guard( gleam_stdlib:contains_string(Name, <<" "/utf8>>) orelse gleam_stdlib:contains_string( Name, <<"\t"/utf8>> ), {error, oaspec@openapi@diagnostic:invalid_value( <<"parameter"/utf8>>, <<<<"Parameter name must not contain spaces or tabs (got '"/utf8, Name/binary>>/binary, "')."/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"parameter"/utf8>>, <<"name"/utf8>> ) )}, fun() -> {ok, nil} end ) end ) end ). -file("src/oaspec/openapi/parser.gleam", 1660). -spec parameter_in_string(oaspec@internal@openapi@spec:parameter_in()) -> binary(). parameter_in_string(In_) -> case In_ of in_path -> <<"path"/utf8>>; in_query -> <<"query"/utf8>>; in_header -> <<"header"/utf8>>; in_cookie -> <<"cookie"/utf8>> end. -file("src/oaspec/openapi/parser.gleam", 1669). -spec find_first_duplicate(list(binary()), list(binary())) -> gleam@option:option(binary()). find_first_duplicate(Remaining, Seen) -> case Remaining of [] -> none; [Head | Rest] -> case gleam@list:contains(Seen, Head) of true -> {some, Head}; false -> find_first_duplicate(Rest, [Head | Seen]) end end. -file("src/oaspec/openapi/parser.gleam", 743). ?DOC( " Collect every Value(PathItem) operationId across the paths Dict and\n" " reject the first duplicate. Ref entries are skipped because the\n" " resolved operationId is not visible at parse time. (#591)\n" ). -spec validate_unique_operation_ids( gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:path_item(oaspec@internal@openapi@spec:unresolved()))), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. validate_unique_operation_ids(Paths, Index) -> Ids = begin _pipe = maps:values(Paths), gleam@list:flat_map(_pipe, fun(P) -> case P of {value, Pi} -> path_item_operation_ids(Pi); {ref, _} -> [] end end) end, case find_first_duplicate(Ids, []) of none -> {ok, nil}; {some, Dup} -> {error, oaspec@openapi@diagnostic:invalid_value( <<"paths"/utf8>>, <<<<"Duplicate operationId '"/utf8, Dup/binary>>/binary, "'. OAS 3.0 §4.7.10.1 requires operationId to be unique across all operations in the API."/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <<"paths"/utf8>> ) )} end. -file("src/oaspec/openapi/parser.gleam", 1636). -spec check_unique_parameters( list(oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:parameter(oaspec@internal@openapi@spec:unresolved()))), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. check_unique_parameters(Parameters, Index) -> Keys = gleam@list:filter_map(Parameters, fun(P) -> case P of {value, Param} -> {ok, <<<<(parameter_in_string(erlang:element(3, Param)))/binary, "/"/utf8>>/binary, (erlang:element(2, Param))/binary>>}; {ref, _} -> {error, nil} end end), case find_first_duplicate(Keys, []) of none -> {ok, nil}; {some, Dup} -> {error, oaspec@openapi@diagnostic:invalid_value( <<"parameters"/utf8>>, <<<<"Duplicate parameter (name + in): '"/utf8, Dup/binary>>/binary, "'. OAS 3.0 §4.7.10.5 requires the (name, in) pair to be unique within an operation."/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <<"parameters"/utf8>> ) )} end. -file("src/oaspec/openapi/parser.gleam", 1800). ?DOC(" Parse parameter location string.\n"). -spec parse_parameter_in( binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:parameter_in()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_parameter_in(Value, Index) -> case Value of <<"path"/utf8>> -> {ok, in_path}; <<"query"/utf8>> -> {ok, in_query}; <<"header"/utf8>> -> {ok, in_header}; <<"cookie"/utf8>> -> {ok, in_cookie}; _ -> {error, oaspec@openapi@diagnostic:invalid_value( <<"parameter.in"/utf8>>, <<<<"Unknown parameter location: "/utf8, Value/binary>>/binary, ". Must be one of: path, query, header, cookie"/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <<"parameter.in"/utf8>> ) )} end. -file("src/oaspec/openapi/parser.gleam", 1821). ?DOC(" Map a style string to a ParameterStyle ADT value.\n"). -spec parse_parameter_style( binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:parameter_style()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_parameter_style(Value, Index) -> case Value of <<"form"/utf8>> -> {ok, form_style}; <<"simple"/utf8>> -> {ok, simple_style}; <<"deepObject"/utf8>> -> {ok, deep_object_style}; <<"matrix"/utf8>> -> {ok, matrix_style}; <<"label"/utf8>> -> {ok, label_style}; <<"spaceDelimited"/utf8>> -> {ok, space_delimited_style}; <<"pipeDelimited"/utf8>> -> {ok, pipe_delimited_style}; _ -> {error, oaspec@openapi@diagnostic:invalid_value( <<"parameter.style"/utf8>>, <<<<"Unknown parameter style: '"/utf8, Value/binary>>/binary, "'. Must be one of: form, simple, deepObject, matrix, label, spaceDelimited, pipeDelimited"/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <<"parameter.style"/utf8>> ) )} end. -file("src/oaspec/openapi/parser.gleam", 1845). ?DOC(" Map an apiKey \"in\" string to a SecuritySchemeIn ADT value.\n"). -spec parse_security_scheme_in( binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:security_scheme_in()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_security_scheme_in(Value, Index) -> case Value of <<"header"/utf8>> -> {ok, scheme_in_header}; <<"query"/utf8>> -> {ok, scheme_in_query}; <<"cookie"/utf8>> -> {ok, scheme_in_cookie}; _ -> {error, oaspec@openapi@diagnostic:invalid_value( <<"securityScheme.in"/utf8>>, <<<<"Unknown apiKey location: "/utf8, Value/binary>>/binary, ". Must be one of: header, query, cookie"/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <<"securityScheme.in"/utf8>> ) )} end. -file("src/oaspec/openapi/parser.gleam", 2077). ?DOC( " Build the parse-time diagnostic for a response status-code key that\n" " is outside the OAS-allowed grammar (#587). Listed at module scope so\n" " the YAML-string path and the YAML-int path emit the same message.\n" ). -spec invalid_response_status_diagnostic( binary(), oaspec@internal@openapi@location_index:location_index() ) -> oaspec@openapi@diagnostic:diagnostic(). invalid_response_status_diagnostic(Key, Index) -> oaspec@openapi@diagnostic:invalid_value( <<"responses"/utf8>>, <<<<<<"Invalid response status code key '"/utf8, Key/binary>>/binary, "'. OpenAPI accepts 100-599 (canonical 3-digit), the wildcards "/utf8>>/binary, "1XX-5XX, or 'default'."/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<""/utf8>>, <<"responses"/utf8>> ) ). -file("src/oaspec/openapi/parser.gleam", 2132). ?DOC( " Render a `yay` key node as a comparable string for duplicate\n" " detection. Returns `None` for node types that cannot occur as\n" " OpenAPI map keys (lists, maps, etc.); those positions are handled\n" " elsewhere as `invalid_value` errors and do not need duplicate\n" " checking.\n" ). -spec key_node_to_string(yay:node_()) -> gleam@option:option(binary()). key_node_to_string(Node) -> case Node of {node_str, S} -> {some, S}; {node_int, N} -> {some, erlang:integer_to_binary(N)}; {node_bool, true} -> {some, <<"true"/utf8>>}; {node_bool, false} -> {some, <<"false"/utf8>>}; _ -> none end. -file("src/oaspec/openapi/parser.gleam", 2106). ?DOC( " Issue #573: detect duplicate keys in a YAML mapping before consuming\n" " it into a `Dict`. yamerl preserves the original `entries` list with\n" " both occurrences of a duplicate key, but `dict.insert` silently\n" " overwrites — so without this check, duplicates disappear into\n" " undefined behavior (whichever entry happens to be inserted last\n" " wins). Returns the *first* duplicate as a `Diagnostic`; we don't\n" " accumulate every duplicate to keep the parse-phase contract\n" " (one error stops parsing).\n" "\n" " The `index` argument is reserved for a future source-location\n" " lookup (so the diagnostic can name the line of the second\n" " occurrence). For now the location index does not retain per-key\n" " positions inside arbitrary maps, so the diagnostic carries\n" " `NoSourceLoc` and the user-facing message names the path\n" " (`responses`, `components.responses`) instead.\n" ). -spec check_unique_yaml_keys( list({yay:node_(), yay:node_()}), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}. check_unique_yaml_keys(Entries, Path, Index) -> Loc = oaspec@internal@openapi@location_index:lookup(Index, Path), _pipe = gleam@list:try_fold( Entries, [], fun(Seen, Entry) -> {Key_node, _} = Entry, case key_node_to_string(Key_node) of {some, Key} -> case gleam@list:contains(Seen, Key) of true -> {error, oaspec@openapi@diagnostic:duplicate_key( Path, Key, Loc )}; false -> {ok, [Key | Seen]} end; none -> {ok, Seen} end end ), gleam@result:map(_pipe, fun(_) -> nil end). -file("src/oaspec/openapi/parser.gleam", 2268). ?DOC(" Parse the schemas map from components.\n"). -spec parse_schemas_map( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@schema:schema_ref())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_schemas_map(Components_node, Index) -> case yay:select_sugar(Components_node, <<"schemas"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Name} -> gleam@result:'try'( oaspec@internal@openapi@parser_schema:parse_schema_ref( Value_node, <<"components.schemas."/utf8, Name/binary>>, Index ), fun(Schema_ref) -> {ok, gleam@dict:insert(Acc, Name, Schema_ref)} end ); _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 2388). ?DOC( " Parse a schema reference (either $ref or inline schema).\n" " Convert a parse error to a human-readable string.\n" ). -spec parse_error_to_string(oaspec@openapi@diagnostic:diagnostic()) -> binary(). parse_error_to_string(Error) -> oaspec@openapi@diagnostic:to_short_string(Error). -file("src/oaspec/openapi/parser.gleam", 2554). ?DOC(" Parse OAuth2 flows from a security scheme node.\n"). -spec parse_oauth2_flows(yay:node_()) -> gleam@dict:dict(binary(), oaspec@internal@openapi@spec:o_auth2_flow()). parse_oauth2_flows(Node) -> case yay:select_sugar(Node, <<"flows"/utf8>>) of {ok, {node_map, Flow_entries}} -> gleam@list:fold( Flow_entries, maps:new(), fun(Acc, Entry) -> {Key_node, Flow_node} = Entry, case Key_node of {node_str, Flow_name} -> Authorization_url = oaspec@internal@openapi@parser_value:optional_string( Flow_node, <<"authorizationUrl"/utf8>> ), Token_url = oaspec@internal@openapi@parser_value:optional_string( Flow_node, <<"tokenUrl"/utf8>> ), Refresh_url = oaspec@internal@openapi@parser_value:optional_string( Flow_node, <<"refreshUrl"/utf8>> ), Scopes = case yay:select_sugar( Flow_node, <<"scopes"/utf8>> ) of {ok, {node_map, Scope_entries}} -> gleam@list:fold( Scope_entries, maps:new(), fun(Sacc, Sentry) -> {Sk, Sv} = Sentry, case {Sk, Sv} of {{node_str, Scope_name}, {node_str, Scope_desc}} -> gleam@dict:insert( Sacc, Scope_name, Scope_desc ); {_, _} -> Sacc end end ); _ -> maps:new() end, gleam@dict:insert( Acc, Flow_name, {o_auth2_flow, Authorization_url, Token_url, Refresh_url, Scopes} ); _ -> Acc end end ); _ -> maps:new() end. -file("src/oaspec/openapi/parser.gleam", 2457). ?DOC(" Parse a single security scheme.\n"). -spec parse_security_scheme( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:security_scheme()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_security_scheme(Node, Index) -> gleam@result:'try'( begin _pipe = yay:extract_string(Node, <<"type"/utf8>>), gleam@result:map_error( _pipe, fun(_capture) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture, <<"securityScheme"/utf8>>, <<"type"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"securityScheme"/utf8>>, <<"type"/utf8>> ) ) end ) end, fun(Type_str) -> case Type_str of <<"apiKey"/utf8>> -> gleam@result:'try'( begin _pipe@1 = yay:extract_string(Node, <<"name"/utf8>>), gleam@result:map_error( _pipe@1, fun(_capture@1) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture@1, <<"securityScheme.apiKey"/utf8>>, <<"name"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"securityScheme.apiKey"/utf8>>, <<"name"/utf8>> ) ) end ) end, fun(Name) -> gleam@result:'try'( begin _pipe@2 = yay:extract_string( Node, <<"in"/utf8>> ), gleam@result:map_error( _pipe@2, fun(_capture@2) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture@2, <<"securityScheme.apiKey"/utf8>>, <<"in"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"securityScheme.apiKey"/utf8>>, <<"in"/utf8>> ) ) end ) end, fun(In_str) -> case parse_security_scheme_in(In_str, Index) of {ok, In_} -> {ok, {api_key_scheme, Name, In_}}; {error, _} -> {error, oaspec@openapi@diagnostic:invalid_value( <<"securityScheme.apiKey.in"/utf8>>, <<<<"Only 'header', 'query' and 'cookie' are supported for apiKey. Got: '"/utf8, In_str/binary>>/binary, "'"/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <<"securityScheme.apiKey.in"/utf8>> ) )} end end ) end ); <<"http"/utf8>> -> gleam@result:'try'( begin _pipe@3 = yay:extract_string( Node, <<"scheme"/utf8>> ), gleam@result:map_error( _pipe@3, fun(_capture@3) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture@3, <<"securityScheme.http"/utf8>>, <<"scheme"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"securityScheme.http"/utf8>>, <<"scheme"/utf8>> ) ) end ) end, fun(Scheme) -> Bearer_format = oaspec@internal@openapi@parser_value:optional_string( Node, <<"bearerFormat"/utf8>> ), {ok, {http_scheme, Scheme, Bearer_format}} end ); <<"oauth2"/utf8>> -> Description = oaspec@internal@openapi@parser_value:optional_string( Node, <<"description"/utf8>> ), Flows = parse_oauth2_flows(Node), {ok, {o_auth2_scheme, Description, Flows}}; <<"openIdConnect"/utf8>> -> Description@1 = oaspec@internal@openapi@parser_value:optional_string( Node, <<"description"/utf8>> ), gleam@result:'try'( begin _pipe@4 = yay:extract_string( Node, <<"openIdConnectUrl"/utf8>> ), gleam@result:map_error( _pipe@4, fun(_capture@4) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture@4, <<"securityScheme.openIdConnect"/utf8>>, <<"openIdConnectUrl"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"securityScheme.openIdConnect"/utf8>>, <<"openIdConnectUrl"/utf8>> ) ) end ) end, fun(Open_id_connect_url) -> {ok, {open_id_connect_scheme, Open_id_connect_url, Description@1}} end ); _ -> {ok, {unsupported_scheme, Type_str}} end end ). -file("src/oaspec/openapi/parser.gleam", 2395). ?DOC( " Parse security schemes from components.\n" " Returns Ok(empty dict) if the section is absent, Error if present but\n" " malformed.\n" ). -spec parse_security_schemes_map( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:security_scheme()))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_security_schemes_map(Components_node, Index) -> case yay:select_sugar(Components_node, <<"securitySchemes"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Name} -> case yay:extract_optional_string( Value_node, <<"$ref"/utf8>> ) of {ok, {some, Ref_str}} -> {ok, gleam@dict:insert( Acc, Name, {ref, Ref_str} )}; _ -> gleam@result:'try'( parse_security_scheme(Value_node, Index), fun(Scheme) -> {ok, gleam@dict:insert( Acc, Name, {value, Scheme} )} end ) end; _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 2638). ?DOC( " Parse a single security requirement object.\n" " Returns one SecurityRequirement whose schemes list contains all AND-ed\n" " scheme refs. The outer list (caller) represents OR alternatives.\n" ). -spec parse_security_requirement_object( yay:node_(), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:security_requirement()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_security_requirement_object(Node, Context, Index) -> case Node of {node_map, Entries} -> gleam@result:'try'( gleam@list:try_map( Entries, fun(Entry) -> {Key_node, Scopes_node} = Entry, case Key_node of {node_str, Scheme_name} -> gleam@result:'try'(case Scopes_node of {node_seq, Scope_items} -> gleam@list:try_map( Scope_items, fun(S) -> case S of {node_str, V} -> {ok, V}; _ -> {error, oaspec@openapi@diagnostic:invalid_value( <<<>/binary, Scheme_name/binary>>, <<"Scope must be a string"/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <<<>/binary, Scheme_name/binary>> ) )} end end ); node_nil -> {ok, []}; _ -> {error, oaspec@openapi@diagnostic:invalid_value( <<<>/binary, Scheme_name/binary>>, <<"Scopes must be an array of strings"/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <<<>/binary, Scheme_name/binary>> ) )} end, fun(Scopes) -> {ok, {security_scheme_ref, Scheme_name, Scopes}} end); _ -> {error, oaspec@openapi@diagnostic:invalid_value( <>, <<"Security requirement key must be a string"/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <> ) )} end end ), fun(Scheme_refs) -> {ok, {security_requirement, Scheme_refs}} end ); _ -> {error, oaspec@openapi@diagnostic:invalid_value( <>, <<"Security requirement must be an object"/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <> ) )} end. -file("src/oaspec/openapi/parser.gleam", 2600). ?DOC( " Parse top-level security requirements.\n" " Returns Ok([]) if absent, Error if present but malformed.\n" ). -spec parse_security_requirements( yay:node_(), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, list(oaspec@internal@openapi@spec:security_requirement())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_security_requirements(Node, Context, Index) -> case yay:select_sugar(Node, <<"security"/utf8>>) of {ok, {node_seq, Items}} -> gleam@list:try_map( Items, fun(Item) -> parse_security_requirement_object(Item, Context, Index) end ); _ -> {ok, []} end. -file("src/oaspec/openapi/parser.gleam", 2617). ?DOC( " Parse operation-level security requirements.\n" " Returns Ok(None) if absent (inherits top-level), Ok(Some([])) if explicitly\n" " empty (opts out), Ok(Some([...])) if specified.\n" ). -spec parse_optional_security_requirements( yay:node_(), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@option:option(list(oaspec@internal@openapi@spec:security_requirement()))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_optional_security_requirements(Node, Context, Index) -> case yay:select_sugar(Node, <<"security"/utf8>>) of {ok, {node_seq, Items}} -> gleam@result:'try'( gleam@list:try_map( Items, fun(Item) -> parse_security_requirement_object(Item, Context, Index) end ), fun(Reqs) -> {ok, {some, Reqs}} end ); _ -> {ok, none} end. -file("src/oaspec/openapi/parser.gleam", 2812). ?DOC(" Parse optional contact from an info node.\n"). -spec parse_optional_contact(yay:node_()) -> gleam@option:option(oaspec@internal@openapi@spec:contact()). parse_optional_contact(Info_node) -> case yay:select_sugar(Info_node, <<"contact"/utf8>>) of {ok, Contact_node} -> Name = oaspec@internal@openapi@parser_value:optional_string( Contact_node, <<"name"/utf8>> ), Url = oaspec@internal@openapi@parser_value:optional_string( Contact_node, <<"url"/utf8>> ), Email = oaspec@internal@openapi@parser_value:optional_string( Contact_node, <<"email"/utf8>> ), {some, {contact, Name, Url, Email}}; _ -> none end. -file("src/oaspec/openapi/parser.gleam", 2825). ?DOC(" Parse optional license from an info node.\n"). -spec parse_optional_license(yay:node_()) -> gleam@option:option(oaspec@internal@openapi@spec:license()). parse_optional_license(Info_node) -> case yay:select_sugar(Info_node, <<"license"/utf8>>) of {ok, License_node} -> case yay:extract_string(License_node, <<"name"/utf8>>) of {ok, Name} -> Url = oaspec@internal@openapi@parser_value:optional_string( License_node, <<"url"/utf8>> ), {some, {license, Name, Url}}; _ -> none end; _ -> none end. -file("src/oaspec/openapi/parser.gleam", 1050). ?DOC(" Parse the info object.\n"). -spec parse_info( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:info()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_info(Root, Index) -> gleam@result:'try'( begin _pipe = yay:select_sugar(Root, <<"info"/utf8>>), gleam@result:map_error( _pipe, fun(_capture) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_selector( _capture, <<""/utf8>>, <<"info"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<""/utf8>>, <<"info"/utf8>> ) ) end ) end, fun(Info_node) -> gleam@result:'try'( begin _pipe@1 = yay:extract_string(Info_node, <<"title"/utf8>>), gleam@result:map_error( _pipe@1, fun(_capture@1) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture@1, <<"info"/utf8>>, <<"title"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"info"/utf8>>, <<"title"/utf8>> ) ) end ) end, fun(Title) -> gleam@result:'try'( begin _pipe@2 = yay:extract_string( Info_node, <<"version"/utf8>> ), gleam@result:map_error( _pipe@2, fun(_capture@2) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture@2, <<"info"/utf8>>, <<"version"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"info"/utf8>>, <<"version"/utf8>> ) ) end ) end, fun(Version) -> Description = oaspec@internal@openapi@parser_value:optional_string( Info_node, <<"description"/utf8>> ), Summary = oaspec@internal@openapi@parser_value:optional_string( Info_node, <<"summary"/utf8>> ), Terms_of_service = oaspec@internal@openapi@parser_value:optional_string( Info_node, <<"termsOfService"/utf8>> ), Contact = parse_optional_contact(Info_node), License = parse_optional_license(Info_node), {ok, {info, Title, Description, Version, Summary, Terms_of_service, Contact, License}} end ) end ) end ). -file("src/oaspec/openapi/parser.gleam", 2841). ?DOC(" Parse server variables from a server node.\n"). -spec parse_server_variables(yay:node_()) -> gleam@dict:dict(binary(), oaspec@internal@openapi@spec:server_variable()). parse_server_variables(Node) -> case yay:select_sugar(Node, <<"variables"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Var_name} -> Default = oaspec@internal@openapi@parser_value:string_default( Value_node, <<"default"/utf8>>, <<""/utf8>> ), Enum_values = case yay:extract_string_list( Value_node, <<"enum"/utf8>> ) of {ok, Values} -> Values; _ -> [] end, Description = oaspec@internal@openapi@parser_value:optional_string( Value_node, <<"description"/utf8>> ), gleam@dict:insert( Acc, Var_name, {server_variable, Default, Enum_values, Description} ); _ -> Acc end end ); _ -> maps:new() end. -file("src/oaspec/openapi/parser.gleam", 1115). ?DOC(" Parse a single server object.\n"). -spec parse_server( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:server()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_server(Node, Index) -> gleam@result:'try'( begin _pipe = yay:extract_string(Node, <<"url"/utf8>>), gleam@result:map_error( _pipe, fun(_capture) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture, <<"servers"/utf8>>, <<"url"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"servers"/utf8>>, <<"url"/utf8>> ) ) end ) end, fun(Url) -> Description = oaspec@internal@openapi@parser_value:optional_string( Node, <<"description"/utf8>> ), Variables = parse_server_variables(Node), {ok, {server, Url, Description, Variables}} end ). -file("src/oaspec/openapi/parser.gleam", 1103). ?DOC(" Parse servers array.\n"). -spec parse_servers( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, list(oaspec@internal@openapi@spec:server())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_servers(Root, Index) -> case yay:select_sugar(Root, <<"servers"/utf8>>) of {ok, {node_seq, Items}} -> gleam@list:try_map( Items, fun(Item) -> parse_server(Item, Index) end ); _ -> {ok, []} end. -file("src/oaspec/openapi/parser.gleam", 2869). ?DOC(" Parse optional external docs from a node.\n"). -spec parse_optional_external_docs(yay:node_()) -> gleam@option:option(oaspec@internal@openapi@spec:external_doc()). parse_optional_external_docs(Node) -> case yay:select_sugar(Node, <<"externalDocs"/utf8>>) of {ok, Doc_node} -> case yay:extract_string(Doc_node, <<"url"/utf8>>) of {ok, Url} -> Description = oaspec@internal@openapi@parser_value:optional_string( Doc_node, <<"description"/utf8>> ), {some, {external_doc, Url, Description}}; _ -> none end; _ -> none end. -file("src/oaspec/openapi/parser.gleam", 2886). ?DOC(" Parse tags array from root.\n"). -spec parse_tags(yay:node_()) -> list(oaspec@internal@openapi@spec:tag()). parse_tags(Node) -> case yay:select_sugar(Node, <<"tags"/utf8>>) of {ok, {node_seq, Items}} -> gleam@list:filter_map( Items, fun(Tag_node) -> case yay:extract_string(Tag_node, <<"name"/utf8>>) of {ok, Name} -> Description = oaspec@internal@openapi@parser_value:optional_string( Tag_node, <<"description"/utf8>> ), External_docs = parse_optional_external_docs( Tag_node ), {ok, {tag, Name, Description, External_docs}}; _ -> {error, nil} end end ); _ -> [] end. -file("src/oaspec/openapi/parser.gleam", 2985). ?DOC(" Parse encoding map from a media type node.\n"). -spec parse_encoding_map( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:encoding())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_encoding_map(Node, Index) -> case yay:select_sugar(Node, <<"encoding"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Prop_name} -> Content_type = oaspec@internal@openapi@parser_value:optional_string( Value_node, <<"contentType"/utf8>> ), gleam@result:'try'( case oaspec@internal@openapi@parser_value:optional_string( Value_node, <<"style"/utf8>> ) of {some, S} -> gleam@result:'try'( parse_parameter_style(S, Index), fun(Parsed) -> {ok, {some, Parsed}} end ); none -> {ok, none} end, fun(Style) -> Explode = oaspec@internal@openapi@parser_value:optional_bool( Value_node, <<"explode"/utf8>> ), {ok, gleam@dict:insert( Acc, Prop_name, {encoding, Content_type, Style, Explode} )} end ); _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 1908). ?DOC(" Parse content map, requiring at least one entry for request bodies.\n"). -spec parse_required_content( yay:node_(), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:media_type())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_required_content(Node, Context, Index) -> case yay:select_sugar(Node, <<"content"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Media_type_name} -> gleam@result:'try'( case yay:select_sugar( Value_node, <<"schema"/utf8>> ) of {ok, Schema_node} -> gleam@result:'try'( oaspec@internal@openapi@parser_schema:parse_schema_ref( Schema_node, <>, Index ), fun(Sr) -> {ok, {some, Sr}} end ); _ -> {ok, none} end, fun(Mt_schema) -> Mt_example = oaspec@internal@openapi@parser_value:extract_optional( Value_node, <<"example"/utf8>> ), Mt_examples = oaspec@internal@openapi@parser_value:extract_map( Value_node, <<"examples"/utf8>> ), gleam@result:'try'( parse_encoding_map(Value_node, Index), fun(Mt_encoding) -> {ok, gleam@dict:insert( Acc, Media_type_name, {media_type, Mt_schema, Mt_example, Mt_examples, Mt_encoding} )} end ) end ); _ -> {ok, Acc} end end ); _ -> {error, oaspec@openapi@diagnostic:missing_field( <>, <<"content"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <>, <<"content"/utf8>> ) )} end. -file("src/oaspec/openapi/parser.gleam", 1892). ?DOC(" Parse a request body object.\n"). -spec parse_request_body( yay:node_(), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:request_body(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_request_body(Node, Context, Index) -> Description = oaspec@internal@openapi@parser_value:optional_string( Node, <<"description"/utf8>> ), Required = oaspec@internal@openapi@parser_value:bool_default( Node, <<"required"/utf8>>, false ), gleam@result:'try'( parse_required_content(Node, Context, Index), fun(Content) -> {ok, {request_body, Description, Content, Required}} end ). -file("src/oaspec/openapi/parser.gleam", 1865). ?DOC(" Parse requestBody from a node.\n"). -spec parse_request_body_at( yay:node_(), binary(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:request_body(oaspec@internal@openapi@spec:unresolved()))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_request_body_at(Node, Context, _, Index) -> gleam@result:'try'( begin _pipe = yay:select_sugar(Node, <<"requestBody"/utf8>>), gleam@result:map_error( _pipe, fun(_capture) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_selector( _capture, Context, <<"requestBody"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, Context, <<"requestBody"/utf8>> ) ) end ) end, fun(Rb_node) -> case yay:extract_optional_string(Rb_node, <<"$ref"/utf8>>) of {ok, {some, Ref_str}} -> {ok, {ref, Ref_str}}; _ -> gleam@result:'try'( parse_request_body(Rb_node, Context, Index), fun(Rb) -> {ok, {value, Rb}} end ) end end ). -file("src/oaspec/openapi/parser.gleam", 1545). ?DOC( " Parse optional requestBody: Ok(None) if absent, Ok(Some(..)) if valid,\n" " Error if present but malformed.\n" ). -spec parse_optional_request_body( yay:node_(), binary(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@option:option(oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:request_body(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_optional_request_body(Node, Context, Components, Index) -> Present = gleam@result:is_ok(yay:select_sugar(Node, <<"requestBody"/utf8>>)), gleam@bool:guard( not Present, {ok, none}, fun() -> gleam@result:'try'( parse_request_body_at(Node, Context, Components, Index), fun(Rb) -> {ok, {some, Rb}} end ) end ). -file("src/oaspec/openapi/parser.gleam", 1965). ?DOC(" Parse content map (media type -> schema).\n"). -spec parse_content( yay:node_(), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:media_type())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_content(Node, Context, Index) -> case yay:select_sugar(Node, <<"content"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Media_type_name} -> gleam@result:'try'( case yay:select_sugar( Value_node, <<"schema"/utf8>> ) of {ok, Schema_node} -> gleam@result:'try'( oaspec@internal@openapi@parser_schema:parse_schema_ref( Schema_node, <>, Index ), fun(Sr) -> {ok, {some, Sr}} end ); _ -> {ok, none} end, fun(Mt_schema) -> Mt_example = oaspec@internal@openapi@parser_value:extract_optional( Value_node, <<"example"/utf8>> ), Mt_examples = oaspec@internal@openapi@parser_value:extract_map( Value_node, <<"examples"/utf8>> ), gleam@result:'try'( parse_encoding_map(Value_node, Index), fun(Mt_encoding) -> {ok, gleam@dict:insert( Acc, Media_type_name, {media_type, Mt_schema, Mt_example, Mt_examples, Mt_encoding} )} end ) end ); _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 2321). ?DOC(" Parse the requestBodies map from components.\n"). -spec parse_request_bodies_map( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:request_body(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_request_bodies_map(Components_node, Index) -> case yay:select_sugar(Components_node, <<"requestBodies"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Name} -> case yay:extract_optional_string( Value_node, <<"$ref"/utf8>> ) of {ok, {some, Ref_str}} -> {ok, gleam@dict:insert( Acc, Name, {ref, Ref_str} )}; _ -> gleam@result:'try'( parse_request_body( Value_node, <<"components.requestBodies."/utf8, Name/binary>>, Index ), fun(Rb) -> {ok, gleam@dict:insert( Acc, Name, {value, Rb} )} end ) end; _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 2939). ?DOC(" Parse a content map (non-result version for use in Parameter).\n"). -spec parse_content_map( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:media_type())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_content_map(Node, Index) -> case yay:select_sugar(Node, <<"content"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Media_type_name} -> gleam@result:'try'( case yay:select_sugar( Value_node, <<"schema"/utf8>> ) of {ok, Schema_node} -> gleam@result:'try'( oaspec@internal@openapi@parser_schema:parse_schema_ref( Schema_node, <<"content.schema"/utf8>>, Index ), fun(Sr) -> {ok, {some, Sr}} end ); _ -> {ok, none} end, fun(Mt_schema) -> Mt_example = oaspec@internal@openapi@parser_value:extract_optional( Value_node, <<"example"/utf8>> ), Mt_examples = oaspec@internal@openapi@parser_value:extract_map( Value_node, <<"examples"/utf8>> ), gleam@result:'try'( parse_encoding_map(Value_node, Index), fun(Mt_encoding) -> {ok, gleam@dict:insert( Acc, Media_type_name, {media_type, Mt_schema, Mt_example, Mt_examples, Mt_encoding} )} end ) end ); _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 1684). ?DOC(" Parse a single parameter.\n"). -spec parse_parameter( yay:node_(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:parameter(oaspec@internal@openapi@spec:unresolved()))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_parameter(Node, _, Index) -> case yay:extract_optional_string(Node, <<"$ref"/utf8>>) of {ok, {some, Ref_str}} -> {ok, {ref, Ref_str}}; _ -> gleam@result:'try'( begin _pipe = yay:extract_string(Node, <<"name"/utf8>>), gleam@result:map_error( _pipe, fun(_capture) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture, <<"parameter"/utf8>>, <<"name"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"parameter"/utf8>>, <<"name"/utf8>> ) ) end ) end, fun(Name) -> gleam@result:'try'( validate_parameter_name(Name, Index), fun(_) -> gleam@result:'try'( begin _pipe@1 = yay:extract_string( Node, <<"in"/utf8>> ), gleam@result:map_error( _pipe@1, fun(_capture@1) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture@1, <<"parameter."/utf8, Name/binary>>, <<"in"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"parameter."/utf8, Name/binary>>, <<"in"/utf8>> ) ) end ) end, fun(In_str) -> gleam@result:'try'( parse_parameter_in(In_str, Index), fun(In_) -> Description = oaspec@internal@openapi@parser_value:optional_string( Node, <<"description"/utf8>> ), Explicit_required = oaspec@internal@openapi@parser_value:optional_bool( Node, <<"required"/utf8>> ), gleam@result:'try'( case {In_, Explicit_required} of {in_path, {some, true}} -> {ok, true}; {in_path, _} -> {error, oaspec@openapi@diagnostic:invalid_value( <<"parameter."/utf8, Name/binary>>, <<"Path parameters must have required: true"/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <<"parameter."/utf8, Name/binary>> ) )}; {_, {some, V}} -> {ok, V}; {_, none} -> {ok, false} end, fun(Required) -> Deprecated = oaspec@internal@openapi@parser_value:bool_default( Node, <<"deprecated"/utf8>>, false ), gleam@result:'try'( case yay:select_sugar( Node, <<"schema"/utf8>> ) of {ok, Schema_node} -> gleam@result:'try'( oaspec@internal@openapi@parser_schema:parse_schema_ref( Schema_node, <<"parameter.schema"/utf8>>, Index ), fun(Sr) -> {ok, {ok, Sr}} end ); _ -> {ok, {error, nil}} end, fun(Param_schema) -> gleam@result:'try'( case oaspec@internal@openapi@parser_value:optional_string( Node, <<"style"/utf8>> ) of {some, S} -> gleam@result:'try'( parse_parameter_style( S, Index ), fun( Parsed ) -> {ok, {some, Parsed}} end ); none -> {ok, none} end, fun(Style) -> Explode = oaspec@internal@openapi@parser_value:optional_bool( Node, <<"explode"/utf8>> ), Allow_reserved = oaspec@internal@openapi@parser_value:bool_default( Node, <<"allowReserved"/utf8>>, false ), gleam@result:'try'( parse_content_map( Node, Index ), fun( Content ) -> Examples = oaspec@internal@openapi@parser_value:extract_map( Node, <<"examples"/utf8>> ), Payload = case Param_schema of {ok, Sr@1} -> {parameter_schema, Sr@1}; {error, _} -> {parameter_content, Content} end, {ok, {value, {parameter, Name, In_, Description, Required, Payload, Style, Explode, Deprecated, Allow_reserved, Examples}}} end ) end ) end ) end ) end ) end ) end ) end ) end. -file("src/oaspec/openapi/parser.gleam", 1574). ?DOC(" Parse parameters list from a node.\n"). -spec parse_parameters_list( yay:node_(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, list(oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:parameter(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_parameters_list(Node, Components, Index) -> case yay:select_sugar(Node, <<"parameters"/utf8>>) of {ok, {node_seq, Items}} -> gleam@result:'try'( gleam@list:try_map( Items, fun(Item) -> parse_parameter(Item, Components, Index) end ), fun(Parsed) -> gleam@result:'try'( check_unique_parameters(Parsed, Index), fun(_) -> {ok, Parsed} end ) end ); _ -> {ok, []} end. -file("src/oaspec/openapi/parser.gleam", 2294). ?DOC(" Parse the parameters map from components.\n"). -spec parse_parameters_map( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:parameter(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_parameters_map(Components_node, Index) -> case yay:select_sugar(Components_node, <<"parameters"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Name} -> case yay:extract_optional_string( Value_node, <<"$ref"/utf8>> ) of {ok, {some, Ref_str}} -> {ok, gleam@dict:insert( Acc, Name, {ref, Ref_str} )}; _ -> gleam@result:'try'( parse_parameter(Value_node, none, Index), fun(Param) -> {ok, gleam@dict:insert( Acc, Name, Param )} end ) end; _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 3021). ?DOC(" Parse headers map from a node.\n"). -spec parse_headers_map( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:header())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_headers_map(Node, Index) -> case yay:select_sugar(Node, <<"headers"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Header_name} -> Description = oaspec@internal@openapi@parser_value:optional_string( Value_node, <<"description"/utf8>> ), Required = oaspec@internal@openapi@parser_value:bool_default( Value_node, <<"required"/utf8>>, false ), gleam@result:'try'( case yay:select_sugar( Value_node, <<"schema"/utf8>> ) of {ok, Schema_node} -> gleam@result:'try'( oaspec@internal@openapi@parser_schema:parse_schema_ref( Schema_node, <<<<"header."/utf8, Header_name/binary>>/binary, ".schema"/utf8>>, Index ), fun(Sr) -> {ok, {some, Sr}} end ); _ -> {ok, none} end, fun(Hdr_schema) -> {ok, gleam@dict:insert( Acc, Header_name, {header, Description, Required, Hdr_schema} )} end ); _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 3062). ?DOC(" Parse links map from a node.\n"). -spec parse_links_map(yay:node_()) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:link())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_links_map(Node) -> case yay:select_sugar(Node, <<"links"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Link_name} -> Operation_id = oaspec@internal@openapi@parser_value:optional_string( Value_node, <<"operationId"/utf8>> ), Description = oaspec@internal@openapi@parser_value:optional_string( Value_node, <<"description"/utf8>> ), {ok, gleam@dict:insert( Acc, Link_name, {link, Operation_id, Description} )}; _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 2143). ?DOC(" Parse a single response object.\n"). -spec parse_response( yay:node_(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:response(oaspec@internal@openapi@spec:unresolved()))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_response(Node, _, Index) -> case yay:extract_optional_string(Node, <<"$ref"/utf8>>) of {ok, {some, Ref_str}} -> {ok, {ref, Ref_str}}; _ -> gleam@result:'try'( begin _pipe = yay:extract_string(Node, <<"description"/utf8>>), _pipe@1 = gleam@result:map( _pipe, fun(Field@0) -> {some, Field@0} end ), gleam@result:map_error( _pipe@1, fun(_capture) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction( _capture, <<"response"/utf8>>, <<"description"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"response"/utf8>>, <<"description"/utf8>> ) ) end ) end, fun(Description) -> gleam@result:'try'( parse_content(Node, <<"response"/utf8>>, Index), fun(Content) -> gleam@result:'try'( parse_headers_map(Node, Index), fun(Headers) -> gleam@result:'try'( parse_links_map(Node), fun(Links) -> {ok, {value, {response, Description, Content, Headers, Links}}} end ) end ) end ) end ) end. -file("src/oaspec/openapi/parser.gleam", 2013). ?DOC(" Parse responses object.\n"). -spec parse_responses( yay:node_(), binary(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(oaspec@internal@util@http:http_status_code(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:response(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_responses(Node, _, Components, Index) -> case yay:select_sugar(Node, <<"responses"/utf8>>) of {ok, {node_map, Entries}} -> gleam@result:'try'( check_unique_yaml_keys(Entries, <<"responses"/utf8>>, Index), fun(_) -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Status_code} -> case gleam_stdlib:string_starts_with( Status_code, <<"x-"/utf8>> ) of true -> {ok, Acc}; false -> case oaspec@internal@util@http:parse_status_code( Status_code ) of {ok, Code} -> gleam@result:'try'( parse_response( Value_node, Components, Index ), fun(Resp) -> {ok, gleam@dict:insert( Acc, Code, Resp )} end ); {error, _} -> {error, invalid_response_status_diagnostic( Status_code, Index )} end end; {node_int, Code@1} -> case oaspec@internal@util@http:http_status_from_int( Code@1 ) of {ok, Status} -> gleam@result:'try'( parse_response( Value_node, Components, Index ), fun(Resp@1) -> {ok, gleam@dict:insert( Acc, Status, Resp@1 )} end ); {error, _} -> {error, invalid_response_status_diagnostic( erlang:integer_to_binary( Code@1 ), Index )} end; _ -> {ok, Acc} end end ) end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 1561). ?DOC( " Parse responses, treating the field as optional.\n" " OpenAPI 3.x requires responses on operations, but webhook operations\n" " in the wild often omit them. Validation can catch missing responses.\n" ). -spec parse_responses_required( yay:node_(), binary(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(oaspec@internal@util@http:http_status_code(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:response(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_responses_required(Node, Context, Components, Index) -> parse_responses(Node, Context, Components, Index). -file("src/oaspec/openapi/parser.gleam", 2352). ?DOC(" Parse the responses map from components.\n"). -spec parse_responses_map( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:response(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_responses_map(Components_node, Index) -> case yay:select_sugar(Components_node, <<"responses"/utf8>>) of {ok, {node_map, Entries}} -> gleam@result:'try'( check_unique_yaml_keys( Entries, <<"components.responses"/utf8>>, Index ), fun(_) -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Name} -> case yay:extract_optional_string( Value_node, <<"$ref"/utf8>> ) of {ok, {some, Ref_str}} -> {ok, gleam@dict:insert( Acc, Name, {ref, Ref_str} )}; _ -> gleam@result:'try'( parse_response( Value_node, none, Index ), fun(Resp) -> {ok, gleam@dict:insert( Acc, Name, Resp )} end ) end; _ -> {ok, Acc} end end ) end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 2753). ?DOC( " Parse a single callback object (maps URL expressions -> PathItems).\n" " Propagates parse errors from individual URL expression path items.\n" ). -spec parse_callback_object( yay:node_(), binary(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:callback(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_callback_object(Node, Context, Components, Index) -> case Node of {node_map, Entries} -> gleam@result:'try'( gleam@list:try_fold( Entries, [], fun(Acc, Entry) -> {Key_node, Path_item_node} = Entry, case Key_node of {node_str, Url_expression} -> case oaspec@internal@openapi@parser_value:optional_string( Path_item_node, <<"$ref"/utf8>> ) of {some, Ref_str} -> {ok, [{Url_expression, {ref, Ref_str}} | Acc]}; none -> gleam@result:'try'( parse_path_item( Path_item_node, <<<>/binary, Url_expression/binary>>, Components, Index ), fun(Path_item) -> {ok, [{Url_expression, {value, Path_item}} | Acc]} end ) end; _ -> {ok, Acc} end end ), fun(Parsed_entries) -> case Parsed_entries of [] -> {error, oaspec@openapi@diagnostic:missing_field( <>, <<"url expression"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <>, <<"url expression"/utf8>> ) )}; _ -> {ok, {callback, maps:from_list(Parsed_entries)}} end end ); _ -> {error, oaspec@openapi@diagnostic:missing_field( <>, <<"url expression"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <>, <<"url expression"/utf8>> ) )} end. -file("src/oaspec/openapi/parser.gleam", 1350). ?DOC(" Parse a single path item.\n"). -spec parse_path_item( yay:node_(), binary(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:path_item(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_path_item(Node, Path, Components, Index) -> Summary = oaspec@internal@openapi@parser_value:optional_string( Node, <<"summary"/utf8>> ), Description = oaspec@internal@openapi@parser_value:optional_string( Node, <<"description"/utf8>> ), gleam@result:'try'( parse_optional_operation(Node, <<"get"/utf8>>, Path, Components, Index), fun(Get) -> gleam@result:'try'( parse_optional_operation( Node, <<"post"/utf8>>, Path, Components, Index ), fun(Post) -> gleam@result:'try'( parse_optional_operation( Node, <<"put"/utf8>>, Path, Components, Index ), fun(Put) -> gleam@result:'try'( parse_optional_operation( Node, <<"delete"/utf8>>, Path, Components, Index ), fun(Delete) -> gleam@result:'try'( parse_optional_operation( Node, <<"patch"/utf8>>, Path, Components, Index ), fun(Patch) -> gleam@result:'try'( parse_optional_operation( Node, <<"head"/utf8>>, Path, Components, Index ), fun(Head) -> gleam@result:'try'( parse_optional_operation( Node, <<"options"/utf8>>, Path, Components, Index ), fun(Options) -> gleam@result:'try'( parse_optional_operation( Node, <<"trace"/utf8>>, Path, Components, Index ), fun(Trace) -> gleam@result:'try'( parse_parameters_list( Node, Components, Index ), fun( Parameters ) -> gleam@result:'try'( parse_servers( Node, Index ), fun( Servers ) -> {ok, {path_item, Summary, Description, Get, Post, Put, Delete, Patch, Head, Options, Trace, Parameters, Servers}} end ) end ) end ) end ) end ) end ) end ) end ) end ) end ). -file("src/oaspec/openapi/parser.gleam", 1439). ?DOC( " Parse an optional operation: Ok(None) if key absent, Ok(Some(..)) if valid,\n" " Error if present but malformed.\n" ). -spec parse_optional_operation( yay:node_(), binary(), binary(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@option:option(oaspec@internal@openapi@spec:operation(oaspec@internal@openapi@spec:unresolved()))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_optional_operation(Node, Method_key, Path, Components, Index) -> Present = gleam@result:is_ok(yay:select_sugar(Node, Method_key)), gleam@bool:guard( not Present, {ok, none}, fun() -> gleam@result:'try'( parse_operation_at(Node, Method_key, Path, Components, Index), fun(Op) -> {ok, {some, Op}} end ) end ). -file("src/oaspec/openapi/parser.gleam", 1459). ?DOC(" Parse an operation at a specific HTTP method key.\n"). -spec parse_operation_at( yay:node_(), binary(), binary(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:operation(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_operation_at(Node, Method_key, Path, Components, Index) -> gleam@result:'try'( begin _pipe = yay:select_sugar(Node, Method_key), gleam@result:map_error( _pipe, fun(_capture) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_selector( _capture, Path, Method_key, oaspec@internal@openapi@location_index:lookup_field( Index, Path, Method_key ) ) end ) end, fun(Op_node) -> parse_operation( Op_node, <<<>/binary, Method_key/binary>>, Components, Index ) end ). -file("src/oaspec/openapi/parser.gleam", 1480). ?DOC(" Parse a single operation.\n"). -spec parse_operation( yay:node_(), binary(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:operation(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_operation(Node, Context, Components, Index) -> Operation_id = oaspec@internal@openapi@parser_value:optional_string( Node, <<"operationId"/utf8>> ), Summary = oaspec@internal@openapi@parser_value:optional_string( Node, <<"summary"/utf8>> ), Description = oaspec@internal@openapi@parser_value:optional_string( Node, <<"description"/utf8>> ), Tags = case yay:extract_string_list(Node, <<"tags"/utf8>>) of {ok, T} -> T; _ -> [] end, Deprecated = oaspec@internal@openapi@parser_value:bool_default( Node, <<"deprecated"/utf8>>, false ), gleam@result:'try'( parse_parameters_list(Node, Components, Index), fun(Parameters) -> gleam@result:'try'( parse_optional_request_body(Node, Context, Components, Index), fun(Request_body) -> gleam@result:'try'( parse_responses_required( Node, Context, Components, Index ), fun(Responses) -> gleam@result:'try'( parse_optional_security_requirements( Node, Context, Index ), fun(Security) -> gleam@result:'try'( parse_callbacks( Node, Context, Components, Index ), fun(Callbacks) -> gleam@result:'try'( parse_servers(Node, Index), fun(Op_servers) -> External_docs = parse_optional_external_docs( Node ), {ok, {operation, Operation_id, Summary, Description, Tags, Parameters, Request_body, Responses, Deprecated, Security, Callbacks, Op_servers, External_docs}} end ) end ) end ) end ) end ) end ). -file("src/oaspec/openapi/parser.gleam", 2705). ?DOC( " Parse callbacks from an operation node.\n" " Returns an empty dict if no callbacks are present.\n" " Each entry is a `RefOr(Callback)`: a top-level `$ref` pointing at\n" " `#/components/callbacks/foo` is preserved as-is (so the reusable\n" " callback reference survives into the resolved AST), while inline\n" " definitions are parsed into their URL-expression→PathItem shape.\n" ). -spec parse_callbacks( yay:node_(), binary(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:callback(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_callbacks(Node, Context, Components, Index) -> case yay:select_sugar(Node, <<"callbacks"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Callback_name} -> case yay:extract_optional_string( Value_node, <<"$ref"/utf8>> ) of {ok, {some, Ref_str}} -> {ok, gleam@dict:insert( Acc, Callback_name, {ref, Ref_str} )}; {ok, none} -> gleam@result:'try'( parse_callback_object( Value_node, Context, Components, Index ), fun(Callback) -> {ok, gleam@dict:insert( Acc, Callback_name, {value, Callback} )} end ); {error, _} -> {error, oaspec@openapi@diagnostic:invalid_value( <<<<<>/binary, Callback_name/binary>>/binary, ".$ref"/utf8>>, <<"`$ref` under a callback entry must be a string pointing at '#/components/callbacks/...'."/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<<>/binary, Callback_name/binary>>, <<"$ref"/utf8>> ) )} end; _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 1137). ?DOC(" Parse the paths object.\n"). -spec parse_paths( yay:node_(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:path_item(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_paths(Root, Components, Index) -> case yay:select_sugar(Root, <<"paths"/utf8>>) of {ok, {node_map, Entries}} -> gleam@result:'try'( check_unique_yaml_keys(Entries, <<"paths"/utf8>>, Index), fun(_) -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Path} -> case gleam_stdlib:string_starts_with( Path, <<"x-"/utf8>> ) of true -> {ok, Acc}; false -> gleam@result:'try'( validate_path_template_key( Path, Index ), fun(_) -> gleam@result:'try'( case oaspec@internal@openapi@parser_value:optional_string( Value_node, <<"$ref"/utf8>> ) of {some, Ref_str} -> {ok, {ref, Ref_str}}; none -> gleam@result:'try'( parse_path_item( Value_node, Path, Components, Index ), fun(Pi) -> {ok, {value, Pi}} end ) end, fun(Ref_or_path_item) -> {ok, gleam@dict:insert( Acc, Path, Ref_or_path_item )} end ) end ) end; _ -> {ok, Acc} end end ) end ); {ok, Other} -> {error, oaspec@openapi@diagnostic:invalid_value( <<"paths"/utf8>>, <<"paths must be a Paths Object (map), got "/utf8, (node_kind_name(Other))/binary>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<""/utf8>>, <<"paths"/utf8>> ) )}; _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 2226). ?DOC( " Parse `components.callbacks`: each entry is a named reusable callback\n" " object. Returns an empty dict if the section is missing. `$ref` values\n" " are preserved so chains like `components.callbacks.foo.$ref: ...`\n" " stay as references.\n" ). -spec parse_components_callbacks_map( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:callback(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_components_callbacks_map(Components_node, Index) -> case yay:select_sugar(Components_node, <<"callbacks"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Name} -> case yay:extract_optional_string( Value_node, <<"$ref"/utf8>> ) of {ok, {some, Ref_str}} -> {ok, gleam@dict:insert( Acc, Name, {ref, Ref_str} )}; {ok, none} -> gleam@result:'try'( parse_callback_object( Value_node, <<"components.callbacks."/utf8, Name/binary>>, none, Index ), fun(Callback) -> {ok, gleam@dict:insert( Acc, Name, {value, Callback} )} end ); {error, _} -> {error, oaspec@openapi@diagnostic:invalid_value( <<<<"components.callbacks."/utf8, Name/binary>>/binary, ".$ref"/utf8>>, <<"`$ref` under a reusable callback must be a string pointing at '#/components/callbacks/...'."/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"components.callbacks."/utf8, Name/binary>>, <<"$ref"/utf8>> ) )} end; _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 2425). ?DOC(" Parse the pathItems map from components.\n"). -spec parse_path_items_map( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:path_item(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_path_items_map(Components_node, Index) -> case yay:select_sugar(Components_node, <<"pathItems"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Name} -> case yay:extract_optional_string( Value_node, <<"$ref"/utf8>> ) of {ok, {some, Ref_str}} -> {ok, gleam@dict:insert( Acc, Name, {ref, Ref_str} )}; _ -> gleam@result:'try'( parse_path_item( Value_node, <<"components.pathItems."/utf8, Name/binary>>, none, Index ), fun(Path_item) -> {ok, gleam@dict:insert( Acc, Name, {value, Path_item} )} end ) end; _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 2174). ?DOC(" Parse the components section.\n"). -spec parse_components( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_components(Root, Index) -> gleam@result:'try'( begin _pipe = yay:select_sugar(Root, <<"components"/utf8>>), gleam@result:map_error( _pipe, fun(_capture) -> oaspec@internal@openapi@parser_yay_error:missing_field_from_selector( _capture, <<""/utf8>>, <<"components"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<""/utf8>>, <<"components"/utf8>> ) ) end ) end, fun(Components_node) -> gleam@result:'try'( parse_schemas_map(Components_node, Index), fun(Schemas) -> gleam@result:'try'( parse_parameters_map(Components_node, Index), fun(Parameters) -> gleam@result:'try'( parse_request_bodies_map(Components_node, Index), fun(Request_bodies) -> gleam@result:'try'( parse_responses_map( Components_node, Index ), fun(Responses) -> gleam@result:'try'( parse_security_schemes_map( Components_node, Index ), fun(Security_schemes) -> gleam@result:'try'( parse_path_items_map( Components_node, Index ), fun(Path_items) -> gleam@result:'try'( parse_headers_map( Components_node, Index ), fun(Headers) -> Examples = oaspec@internal@openapi@parser_value:extract_map( Components_node, <<"examples"/utf8>> ), gleam@result:'try'( parse_links_map( Components_node ), fun( Links ) -> gleam@result:'try'( parse_components_callbacks_map( Components_node, Index ), fun( Callbacks ) -> {ok, {components, Schemas, Parameters, Request_bodies, Responses, Security_schemes, Path_items, Headers, Examples, Links, Callbacks}} end ) end ) end ) end ) end ) end ) end ) end ) end ) end ). -file("src/oaspec/openapi/parser.gleam", 1038). ?DOC( " Parse optional components section.\n" " Returns Ok(None) if not present, Ok(Some(..)) if valid, Error if malformed.\n" ). -spec parse_optional_components( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved()))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_optional_components(Root, Index) -> Present = gleam@result:is_ok(yay:select_sugar(Root, <<"components"/utf8>>)), gleam@bool:guard( not Present, {ok, none}, fun() -> gleam@result:'try'( parse_components(Root, Index), fun(Comps) -> {ok, {some, Comps}} end ) end ). -file("src/oaspec/openapi/parser.gleam", 2905). ?DOC(" Parse webhooks from root.\n"). -spec parse_webhooks( yay:node_(), gleam@option:option(oaspec@internal@openapi@spec:components(oaspec@internal@openapi@spec:unresolved())), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@spec:ref_or(oaspec@internal@openapi@spec:path_item(oaspec@internal@openapi@spec:unresolved())))} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_webhooks(Node, Components, Index) -> case yay:select_sugar(Node, <<"webhooks"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Name} -> case oaspec@internal@openapi@parser_value:optional_string( Value_node, <<"$ref"/utf8>> ) of {some, Ref_str} -> {ok, gleam@dict:insert( Acc, Name, {ref, Ref_str} )}; none -> gleam@result:'try'( parse_path_item( Value_node, <<"webhooks."/utf8, Name/binary>>, Components, Index ), fun(Path_item) -> {ok, gleam@dict:insert( Acc, Name, {value, Path_item} )} end ) end; _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/openapi/parser.gleam", 492). ?DOC(" Parse the root OpenAPI object.\n"). -spec parse_root( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@spec:open_api_spec(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_root(Node, Index) -> gleam@result:'try'( extract_openapi_field(Node, Index), fun(Openapi) -> gleam@result:'try'( validate_openapi_version( Openapi, oaspec@internal@openapi@location_index:lookup_field( Index, <<""/utf8>>, <<"openapi"/utf8>> ) ), fun(_) -> gleam@result:'try'( parse_info(Node, Index), fun(Info) -> gleam@result:'try'(case is_openapi_3_0(Openapi) of true -> require_paths_present(Node, Index); false -> {ok, nil} end, fun(_) -> gleam@result:'try'( parse_optional_components(Node, Index), fun(Components) -> gleam@result:'try'( case Components of {some, C} -> validate_component_keys( C, Index ); none -> {ok, nil} end, fun(_) -> gleam@result:'try'( parse_paths( Node, Components, Index ), fun(Paths) -> gleam@result:'try'( validate_unique_operation_ids( Paths, Index ), fun(_) -> gleam@result:'try'( validate_path_template_params( Paths, Index ), fun(_) -> gleam@result:'try'( validate_unique_path_templates( Paths, Index ), fun( _ ) -> gleam@result:'try'( parse_servers( Node, Index ), fun( Servers ) -> gleam@result:'try'( parse_security_requirements( Node, <<""/utf8>>, Index ), fun( Security ) -> gleam@result:'try'( parse_webhooks( Node, Components, Index ), fun( Webhooks ) -> Tags = parse_tags( Node ), External_docs = parse_optional_external_docs( Node ), Json_schema_dialect = oaspec@internal@openapi@parser_value:optional_string( Node, <<"jsonSchemaDialect"/utf8>> ), {ok, {open_api_spec, Openapi, Info, Paths, Components, Servers, Security, Webhooks, Tags, External_docs, Json_schema_dialect}} end ) end ) end ) end ) end ) end ) end ) end ) end ) end) end ) end ) end ). -file("src/oaspec/openapi/parser.gleam", 285). ?DOC( " Same as `parse_string` but also returns the YAML `LocationIndex`\n" " built from the input. Caller-side companion to\n" " `parse_file_with_locations` (Issue #411).\n" ). -spec parse_string_with_locations(binary()) -> {ok, {oaspec@internal@openapi@spec:open_api_spec(oaspec@internal@openapi@spec:unresolved()), oaspec@internal@openapi@location_index:location_index()}} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_string_with_locations(Content) -> gleam@result:'try'( parse_to_node(Content), fun(_use0) -> {Root, Index} = _use0, gleam@result:map( parse_root(Root, Index), fun(Spec) -> {Spec, Index} end ) end ). -file("src/oaspec/openapi/parser.gleam", 275). ?DOC( " Parse an OpenAPI spec from a YAML/JSON string. The default path\n" " runs the input through yamerl, which preserves YAML semantics and\n" " source locations but is too slow on large JSON specs (the GitHub\n" " REST OpenAPI is ~12 MB and yamerl effectively hangs — see issue\n" " #352). Use `parse_json_string` directly when the content is known\n" " to be JSON.\n" "\n" " `parse_string` does **not** apply the DoS limits documented in\n" " `ParseLimits`. Reach for `parse_string_with_limits` when the input\n" " is attacker-controlled or sourced from an untrusted file system\n" " (admin-uploaded specs, contract-validation pipelines, CI runners\n" " over user-supplied specs) — see issue #553.\n" "\n" " **YAML 1.1 type coercion: parse_string vs parse_json_string.**\n" " yamerl applies YAML 1.1 implicit-type rules to scalars before they\n" " reach metamon's tree walker. The OTP `json:decode/3` frontend used\n" " by `parse_json_string` does not. The two parsers therefore diverge\n" " on the same JSON bytes whenever a value matches a YAML 1.1\n" " implicit-type pattern:\n" "\n" " | JSON literal | `parse_string` (yamerl, YAML 1.1) | `parse_json_string` (OTP) |\n" " | --- | --- | --- |\n" " | `\"version\": \"Yes\"` | bool `True` | string `\"Yes\"` |\n" " | `\"role\": \"No\"` | bool `False` | string `\"No\"` |\n" " | `\"flag\": \"On\"` / `\"Off\"` | bool `True` / `False` | string `\"On\"` / `\"Off\"` |\n" " | `\"version\": 1.10` | float `1.1` (trailing zero lost) | float `1.10` |\n" " | `\"hex\": 0x10` | int `16` (yamerl extension) | parse error (not valid JSON) |\n" "\n" " For JSON OpenAPI documents — Stripe, GitHub, AsyncAPI, etc. — prefer\n" " `parse_json_string` (or `parse_string_or_json_with_locations`,\n" " which auto-routes by inspecting the first non-whitespace byte).\n" " `parse_string` remains correct for YAML input and for JSON inputs\n" " whose values do not collide with YAML 1.1 implicit-type patterns.\n" ). -spec parse_string(binary()) -> {ok, oaspec@internal@openapi@spec:open_api_spec(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_string(Content) -> _pipe = parse_string_with_locations(Content), gleam@result:map(_pipe, fun(Pair) -> erlang:element(1, Pair) end). -file("src/oaspec/openapi/parser.gleam", 306). ?DOC( " Parse an OpenAPI spec with DoS-aware resource limits. Currently\n" " enforces `limits.max_input_bytes` before parsing begins; the other\n" " fields on `ParseLimits` are reserved for future enforcement (see\n" " issue #553).\n" "\n" " The byte cap is checked via `string.byte_size` so the function\n" " returns immediately on oversized input rather than handing it to\n" " yamerl / `json:decode/3` (both of which allocate proportional\n" " tree memory before the size could be discovered downstream).\n" "\n" " Returns the same `Diagnostic`-bearing `Result` as `parse_string`\n" " when the limit is satisfied; returns a structured\n" " `parse_limit_exceeded` diagnostic when the limit is exceeded.\n" ). -spec parse_string_with_limits(binary(), parse_limits()) -> {ok, oaspec@internal@openapi@spec:open_api_spec(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_string_with_limits(Content, Limits) -> case enforce_input_byte_limit(Content, Limits) of {error, D} -> {error, D}; {ok, _} -> parse_string(Content) end. -file("src/oaspec/openapi/parser.gleam", 363). ?DOC( " JSON variant of `parse_string_with_locations`.\n" "\n" " OTP's `json:decode/3` does not expose token positions, so the\n" " returned `LocationIndex` is **always empty** (`location_index.empty()`).\n" " Capability-check diagnostics from a JSON-only spec therefore carry\n" " `NoSourceLoc`, while diagnostics from the YAML path can carry\n" " line/column info via the index. Downstream tooling that wants to\n" " dispatch over both formats with one signature should reach for\n" " `parse_string_or_json_with_locations`, which inspects the first\n" " non-whitespace byte to pick between the two parsers.\n" "\n" " Returning the empty index instead of an `Option(LocationIndex)` keeps\n" " the type identical to `parse_string_with_locations`, so callers do\n" " not need a separate code path — they only lose location-aware\n" " diagnostics on the JSON branch.\n" ). -spec parse_json_string_with_locations(binary()) -> {ok, {oaspec@internal@openapi@spec:open_api_spec(oaspec@internal@openapi@spec:unresolved()), oaspec@internal@openapi@location_index:location_index()}} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_json_string_with_locations(Content) -> gleam@result:'try'( decode_json_to_node(Content), fun(Root) -> gleam@result:map( parse_root(Root, oaspec@internal@openapi@location_index:empty()), fun(Spec) -> {Spec, oaspec@internal@openapi@location_index:empty()} end ) end ). -file("src/oaspec/openapi/parser.gleam", 70). ?DOC( " Internal parse entry that threads the `visited` stack through every\n" " external-ref recursion so `A.yaml -> B.yaml -> A.yaml` cycles fail\n" " fast with a dedicated diagnostic instead of spinning forever.\n" "\n" " Returns both the parsed spec and the YAML `LocationIndex` built from\n" " the file's content. Recursive external-ref resolution discards\n" " nested files' indices — only the top-level file's index is surfaced\n" " (capability-check needs *one* canonical index, and external refs\n" " land back at their `$ref` site in the main file anyway).\n" ). -spec parse_file_internal( binary(), list(binary()), oaspec@internal@progress:reporter() ) -> {ok, {oaspec@internal@openapi@spec:open_api_spec(oaspec@internal@openapi@spec:unresolved()), oaspec@internal@openapi@location_index:location_index()}} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_file_internal(Path, Visited, Reporter) -> Canonical = canonicalize_ref_path(Path), gleam@bool:guard( gleam@list:contains(Visited, Canonical), {error, cyclic_external_ref_diagnostic(Canonical, Visited)}, fun() -> {Elapsed, Read_result} = oaspec@internal@progress:timed( fun() -> simplifile:read(Path) end ), gleam@result:'try'( begin _pipe = Read_result, gleam@result:map_error( _pipe, fun(Err) -> oaspec@openapi@diagnostic:file_error( <<<<<<<<"Cannot read file: "/utf8, Path/binary>>/binary, " ("/utf8>>/binary, (simplifile:describe_error(Err))/binary>>/binary, ")"/utf8>> ) end ) end, fun(Content) -> oaspec@internal@progress:report( Reporter, <<<<<<<<<<<<"read "/utf8, Path/binary>>/binary, " ("/utf8>>/binary, (format_size(erlang:byte_size(Content)))/binary>>/binary, ", took "/utf8>>/binary, (oaspec@internal@progress:format_ms(Elapsed))/binary>>/binary, ")"/utf8>> ), Is_json = looks_like_json_path(Path), {Elapsed@1, Parsed} = oaspec@internal@progress:timed( fun() -> case Is_json of true -> parse_json_string_with_locations(Content); false -> parse_string_with_locations(Content) end end ), oaspec@internal@progress:report(Reporter, case Is_json of true -> <<<<"parse JSON document via OTP json (took "/utf8, (oaspec@internal@progress:format_ms( Elapsed@1 ))/binary>>/binary, ")"/utf8>>; false -> <<<<"parse YAML document via yamerl (took "/utf8, (oaspec@internal@progress:format_ms( Elapsed@1 ))/binary>>/binary, ")"/utf8>> end), gleam@result:'try'( Parsed, fun(_use0) -> {Spec, Index} = _use0, New_visited = [Canonical | Visited], {Elapsed@2, Resolved} = oaspec@internal@progress:timed( fun() -> oaspec@internal@openapi@external_loader:resolve_external_component_refs( Spec, oaspec@internal@openapi@external_loader_planner:base_dir_of( Path ), fun(Sub_path) -> _pipe@1 = parse_file_internal( Sub_path, New_visited, Reporter ), gleam@result:map( _pipe@1, fun(Pair) -> erlang:element(1, Pair) end ) end ) end ), oaspec@internal@progress:report( Reporter, <<<<"resolve relative-file $ref components (took "/utf8, (oaspec@internal@progress:format_ms( Elapsed@2 ))/binary>>/binary, ")"/utf8>> ), _pipe@2 = Resolved, gleam@result:map(_pipe@2, fun(S) -> {S, Index} end) end ) end ) end ). -file("src/oaspec/openapi/parser.gleam", 42). ?DOC( " Parse an OpenAPI spec from a file path.\n" " Supports both YAML (.yaml, .yml) and JSON (.json) files.\n" " After parsing, resolves relative-file `$ref` values across schemas,\n" " parameters, request bodies, responses, and path items — including\n" " nested object/array properties and composition branches — by loading\n" " the referenced files from disk and merging their definitions into the\n" " main spec. Cyclic external ref graphs (`A.yaml → B.yaml → A.yaml`)\n" " fail fast with a dedicated diagnostic. HTTP/HTTPS URLs are not\n" " followed.\n" ). -spec parse_file(binary()) -> {ok, oaspec@internal@openapi@spec:open_api_spec(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_file(Path) -> _pipe = parse_file_internal(Path, [], oaspec@internal@progress:noop()), gleam@result:map(_pipe, fun(Pair) -> erlang:element(1, Pair) end). -file("src/oaspec/openapi/parser.gleam", 54). ?DOC( " Combined `parse_file` entry point that accepts a `Reporter` and\n" " also returns the top-level YAML `LocationIndex`. Issue #411 +\n" " #352. The CLI uses this so capability-check diagnostics surface\n" " `path:line:column:` and progress lines on big specs at the same\n" " time. Library callers that don't need progress should pass\n" " `progress.noop()`; callers that don't need locations can discard\n" " the second tuple element.\n" ). -spec parse_file_with_progress_and_locations( binary(), oaspec@internal@progress:reporter() ) -> {ok, {oaspec@internal@openapi@spec:open_api_spec(oaspec@internal@openapi@spec:unresolved()), oaspec@internal@openapi@location_index:location_index()}} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_file_with_progress_and_locations(Path, Reporter) -> parse_file_internal(Path, [], Reporter). -file("src/oaspec/openapi/parser.gleam", 341). ?DOC( " Parse an OpenAPI spec from a JSON string using OTP's native JSON\n" " decoder instead of yamerl. Roughly two orders of magnitude faster\n" " than `parse_string` on large specs because the YAML pre-processing\n" " and constructor passes are skipped (issue #352). Behaves like\n" " `parse_string` once the tree is built — same `OpenApiSpec` shape,\n" " same downstream pipeline. Diagnostics from this path do not carry\n" " source line/column info because OTP `json:decode/3` does not\n" " expose decoder positions; the caller still gets the path-prefixed\n" " error message that downstream tooling relies on.\n" ). -spec parse_json_string(binary()) -> {ok, oaspec@internal@openapi@spec:open_api_spec(oaspec@internal@openapi@spec:unresolved())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_json_string(Content) -> _pipe = parse_json_string_with_locations(Content), gleam@result:map(_pipe, fun(Pair) -> erlang:element(1, Pair) end). -file("src/oaspec/openapi/parser.gleam", 387). ?DOC( " Auto-dispatch over `parse_string_with_locations` (YAML) and\n" " `parse_json_string_with_locations` (JSON) based on the first\n" " non-whitespace byte of `content`.\n" "\n" " `{` and `[` route to the JSON parser (orders of magnitude faster on\n" " large specs — see `parse_json_string`); anything else routes to\n" " the YAML parser. The dispatch covers the conventional OpenAPI\n" " document shapes (object root for full specs, array root for the\n" " rare top-level component lists). Whitespace prefixes (BOM, leading\n" " spaces, blank lines) are skipped before the discriminator byte\n" " is inspected.\n" "\n" " Use this when downstream tooling needs a single entry point for\n" " both formats — LSP-style features, error-hint generators,\n" " source-map producers — without writing the dispatch wrapper at\n" " every call site.\n" ). -spec parse_string_or_json_with_locations(binary()) -> {ok, {oaspec@internal@openapi@spec:open_api_spec(oaspec@internal@openapi@spec:unresolved()), oaspec@internal@openapi@location_index:location_index()}} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_string_or_json_with_locations(Content) -> case looks_like_json(Content) of true -> parse_json_string_with_locations(Content); false -> parse_string_with_locations(Content) end.