Current section

Files

Jump to
oaspec src oaspec@openapi@parser.erl
Raw

src/oaspec@openapi@parser.erl

-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]).
-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()}.
-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", 407).
?DOC(
" Run yamerl on `content` and return the root node plus a\n"
" location index built from the same content. Both pieces are\n"
" consumed by `parse_root` to produce the OpenApiSpec.\n"
).
-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 = yaml_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", 445).
?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", 471).
-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", 553).
-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", 539).
?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", 1056).
?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", 1077).
?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", 1101).
?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", 1435).
?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", 1547).
?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", 1713).
?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", 1616).
?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", 1554).
?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", 1797).
?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(
<<<<Context/binary,
".security."/utf8>>/binary,
Scheme_name/binary>>,
<<"Scope must be a string"/utf8>>,
oaspec@internal@openapi@location_index:lookup(
Index,
<<<<Context/binary,
".security."/utf8>>/binary,
Scheme_name/binary>>
)
)}
end end
);
node_nil ->
{ok, []};
_ ->
{error,
oaspec@openapi@diagnostic:invalid_value(
<<<<Context/binary,
".security."/utf8>>/binary,
Scheme_name/binary>>,
<<"Scopes must be an array of strings"/utf8>>,
oaspec@internal@openapi@location_index:lookup(
Index,
<<<<Context/binary,
".security."/utf8>>/binary,
Scheme_name/binary>>
)
)}
end, fun(Scopes) ->
{ok,
{security_scheme_ref,
Scheme_name,
Scopes}}
end);
_ ->
{error,
oaspec@openapi@diagnostic:invalid_value(
<<Context/binary, ".security"/utf8>>,
<<"Security requirement key must be a string"/utf8>>,
oaspec@internal@openapi@location_index:lookup(
Index,
<<Context/binary, ".security"/utf8>>
)
)}
end
end
),
fun(Scheme_refs) ->
{ok, {security_requirement, Scheme_refs}}
end
);
_ ->
{error,
oaspec@openapi@diagnostic:invalid_value(
<<Context/binary, ".security"/utf8>>,
<<"Security requirement must be an object"/utf8>>,
oaspec@internal@openapi@location_index:lookup(
Index,
<<Context/binary, ".security"/utf8>>
)
)}
end.
-file("src/oaspec/openapi/parser.gleam", 1759).
?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", 1776).
?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", 1971).
?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", 1984).
?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", 584).
?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", 2000).
?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", 649).
?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", 637).
?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", 2028).
?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", 2045).
?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", 2144).
?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", 1164).
?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,
<<Context/binary,
".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
);
_ ->
{error,
oaspec@openapi@diagnostic:missing_field(
<<Context/binary, ".requestBody"/utf8>>,
<<"content"/utf8>>,
oaspec@internal@openapi@location_index:lookup_field(
Index,
<<Context/binary, ".requestBody"/utf8>>,
<<"content"/utf8>>
)
)}
end.
-file("src/oaspec/openapi/parser.gleam", 1148).
?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", 1121).
?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", 907).
?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", 1221).
?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,
<<Context/binary,
".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", 1488).
?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", 2098).
?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", 949).
?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'(
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, false}} ->
{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>>
)
)};
{in_path, _} ->
{ok, true};
{_, {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.
-file("src/oaspec/openapi/parser.gleam", 936).
?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@list:try_map(
Items,
fun(Item) -> parse_parameter(Item, Components, Index) end
);
_ ->
{ok, []}
end.
-file("src/oaspec/openapi/parser.gleam", 1461).
?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", 2180).
?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", 2221).
?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", 1310).
?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", 1269).
?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@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, _} ->
{ok, Acc}
end
end;
{node_int, Code@1} ->
gleam@result:'try'(
parse_response(Value_node, Components, Index),
fun(Resp@1) ->
{ok,
gleam@dict:insert(
Acc,
{status, Code@1},
Resp@1
)}
end
);
_ ->
{ok, Acc}
end
end
);
_ ->
{ok, maps:new()}
end.
-file("src/oaspec/openapi/parser.gleam", 923).
?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", 1519).
?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@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
);
_ ->
{ok, maps:new()}
end.
-file("src/oaspec/openapi/parser.gleam", 1912).
?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,
<<<<Context/binary,
".callbacks."/utf8>>/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(
<<Context/binary, ".callbacks"/utf8>>,
<<"url expression"/utf8>>,
oaspec@internal@openapi@location_index:lookup_field(
Index,
<<Context/binary, ".callbacks"/utf8>>,
<<"url expression"/utf8>>
)
)};
_ ->
{ok, {callback, maps:from_list(Parsed_entries)}}
end end
);
_ ->
{error,
oaspec@openapi@diagnostic:missing_field(
<<Context/binary, ".callbacks"/utf8>>,
<<"url expression"/utf8>>,
oaspec@internal@openapi@location_index:lookup_field(
Index,
<<Context/binary, ".callbacks"/utf8>>,
<<"url expression"/utf8>>
)
)}
end.
-file("src/oaspec/openapi/parser.gleam", 712).
?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", 801).
?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", 821).
?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,
<<<<Path/binary, "."/utf8>>/binary, Method_key/binary>>,
Components,
Index
)
end
).
-file("src/oaspec/openapi/parser.gleam", 842).
?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", 1864).
?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(
<<<<<<Context/binary,
".callbacks."/utf8>>/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,
<<<<Context/binary,
".callbacks."/utf8>>/binary,
Callback_name/binary>>,
<<"$ref"/utf8>>
)
)}
end;
_ ->
{ok, Acc}
end
end
);
_ ->
{ok, maps:new()}
end.
-file("src/oaspec/openapi/parser.gleam", 671).
?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@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'(
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;
_ ->
{ok, Acc}
end
end
);
_ ->
{ok, maps:new()}
end.
-file("src/oaspec/openapi/parser.gleam", 1393).
?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", 1584).
?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", 1341).
?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", 572).
?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", 2064).
?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", 477).
?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'(
begin
_pipe = yay:extract_string(Node, <<"openapi"/utf8>>),
_pipe@2 = gleam@result:lazy_or(
_pipe,
fun() -> _pipe@1 = yay:extract_float(Node, <<"openapi"/utf8>>),
gleam@result:map(
_pipe@1,
fun(F) -> case F =:= erlang:float(erlang:trunc(F)) of
true ->
<<(erlang:integer_to_binary(erlang:trunc(F)))/binary,
".0"/utf8>>;
false ->
gleam_stdlib:float_to_string(F)
end end
) end
),
gleam@result:map_error(
_pipe@2,
fun(_capture) ->
oaspec@internal@openapi@parser_yay_error:missing_field_from_extraction(
_capture,
<<""/utf8>>,
<<"openapi"/utf8>>,
oaspec@internal@openapi@location_index:lookup_field(
Index,
<<""/utf8>>,
<<"openapi"/utf8>>
)
)
end
)
end,
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'(
parse_optional_components(Node, Index),
fun(Components) ->
gleam@result:'try'(
parse_paths(Node, Components, Index),
fun(Paths) ->
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
).
-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.