Current section

Files

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

src/oaspec@openapi@external_loader.erl

-module(oaspec@openapi@external_loader).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/openapi/external_loader.gleam").
-export([base_dir_of/1, resolve_external_component_refs/3]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Support for external `$ref` values that point at component definitions\n"
" in a sibling YAML/JSON file — `./other.yaml#/components/schemas/Foo`\n"
" style. Walks `components.schemas`, pulls referenced schemas from the\n"
" target file into the main spec, and rewrites the refs to local form.\n"
"\n"
" Supported shapes:\n"
" - top-level component schema entries (`components.schemas.Foo: $ref: ...`)\n"
" - ObjectSchema property values (`properties.field: $ref: ...`)\n"
" - ArraySchema item values (`items: $ref: ...`)\n"
" - ObjectSchema additionalProperties values (`additionalProperties: $ref: ...`)\n"
" - composition branches (`oneOf`, `anyOf`, `allOf` variant refs)\n"
" - `components.parameters.*.schema: $ref: ...` (parameter schema only)\n"
" - `components.parameters.*.content.*.schema: $ref: ...`\n"
" - `components.request_bodies.*.content.*.schema: $ref: ...`\n"
" - `components.responses.*.content.*.schema: $ref: ...`\n"
" - operation-level `parameters[*].schema` / `parameters[*].content.*.schema`\n"
" on both `paths.<path>.parameters` and `paths.<path>.<method>.parameters`\n"
" - operation-level `requestBody.content.*.schema`\n"
" - operation-level `responses.<code>.content.*.schema`\n"
" - `components.headers.*.schema` and every `Response.headers.*.schema`\n"
" (both under `components.responses` and operation-level responses)\n"
" - refs inside `components.path_items.*` (reusable PathItem\n"
" definitions, walked via the same `rewrite_path_item` helper\n"
" used at the top level)\n"
" - refs inside `operation.callbacks.*.entries.*` PathItems (recursive)\n"
" - whole-object external refs for `components.parameters`,\n"
" `components.requestBodies`, `components.responses`, and\n"
" `components.pathItems` (e.g., `$ref: './other.yaml#/components/parameters/Foo'`)\n"
" - one level of alias chaining inside the same external file\n"
" (`LegacyX: $ref: \"#/components/schemas/X\"` in the external file).\n"
" Cross-file chains collapse naturally because parse_file runs\n"
" the resolver on every loaded file before handing it back, so a\n"
" target that is itself an external ref is already inline by the\n"
" time we look it up.\n"
"\n"
" Out of scope (see issue #98 parent):\n"
" - alias chains longer than one hop inside the same file\n"
" - HTTP/HTTPS URLs\n"
" - cyclic external refs (e.g., `A.yaml` → `B.yaml` → `A.yaml`)\n"
" would loop forever; callers are expected to maintain acyclic\n"
" file graphs.\n"
"\n"
" Name collisions — when an external ref would overwrite an existing local\n"
" schema, or when two external refs pull in the same fragment name from\n"
" different files — are surfaced as `Diagnostic` errors rather than\n"
" silently dropping one side.\n"
).
-file("src/oaspec/openapi/external_loader.gleam", 249).
?DOC(" Try each component prefix against a fragment, returning the first match.\n").
-spec try_component_prefix(binary(), binary(), list(binary())) -> gleam@option:option({binary(),
binary(),
binary()}).
try_component_prefix(File_path, Fragment, Prefixes) ->
case Prefixes of
[] ->
none;
[Prefix | Rest] ->
case gleam_stdlib:string_starts_with(Fragment, Prefix) of
true ->
Name = gleam@string:replace(Fragment, Prefix, <<""/utf8>>),
case {gleam_stdlib:contains_string(Name, <<"/"/utf8>>),
Name} of
{false, <<""/utf8>>} ->
none;
{false, _} ->
{some, {File_path, Prefix, Name}};
{true, _} ->
none
end;
false ->
try_component_prefix(File_path, Fragment, Rest)
end
end.
-file("src/oaspec/openapi/external_loader.gleam", 230).
?DOC(
" Detect external refs like `./other.yaml#/components/parameters/Foo`.\n"
" Returns Some(#(file_path, component_prefix, name)) or None.\n"
" Supported prefixes: parameters, requestBodies, responses, pathItems.\n"
).
-spec extract_external_component_ref(binary()) -> gleam@option:option({binary(),
binary(),
binary()}).
extract_external_component_ref(Ref_str) ->
Is_relative = gleam_stdlib:string_starts_with(Ref_str, <<"./"/utf8>>) orelse gleam_stdlib:string_starts_with(
Ref_str,
<<"../"/utf8>>
),
gleam@bool:guard(
not Is_relative,
none,
fun() -> case gleam@string:split_once(Ref_str, <<"#"/utf8>>) of
{ok, {File_path, Fragment}} ->
try_component_prefix(
File_path,
Fragment,
[<<"/components/parameters/"/utf8>>,
<<"/components/requestBodies/"/utf8>>,
<<"/components/responses/"/utf8>>,
<<"/components/pathItems/"/utf8>>]
);
_ ->
none
end end
).
-file("src/oaspec/openapi/external_loader.gleam", 327).
?DOC(
" Generic helper that walks a `Dict(String, RefOr(a))`, resolving any\n"
" `Ref(ref_str)` whose fragment matches `target_prefix` by loading the\n"
" external file and looking up the named object via `finder`.\n"
).
-spec resolve_ref_or_dict(
gleam@dict:dict(binary(), oaspec@openapi@spec:ref_or(QNQ)),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
binary(),
fun((oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved()), binary(), binary()) -> {ok,
QNQ} |
{error, oaspec@openapi@diagnostic:diagnostic()})
) -> {ok, gleam@dict:dict(binary(), oaspec@openapi@spec:ref_or(QNQ))} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
resolve_ref_or_dict(Entries, Base_dir, Parse_file, Target_prefix, Finder) ->
Pairs = maps:to_list(Entries),
gleam@result:'try'(
gleam@list:try_fold(
Pairs,
[],
fun(Acc, Entry) ->
{Name, Ref_or_val} = Entry,
case Ref_or_val of
{ref, Ref_str} ->
case extract_external_component_ref(Ref_str) of
{some, {File_path, Prefix, Obj_name}} when Prefix =:= Target_prefix ->
Resolved_path = filepath:join(
Base_dir,
File_path
),
gleam@result:'try'(
Parse_file(Resolved_path),
fun(Loaded) ->
gleam@result:'try'(
Finder(
Loaded,
Obj_name,
Resolved_path
),
fun(Target) ->
{ok,
[{Name, {value, Target}} |
Acc]}
end
)
end
);
_ ->
{ok, [{Name, Ref_or_val} | Acc]}
end;
{value, _} ->
{ok, [{Name, Ref_or_val} | Acc]}
end
end
),
fun(New_pairs) ->
{ok,
gleam@list:fold(
New_pairs,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
)}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 361).
?DOC(" Look up a parameter by name in an external file's components.\n").
-spec find_external_parameter(
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved()),
binary(),
binary()
) -> {ok, oaspec@openapi@spec:parameter(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
find_external_parameter(Loaded, Name, Source_path) ->
case erlang:element(5, Loaded) of
{some, Components} ->
case gleam_stdlib:map_get(erlang:element(3, Components), Name) of
{ok, {value, P}} ->
{ok, P};
{ok, {ref, _}} ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<<<"External parameter '"/utf8, Name/binary>>/binary,
"' in '"/utf8>>/binary,
Source_path/binary>>/binary,
"' is itself a $ref; chained external refs are not supported."/utf8>>,
severity_error,
target_both,
{some,
<<"Inline the parameter in the external file or flatten the ref chain."/utf8>>}
)};
{error, nil} ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<"External file '"/utf8, Source_path/binary>>/binary,
"' has no components.parameters."/utf8>>/binary,
Name/binary>>,
severity_error,
target_both,
{some,
<<"Verify the ref path and that the referenced file defines the parameter."/utf8>>}
)}
end;
none ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<"External file '"/utf8, Source_path/binary>>/binary,
"' has no components block."/utf8>>,
severity_error,
target_both,
{some,
<<"Add a components.parameters section to the referenced file."/utf8>>}
)}
end.
-file("src/oaspec/openapi/external_loader.gleam", 412).
?DOC(" Look up a request body by name in an external file's components.\n").
-spec find_external_request_body(
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved()),
binary(),
binary()
) -> {ok, oaspec@openapi@spec:request_body(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
find_external_request_body(Loaded, Name, Source_path) ->
case erlang:element(5, Loaded) of
{some, Components} ->
case gleam_stdlib:map_get(erlang:element(4, Components), Name) of
{ok, {value, Rb}} ->
{ok, Rb};
{ok, {ref, _}} ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<<<"External request body '"/utf8,
Name/binary>>/binary,
"' in '"/utf8>>/binary,
Source_path/binary>>/binary,
"' is itself a $ref; chained external refs are not supported."/utf8>>,
severity_error,
target_both,
{some,
<<"Inline the request body in the external file or flatten the ref chain."/utf8>>}
)};
{error, nil} ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<"External file '"/utf8, Source_path/binary>>/binary,
"' has no components.requestBodies."/utf8>>/binary,
Name/binary>>,
severity_error,
target_both,
{some,
<<"Verify the ref path and that the referenced file defines the request body."/utf8>>}
)}
end;
none ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<"External file '"/utf8, Source_path/binary>>/binary,
"' has no components block."/utf8>>,
severity_error,
target_both,
{some,
<<"Add a components.requestBodies section to the referenced file."/utf8>>}
)}
end.
-file("src/oaspec/openapi/external_loader.gleam", 463).
?DOC(" Look up a response by name in an external file's components.\n").
-spec find_external_response(
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved()),
binary(),
binary()
) -> {ok, oaspec@openapi@spec:response(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
find_external_response(Loaded, Name, Source_path) ->
case erlang:element(5, Loaded) of
{some, Components} ->
case gleam_stdlib:map_get(erlang:element(5, Components), Name) of
{ok, {value, R}} ->
{ok, R};
{ok, {ref, _}} ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<<<"External response '"/utf8, Name/binary>>/binary,
"' in '"/utf8>>/binary,
Source_path/binary>>/binary,
"' is itself a $ref; chained external refs are not supported."/utf8>>,
severity_error,
target_both,
{some,
<<"Inline the response in the external file or flatten the ref chain."/utf8>>}
)};
{error, nil} ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<"External file '"/utf8, Source_path/binary>>/binary,
"' has no components.responses."/utf8>>/binary,
Name/binary>>,
severity_error,
target_both,
{some,
<<"Verify the ref path and that the referenced file defines the response."/utf8>>}
)}
end;
none ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<"External file '"/utf8, Source_path/binary>>/binary,
"' has no components block."/utf8>>,
severity_error,
target_both,
{some,
<<"Add a components.responses section to the referenced file."/utf8>>}
)}
end.
-file("src/oaspec/openapi/external_loader.gleam", 512).
?DOC(" Look up a path item by name in an external file's components.\n").
-spec find_external_path_item(
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved()),
binary(),
binary()
) -> {ok, oaspec@openapi@spec:path_item(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
find_external_path_item(Loaded, Name, Source_path) ->
case erlang:element(5, Loaded) of
{some, Components} ->
case gleam_stdlib:map_get(erlang:element(7, Components), Name) of
{ok, {value, Pi}} ->
{ok, Pi};
{ok, {ref, _}} ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<<<"External path item '"/utf8, Name/binary>>/binary,
"' in '"/utf8>>/binary,
Source_path/binary>>/binary,
"' is itself a $ref; chained external refs are not supported."/utf8>>,
severity_error,
target_both,
{some,
<<"Inline the path item in the external file or flatten the ref chain."/utf8>>}
)};
{error, nil} ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<"External file '"/utf8, Source_path/binary>>/binary,
"' has no components.pathItems."/utf8>>/binary,
Name/binary>>,
severity_error,
target_both,
{some,
<<"Verify the ref path and that the referenced file defines the path item."/utf8>>}
)}
end;
none ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<"External file '"/utf8, Source_path/binary>>/binary,
"' has no components block."/utf8>>,
severity_error,
target_both,
{some,
<<"Add a components.pathItems section to the referenced file."/utf8>>}
)}
end.
-file("src/oaspec/openapi/external_loader.gleam", 276).
?DOC(
" Walk `components.parameters`, `request_bodies`, `responses`, and\n"
" `path_items`: for each `Ref(ref_str)` entry that is an external\n"
" component ref (e.g., `./other.yaml#/components/parameters/Foo`),\n"
" load the external file and replace the entry with `Value(loaded_object)`.\n"
" Runs after `process_components` and before `process_nested_property_refs`.\n"
).
-spec process_whole_object_refs(
oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()})
) -> {ok, oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
process_whole_object_refs(Components, Base_dir, Parse_file) ->
gleam@result:'try'(
resolve_ref_or_dict(
erlang:element(3, Components),
Base_dir,
Parse_file,
<<"/components/parameters/"/utf8>>,
fun find_external_parameter/3
),
fun(New_parameters) ->
gleam@result:'try'(
resolve_ref_or_dict(
erlang:element(4, Components),
Base_dir,
Parse_file,
<<"/components/requestBodies/"/utf8>>,
fun find_external_request_body/3
),
fun(New_request_bodies) ->
gleam@result:'try'(
resolve_ref_or_dict(
erlang:element(5, Components),
Base_dir,
Parse_file,
<<"/components/responses/"/utf8>>,
fun find_external_response/3
),
fun(New_responses) ->
gleam@result:'try'(
resolve_ref_or_dict(
erlang:element(7, Components),
Base_dir,
Parse_file,
<<"/components/pathItems/"/utf8>>,
fun find_external_path_item/3
),
fun(New_path_items) ->
{ok,
{components,
erlang:element(2, Components),
New_parameters,
New_request_bodies,
New_responses,
erlang:element(6, Components),
New_path_items,
erlang:element(8, Components),
erlang:element(9, Components),
erlang:element(10, Components)}}
end
)
end
)
end
)
end
).
-file("src/oaspec/openapi/external_loader.gleam", 719).
?DOC(
" Placeholder target used to seed the nested-import tracker with top-level\n"
" imports whose schema was already merged. `Reference(\"\", \"\")` is never\n"
" emitted as a real value — the merge step filters it out by matching only\n"
" on `Inline(_)` targets.\n"
).
-spec no_target() -> oaspec@openapi@schema:schema_ref().
no_target() ->
{reference, <<""/utf8>>, <<""/utf8>>}.
-file("src/oaspec/openapi/external_loader.gleam", 1708).
?DOC(
" Reject a nested property ref whose fragment name collides with a schema\n"
" that was authored inline in the main spec. Without this guard the nested\n"
" import would silently rebind to the local schema — even if its shape is\n"
" unrelated — because the merged dict already holds that slot.\n"
).
-spec check_nested_local_collision(binary(), binary(), list(binary())) -> {ok,
nil} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
check_nested_local_collision(Fragment_name, Source_path, Original_local_names) ->
gleam@bool:guard(
not gleam@list:contains(Original_local_names, Fragment_name),
{ok, nil},
fun() ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<<<"Nested property $ref imports schema '"/utf8,
Fragment_name/binary>>/binary,
"' from '"/utf8>>/binary,
Source_path/binary>>/binary,
"', but a local schema with the same name is already defined."/utf8>>,
severity_error,
target_both,
{some,
<<"Rename one of the colliding schemas, or point the external ref at a file whose fragment name is unique."/utf8>>}
)}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1735).
?DOC(
" Same shape as `check_cross_file_collision` but reads from the\n"
" `imports: Dict(fragment_name, #(source_path, target_schema))` dict used\n"
" by the nested-property phase.\n"
).
-spec check_nested_cross_file_collision(
binary(),
binary(),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()})
) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}.
check_nested_cross_file_collision(Fragment_name, Resolved_path, Imports) ->
case gleam_stdlib:map_get(Imports, Fragment_name) of
{error, _} ->
{ok, nil};
{ok, {Prev_path, _}} ->
case Prev_path =:= Resolved_path of
true ->
{ok, nil};
false ->
{error,
oaspec@openapi@diagnostic:validation(
Resolved_path,
<<<<<<<<<<<<"Nested property $ref imports schema '"/utf8,
Fragment_name/binary>>/binary,
"' from '"/utf8>>/binary,
Resolved_path/binary>>/binary,
"', but the same name was already imported from '"/utf8>>/binary,
Prev_path/binary>>/binary,
"'."/utf8>>,
severity_error,
target_both,
{some,
<<"Rename one of the schemas in the source files so imports do not collide."/utf8>>}
)}
end
end.
-file("src/oaspec/openapi/external_loader.gleam", 1771).
?DOC(
" Reject an external ref whose fragment name would collide with a schema\n"
" already defined locally in the same spec. The case where the entry name\n"
" equals the fragment name (`Widget: $ref: './other.yaml#/.../Widget'`) is\n"
" intentionally allowed — we treat it as the user asking for that slot to\n"
" hold the imported schema.\n"
).
-spec check_local_collision(binary(), binary(), binary(), list(binary())) -> {ok,
nil} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
check_local_collision(
Entry_name,
Fragment_name,
Source_path,
Original_local_names
) ->
gleam@bool:guard(
Entry_name =:= Fragment_name,
{ok, nil},
fun() ->
gleam@bool:guard(
not gleam@list:contains(Original_local_names, Fragment_name),
{ok, nil},
fun() ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<<<"External $ref imports schema '"/utf8,
Fragment_name/binary>>/binary,
"' from '"/utf8>>/binary,
Source_path/binary>>/binary,
"', but a local schema with the same name is already defined."/utf8>>,
severity_error,
target_both,
{some,
<<"Rename one of the colliding schemas, or point the external ref at a file whose fragment name is unique."/utf8>>}
)}
end
)
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1800).
?DOC(
" Reject two external refs that both pull the same fragment name from\n"
" different source files. Re-importing the same name from the same path is\n"
" allowed (idempotent) to keep error messages narrow.\n"
).
-spec check_cross_file_collision(
binary(),
binary(),
gleam@dict:dict(binary(), binary())
) -> {ok, nil} | {error, oaspec@openapi@diagnostic:diagnostic()}.
check_cross_file_collision(Fragment_name, Resolved_path, Imported) ->
case gleam_stdlib:map_get(Imported, Fragment_name) of
{error, _} ->
{ok, nil};
{ok, Prev_path} ->
case Prev_path =:= Resolved_path of
true ->
{ok, nil};
false ->
{error,
oaspec@openapi@diagnostic:validation(
Resolved_path,
<<<<<<<<<<<<"External $ref imports schema '"/utf8,
Fragment_name/binary>>/binary,
"' from '"/utf8>>/binary,
Resolved_path/binary>>/binary,
"', but the same name was already imported from '"/utf8>>/binary,
Prev_path/binary>>/binary,
"'."/utf8>>,
severity_error,
target_both,
{some,
<<"Rename one of the schemas in the source files so imports do not collide."/utf8>>}
)}
end
end.
-file("src/oaspec/openapi/external_loader.gleam", 1834).
?DOC(
" Detect a `./...#/components/schemas/Name` or `../...#/components/schemas/Name`\n"
" ref. Returns `Some(#(file_path, schema_name))` when it matches, `None`\n"
" otherwise.\n"
).
-spec extract_external_ref(oaspec@openapi@schema:schema_ref()) -> gleam@option:option({binary(),
binary()}).
extract_external_ref(Schema_ref) ->
case Schema_ref of
{reference, Ref, _} ->
case gleam_stdlib:string_starts_with(Ref, <<"./"/utf8>>) orelse gleam_stdlib:string_starts_with(
Ref,
<<"../"/utf8>>
) of
true ->
case gleam@string:split_once(Ref, <<"#"/utf8>>) of
{ok, {File_path, Fragment}} ->
case gleam_stdlib:string_starts_with(
Fragment,
<<"/components/schemas/"/utf8>>
) of
true ->
Name = gleam@string:replace(
Fragment,
<<"/components/schemas/"/utf8>>,
<<""/utf8>>
),
case {gleam_stdlib:contains_string(
Name,
<<"/"/utf8>>
),
Name} of
{false, <<""/utf8>>} ->
none;
{false, _} ->
{some, {File_path, Name}};
{true, _} ->
none
end;
false ->
none
end;
_ ->
none
end;
false ->
none
end;
_ ->
none
end.
-file("src/oaspec/openapi/external_loader.gleam", 218).
?DOC(
" Names in the current spec that are *not themselves* external refs.\n"
" These are the schemas the external loader must not silently overwrite.\n"
).
-spec local_schema_names(list({binary(), oaspec@openapi@schema:schema_ref()})) -> list(binary()).
local_schema_names(Entries) ->
gleam@list:filter_map(
Entries,
fun(Entry) -> case extract_external_ref(erlang:element(2, Entry)) of
{some, _} ->
{error, nil};
none ->
{ok, erlang:element(1, Entry)}
end end
).
-file("src/oaspec/openapi/external_loader.gleam", 1951).
?DOC(
" If `ref` is a local component-schemas ref inside the same file (no\n"
" leading file part), return the schema name. Otherwise return None\n"
" so the caller can surface a cross-file diagnostic.\n"
).
-spec local_schema_name_from_ref(binary()) -> gleam@option:option(binary()).
local_schema_name_from_ref(Ref) ->
Prefix = <<"#/components/schemas/"/utf8>>,
case gleam_stdlib:string_starts_with(Ref, Prefix) of
true ->
Name = gleam@string:replace(Ref, Prefix, <<""/utf8>>),
case {gleam_stdlib:contains_string(Name, <<"/"/utf8>>), Name} of
{false, <<""/utf8>>} ->
none;
{false, _} ->
{some, Name};
{true, _} ->
none
end;
false ->
none
end.
-file("src/oaspec/openapi/external_loader.gleam", 1888).
?DOC(
" Look up `name` in the external file's `components.schemas`. If the\n"
" value is inline, return it directly. If it is a local alias\n"
" (`#/components/schemas/Other`) pointing at another schema *in the\n"
" same file*, follow the alias one level. Cross-file chained refs and\n"
" longer alias chains still surface a diagnostic.\n"
).
-spec find_schema_follow_alias(
oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
binary(),
binary()
) -> {ok, oaspec@openapi@schema:schema_ref()} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
find_schema_follow_alias(Components, Name, Source_path) ->
case gleam_stdlib:map_get(erlang:element(2, Components), Name) of
{ok, {inline, _} = S} ->
{ok, S};
{ok, {reference, Ref, _}} ->
case local_schema_name_from_ref(Ref) of
{some, Aliased} ->
case gleam_stdlib:map_get(
erlang:element(2, Components),
Aliased
) of
{ok, {inline, _} = S@1} ->
{ok, S@1};
_ ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<<<<<<<"External schema '"/utf8,
Name/binary>>/binary,
"' in '"/utf8>>/binary,
Source_path/binary>>/binary,
"' aliases '"/utf8>>/binary,
Aliased/binary>>/binary,
"', but the target is itself a $ref or missing; only a single level of aliasing inside the same file is supported."/utf8>>,
severity_error,
target_both,
{some,
<<"Flatten the alias chain in the external file so the target resolves to an inline schema."/utf8>>}
)}
end;
none ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<<<"External schema '"/utf8, Name/binary>>/binary,
"' in '"/utf8>>/binary,
Source_path/binary>>/binary,
"' is itself a $ref pointing outside this file; chained external refs are not supported yet."/utf8>>,
severity_error,
target_both,
{some,
<<"Inline the external schema or flatten the ref to live inside the same file."/utf8>>}
)}
end;
{error, nil} ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<<<"External file '"/utf8, Source_path/binary>>/binary,
"' has no components.schemas."/utf8>>/binary,
Name/binary>>,
severity_error,
target_both,
{some,
<<"Verify the ref path and that the referenced file defines the schema."/utf8>>}
)}
end.
-file("src/oaspec/openapi/external_loader.gleam", 1863).
-spec find_external_schema(
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved()),
binary(),
binary()
) -> {ok, oaspec@openapi@schema:schema_ref()} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
find_external_schema(Loaded, Name, Source_path) ->
case erlang:element(5, Loaded) of
{some, Components} ->
find_schema_follow_alias(Components, Name, Source_path);
none ->
{error,
oaspec@openapi@diagnostic:validation(
Source_path,
<<<<"External file '"/utf8, Source_path/binary>>/binary,
"' has no components block."/utf8>>,
severity_error,
target_both,
{some,
<<"Add a components.schemas section to the referenced file or inline the schema."/utf8>>}
)}
end.
-file("src/oaspec/openapi/external_loader.gleam", 158).
-spec process_components(
oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
list(binary())
) -> {ok,
{oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
gleam@dict:dict(binary(), binary())}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
process_components(Components, Base_dir, Parse_file, Original_local_names) ->
Entries = maps:to_list(erlang:element(2, Components)),
gleam@result:'try'(
gleam@list:try_fold(
Entries,
{[], maps:new()},
fun(Acc, Entry) ->
{Pending, Imported} = Acc,
{Name, Schema_ref} = Entry,
case extract_external_ref(Schema_ref) of
{some, {Rel_path, Fragment_name}} ->
Resolved_path = filepath:join(Base_dir, Rel_path),
gleam@result:'try'(
check_local_collision(
Name,
Fragment_name,
Resolved_path,
Original_local_names
),
fun(_) ->
gleam@result:'try'(
check_cross_file_collision(
Fragment_name,
Resolved_path,
Imported
),
fun(_) ->
gleam@result:'try'(
Parse_file(Resolved_path),
fun(Loaded) ->
gleam@result:'try'(
find_external_schema(
Loaded,
Fragment_name,
Resolved_path
),
fun(Target) ->
Local_ref = {reference,
<<"#/components/schemas/"/utf8,
Fragment_name/binary>>,
Fragment_name},
Pending@1 = [{Name,
Local_ref},
{Fragment_name,
Target} |
Pending],
Imported@1 = gleam@dict:insert(
Imported,
Fragment_name,
Resolved_path
),
{ok,
{Pending@1,
Imported@1}}
end
)
end
)
end
)
end
);
none ->
{ok, {[{Name, Schema_ref} | Pending], Imported}}
end
end
),
fun(_use0) ->
{New_entries, Imported@2} = _use0,
Merged = gleam@list:fold(
New_entries,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
{ok,
{{components,
Merged,
erlang:element(3, Components),
erlang:element(4, Components),
erlang:element(5, Components),
erlang:element(6, Components),
erlang:element(7, Components),
erlang:element(8, Components),
erlang:element(9, Components),
erlang:element(10, Components)},
Imported@2}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1665).
?DOC(
" Core external-ref hoisting step shared by the property-value and\n"
" array-items resolvers. If `schema_ref` is a relative-file external ref,\n"
" the referenced schema is staged for merging and the ref is rewritten to\n"
" a local `#/components/schemas/<fragment>` ref. Otherwise the ref passes\n"
" through unchanged.\n"
).
-spec maybe_hoist_ref(
oaspec@openapi@schema:schema_ref(),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{oaspec@openapi@schema:schema_ref(),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
maybe_hoist_ref(Schema_ref, Base_dir, Parse_file, Imports, Original_local_names) ->
case extract_external_ref(Schema_ref) of
{some, {Rel_path, Fragment_name}} ->
Resolved_path = filepath:join(Base_dir, Rel_path),
gleam@result:'try'(
check_nested_local_collision(
Fragment_name,
Resolved_path,
Original_local_names
),
fun(_) ->
gleam@result:'try'(
check_nested_cross_file_collision(
Fragment_name,
Resolved_path,
Imports
),
fun(_) ->
gleam@result:'try'(
Parse_file(Resolved_path),
fun(Loaded) ->
gleam@result:'try'(
find_external_schema(
Loaded,
Fragment_name,
Resolved_path
),
fun(Target) ->
Local_ref = {reference,
<<"#/components/schemas/"/utf8,
Fragment_name/binary>>,
Fragment_name},
New_imports = gleam@dict:insert(
Imports,
Fragment_name,
{Resolved_path, Target}
),
{ok, {Local_ref, New_imports}}
end
)
end
)
end
)
end
);
none ->
{ok, {Schema_ref, Imports}}
end.
-file("src/oaspec/openapi/external_loader.gleam", 1396).
?DOC(
" Walk a `Dict(String, Header)` and hoist any external `$ref` found\n"
" on `Header.schema`. Headers are not `RefOr(Header)`, so no\n"
" placeholder handling — just iterate the dict and rewrite each\n"
" entry that has a schema.\n"
).
-spec rewrite_header_map(
gleam@dict:dict(binary(), oaspec@openapi@spec:header()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{gleam@dict:dict(binary(), oaspec@openapi@spec:header()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_header_map(Headers, Base_dir, Parse_file, Imports, Original_local_names) ->
Entries = maps:to_list(Headers),
gleam@result:'try'(
gleam@list:try_fold(
Entries,
{[], Imports},
fun(Acc, Entry) ->
{Collected, Imports@1} = Acc,
{Name, Header} = Entry,
case erlang:element(4, Header) of
{some, Ref} ->
gleam@result:'try'(
maybe_hoist_ref(
Ref,
Base_dir,
Parse_file,
Imports@1,
Original_local_names
),
fun(_use0) ->
{New_ref, New_imports} = _use0,
New_header = {header,
erlang:element(2, Header),
erlang:element(3, Header),
{some, New_ref}},
{ok,
{[{Name, New_header} | Collected],
New_imports}}
end
);
none ->
{ok, {[{Name, Header} | Collected], Imports@1}}
end
end
),
fun(_use0@1) ->
{Rewritten, New_imports@1} = _use0@1,
New_map = gleam@list:fold(
Rewritten,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
{ok, {New_map, New_imports@1}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1438).
?DOC(
" Walk `components.headers` (plain `Dict(String, Header)`, not\n"
" `RefOr`) and hoist any external `$ref` found on `Header.schema`.\n"
" Runs after the other components-side passes so imported schemas\n"
" continue to flow into `components.schemas`.\n"
).
-spec process_component_headers(
oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
list(binary()),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()})
) -> {ok,
{oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
process_component_headers(
Components,
Base_dir,
Parse_file,
Original_local_names,
Seeded_imports
) ->
gleam@result:'try'(
rewrite_header_map(
erlang:element(8, Components),
Base_dir,
Parse_file,
Seeded_imports,
Original_local_names
),
fun(_use0) ->
{New_headers, Final_imports} = _use0,
New_schemas = gleam@dict:fold(
Final_imports,
erlang:element(2, Components),
fun(D, Frag_name, Pair) ->
{_, Target_schema} = Pair,
case {gleam@dict:has_key(D, Frag_name), Target_schema} of
{true, _} ->
D;
{false, {inline, _}} ->
gleam@dict:insert(D, Frag_name, Target_schema);
{false, _} ->
D
end
end
),
{ok,
{{components,
New_schemas,
erlang:element(3, Components),
erlang:element(4, Components),
erlang:element(5, Components),
erlang:element(6, Components),
erlang:element(7, Components),
New_headers,
erlang:element(9, Components),
erlang:element(10, Components)},
Final_imports}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1527).
?DOC(
" Walk every MediaType in a content-type dict and hoist any external ref\n"
" found on `MediaType.schema`. MediaType values with no schema pass\n"
" through unchanged.\n"
).
-spec rewrite_media_type_map(
gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_media_type_map(
Content,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
Entries = maps:to_list(Content),
gleam@result:'try'(
gleam@list:try_fold(
Entries,
{[], Imports},
fun(Acc, Entry) ->
{Collected, Imports@1} = Acc,
{Media_key, Media} = Entry,
case erlang:element(2, Media) of
{some, Ref} ->
gleam@result:'try'(
maybe_hoist_ref(
Ref,
Base_dir,
Parse_file,
Imports@1,
Original_local_names
),
fun(_use0) ->
{New_ref, New_imports} = _use0,
New_media = {media_type,
{some, New_ref},
erlang:element(3, Media),
erlang:element(4, Media),
erlang:element(5, Media)},
{ok,
{[{Media_key, New_media} | Collected],
New_imports}}
end
);
none ->
{ok, {[{Media_key, Media} | Collected], Imports@1}}
end
end
),
fun(_use0@1) ->
{Rewritten, New_imports@1} = _use0@1,
New_content = gleam@list:fold(
Rewritten,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
{ok, {New_content, New_imports@1}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1097).
-spec rewrite_optional_request_body(
gleam@option:option(oaspec@openapi@spec:ref_or(oaspec@openapi@spec:request_body(oaspec@openapi@spec:unresolved()))),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{gleam@option:option(oaspec@openapi@spec:ref_or(oaspec@openapi@spec:request_body(oaspec@openapi@spec:unresolved()))),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_optional_request_body(
Body,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
case Body of
{some, {value, Rb}} ->
gleam@result:'try'(
rewrite_media_type_map(
erlang:element(3, Rb),
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_content, New_imports} = _use0,
New_rb = {request_body,
erlang:element(2, Rb),
New_content,
erlang:element(4, Rb)},
{ok, {{some, {value, New_rb}}, New_imports}}
end
);
{some, {ref, _}} ->
{ok, {Body, Imports}};
none ->
{ok, {none, Imports}}
end.
-file("src/oaspec/openapi/external_loader.gleam", 1311).
?DOC(
" Rewrite a single `RefOr(Parameter)` by hoisting external refs inside\n"
" either a `ParameterSchema` or a `ParameterContent` payload. `Ref`\n"
" entries (external parameter-object refs) pass through unchanged.\n"
).
-spec rewrite_ref_or_parameter(
oaspec@openapi@spec:ref_or(oaspec@openapi@spec:parameter(oaspec@openapi@spec:unresolved())),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{oaspec@openapi@spec:ref_or(oaspec@openapi@spec:parameter(oaspec@openapi@spec:unresolved())),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_ref_or_parameter(
Ref_or_param,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
case Ref_or_param of
{value, P} ->
case erlang:element(6, P) of
{parameter_schema, Schema_ref} ->
gleam@result:'try'(
maybe_hoist_ref(
Schema_ref,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_ref, New_imports} = _use0,
New_param = {parameter,
erlang:element(2, P),
erlang:element(3, P),
erlang:element(4, P),
erlang:element(5, P),
{parameter_schema, New_ref},
erlang:element(7, P),
erlang:element(8, P),
erlang:element(9, P),
erlang:element(10, P),
erlang:element(11, P)},
{ok, {{value, New_param}, New_imports}}
end
);
{parameter_content, Content} ->
gleam@result:'try'(
rewrite_media_type_map(
Content,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0@1) ->
{New_content, New_imports@1} = _use0@1,
New_param@1 = {parameter,
erlang:element(2, P),
erlang:element(3, P),
erlang:element(4, P),
erlang:element(5, P),
{parameter_content, New_content},
erlang:element(7, P),
erlang:element(8, P),
erlang:element(9, P),
erlang:element(10, P),
erlang:element(11, P)},
{ok, {{value, New_param@1}, New_imports@1}}
end
)
end;
{ref, _} ->
{ok, {Ref_or_param, Imports}}
end.
-file("src/oaspec/openapi/external_loader.gleam", 784).
?DOC(
" Rewrite each `RefOr(Parameter)` in a parameter list, threading the\n"
" imports tracker so collisions across operation-level and components\n"
" refs remain visible.\n"
).
-spec rewrite_parameter_list(
list(oaspec@openapi@spec:ref_or(oaspec@openapi@spec:parameter(oaspec@openapi@spec:unresolved()))),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{list(oaspec@openapi@spec:ref_or(oaspec@openapi@spec:parameter(oaspec@openapi@spec:unresolved()))),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_parameter_list(
Parameters,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
gleam@result:'try'(
gleam@list:try_fold(
Parameters,
{[], Imports},
fun(Acc, Item) ->
{Done, Imports@1} = Acc,
gleam@result:'try'(
rewrite_ref_or_parameter(
Item,
Base_dir,
Parse_file,
Imports@1,
Original_local_names
),
fun(_use0) ->
{New_item, New_imports} = _use0,
{ok, {[New_item | Done], New_imports}}
end
)
end
),
fun(_use0@1) ->
{Collected, New_imports@1} = _use0@1,
{ok, {lists:reverse(Collected), New_imports@1}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1175).
?DOC(
" Walk `components.parameters`: for each inline `Parameter` whose payload is\n"
" a `ParameterSchema(SchemaRef)` pointing at a relative-file external ref,\n"
" hoist the target schema into `components.schemas` and rewrite the inner\n"
" ref. Parameters expressed via `content` media type maps, `Ref` placeholders\n"
" pointing at other parameter objects, and local refs all pass through.\n"
).
-spec process_parameter_schemas(
oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
list(binary()),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()})
) -> {ok,
{oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
process_parameter_schemas(
Components,
Base_dir,
Parse_file,
Original_local_names,
Seeded_imports
) ->
Entries = maps:to_list(erlang:element(3, Components)),
gleam@result:'try'(
gleam@list:try_fold(
Entries,
{[], Seeded_imports},
fun(Acc, Entry) ->
{Collected, Imports} = Acc,
{Name, Ref_or_param} = Entry,
gleam@result:'try'(
rewrite_ref_or_parameter(
Ref_or_param,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_param, New_imports} = _use0,
{ok, {[{Name, New_param} | Collected], New_imports}}
end
)
end
),
fun(_use0@1) ->
{Rewritten, Imports@1} = _use0@1,
New_parameters = gleam@list:fold(
Rewritten,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
New_schemas = gleam@dict:fold(
Imports@1,
erlang:element(2, Components),
fun(D@1, Frag_name, Pair@1) ->
{_, Target_schema} = Pair@1,
case {gleam@dict:has_key(D@1, Frag_name), Target_schema} of
{true, _} ->
D@1;
{false, {inline, _}} ->
gleam@dict:insert(D@1, Frag_name, Target_schema);
{false, _} ->
D@1
end
end
),
{ok,
{{components,
New_schemas,
New_parameters,
erlang:element(4, Components),
erlang:element(5, Components),
erlang:element(6, Components),
erlang:element(7, Components),
erlang:element(8, Components),
erlang:element(9, Components),
erlang:element(10, Components)},
Imports@1}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1360).
?DOC(
" Rewrite both the `content` media map and the `headers` map of a\n"
" single Response value. Shared by the components-side and operation-\n"
" side response walkers so header schemas get the same external-ref\n"
" treatment as body schemas.\n"
).
-spec rewrite_response_value(
oaspec@openapi@spec:response(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{oaspec@openapi@spec:response(oaspec@openapi@spec:unresolved()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_response_value(
Response,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
gleam@result:'try'(
rewrite_media_type_map(
erlang:element(3, Response),
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_content, Imports_after_content} = _use0,
gleam@result:'try'(
rewrite_header_map(
erlang:element(4, Response),
Base_dir,
Parse_file,
Imports_after_content,
Original_local_names
),
fun(_use0@1) ->
{New_headers, New_imports} = _use0@1,
{ok,
{{response,
erlang:element(2, Response),
New_content,
New_headers,
erlang:element(5, Response)},
New_imports}}
end
)
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1127).
-spec rewrite_response_map(
gleam@dict:dict(oaspec@util@http:http_status_code(), oaspec@openapi@spec:ref_or(oaspec@openapi@spec:response(oaspec@openapi@spec:unresolved()))),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{gleam@dict:dict(oaspec@util@http:http_status_code(), oaspec@openapi@spec:ref_or(oaspec@openapi@spec:response(oaspec@openapi@spec:unresolved()))),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_response_map(
Responses,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
Entries = maps:to_list(Responses),
gleam@result:'try'(
gleam@list:try_fold(
Entries,
{[], Imports},
fun(Acc, Entry) ->
{Collected, Imports@1} = Acc,
{Status, Ref_or_resp} = Entry,
case Ref_or_resp of
{value, Resp} ->
gleam@result:'try'(
rewrite_response_value(
Resp,
Base_dir,
Parse_file,
Imports@1,
Original_local_names
),
fun(_use0) ->
{New_resp, New_imports} = _use0,
{ok,
{[{Status, {value, New_resp}} | Collected],
New_imports}}
end
);
{ref, _} ->
{ok, {[{Status, Ref_or_resp} | Collected], Imports@1}}
end
end
),
fun(_use0@1) ->
{Rewritten, New_imports@1} = _use0@1,
New_map = gleam@list:fold(
Rewritten,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
{ok, {New_map, New_imports@1}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1229).
?DOC(
" Walk `components.request_bodies` and `components.responses`, hoisting\n"
" any external `$ref` found on a `MediaType.schema` field inside the\n"
" `content` dict. The imports tracker is shared with earlier passes so\n"
" cross-file collisions across every component kind stay honest.\n"
" Entries whose top-level value is a `Ref(...)` (reference to an\n"
" external body/response object) pass through untouched — handling those\n"
" requires hoisting the body/response itself, not just its schema.\n"
).
-spec process_body_response_schemas(
oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
list(binary()),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()})
) -> {ok,
{oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
process_body_response_schemas(
Components,
Base_dir,
Parse_file,
Original_local_names,
Seeded_imports
) ->
Body_entries = maps:to_list(erlang:element(4, Components)),
gleam@result:'try'(
gleam@list:try_fold(
Body_entries,
{[], Seeded_imports},
fun(Acc, Entry) ->
{Collected, Imports} = Acc,
{Name, Ref_or_body} = Entry,
case Ref_or_body of
{value, Body} ->
gleam@result:'try'(
rewrite_media_type_map(
erlang:element(3, Body),
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_content, New_imports} = _use0,
New_body = {request_body,
erlang:element(2, Body),
New_content,
erlang:element(4, Body)},
{ok,
{[{Name, {value, New_body}} | Collected],
New_imports}}
end
);
{ref, _} ->
{ok, {[{Name, Ref_or_body} | Collected], Imports}}
end
end
),
fun(_use0@1) ->
{New_bodies, Imports_after_bodies} = _use0@1,
Response_entries = maps:to_list(erlang:element(5, Components)),
gleam@result:'try'(
gleam@list:try_fold(
Response_entries,
{[], Imports_after_bodies},
fun(Acc@1, Entry@1) ->
{Collected@1, Imports@1} = Acc@1,
{Name@1, Ref_or_resp} = Entry@1,
case Ref_or_resp of
{value, Resp} ->
gleam@result:'try'(
rewrite_response_value(
Resp,
Base_dir,
Parse_file,
Imports@1,
Original_local_names
),
fun(_use0@2) ->
{New_resp, New_imports@1} = _use0@2,
{ok,
{[{Name@1, {value, New_resp}} |
Collected@1],
New_imports@1}}
end
);
{ref, _} ->
{ok,
{[{Name@1, Ref_or_resp} | Collected@1],
Imports@1}}
end
end
),
fun(_use0@3) ->
{New_responses, Final_imports} = _use0@3,
New_request_bodies = gleam@list:fold(
New_bodies,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
New_responses_dict = gleam@list:fold(
New_responses,
maps:new(),
fun(D@1, Pair@1) ->
gleam@dict:insert(
D@1,
erlang:element(1, Pair@1),
erlang:element(2, Pair@1)
)
end
),
New_schemas = gleam@dict:fold(
Final_imports,
erlang:element(2, Components),
fun(D@2, Frag_name, Pair@2) ->
{_, Target_schema} = Pair@2,
case {gleam@dict:has_key(D@2, Frag_name),
Target_schema} of
{true, _} ->
D@2;
{false, {inline, _}} ->
gleam@dict:insert(
D@2,
Frag_name,
Target_schema
);
{false, _} ->
D@2
end
end
),
{ok,
{{components,
New_schemas,
erlang:element(3, Components),
New_request_bodies,
New_responses_dict,
erlang:element(6, Components),
erlang:element(7, Components),
erlang:element(8, Components),
erlang:element(9, Components),
erlang:element(10, Components)},
Final_imports}}
end
)
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1569).
?DOC(
" Walk the properties dict of an ObjectSchema; for each property that is a\n"
" relative-file `$ref`, resolve it, stage the target schema for merging,\n"
" and rewrite the property to a local ref. Non-external property refs pass\n"
" through unchanged.\n"
).
-spec rewrite_object_properties(
gleam@dict:dict(binary(), oaspec@openapi@schema:schema_ref()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{gleam@dict:dict(binary(), oaspec@openapi@schema:schema_ref()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_object_properties(
Properties,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
Entries = maps:to_list(Properties),
gleam@result:'try'(
gleam@list:try_fold(
Entries,
{[], Imports},
fun(Acc, Entry) ->
{Prop_acc, Imports@1} = Acc,
{Prop_name, Prop_ref} = Entry,
gleam@result:'try'(
maybe_hoist_ref(
Prop_ref,
Base_dir,
Parse_file,
Imports@1,
Original_local_names
),
fun(_use0) ->
{New_ref, New_imports} = _use0,
{ok, {[{Prop_name, New_ref} | Prop_acc], New_imports}}
end
)
end
),
fun(_use0@1) ->
{New_entries, New_imports@1} = _use0@1,
New_properties = gleam@list:fold(
New_entries,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
{ok, {New_properties, New_imports@1}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1605).
?DOC(
" Walk an ObjectSchema's `additionalProperties` value; when it is a\n"
" `Typed(SchemaRef)` whose ref is external, hoist the target schema and\n"
" rewrite the inner ref to a local reference. `Forbidden` and `Untyped`\n"
" pass through untouched.\n"
).
-spec rewrite_additional_properties(
oaspec@openapi@schema:additional_properties(),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{oaspec@openapi@schema:additional_properties(),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_additional_properties(
Additional,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
case Additional of
{typed, Ref} ->
gleam@result:'try'(
maybe_hoist_ref(
Ref,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_ref, New_imports} = _use0,
{ok, {{typed, New_ref}, New_imports}}
end
);
_ ->
{ok, {Additional, Imports}}
end.
-file("src/oaspec/openapi/external_loader.gleam", 1634).
?DOC(
" Walk a `List(SchemaRef)` produced by an allOf/oneOf/anyOf composition,\n"
" hoisting any relative-file external ref the same way property values\n"
" and array items are handled. Branches that are inline or local refs\n"
" pass through unchanged.\n"
).
-spec rewrite_schema_list(
list(oaspec@openapi@schema:schema_ref()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{list(oaspec@openapi@schema:schema_ref()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_schema_list(
Branches,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
gleam@result:'try'(
gleam@list:try_fold(
Branches,
{[], Imports},
fun(Acc, Branch) ->
{Collected, Imports@1} = Acc,
gleam@result:'try'(
maybe_hoist_ref(
Branch,
Base_dir,
Parse_file,
Imports@1,
Original_local_names
),
fun(_use0) ->
{New_branch, New_imports} = _use0,
{ok, {[New_branch | Collected], New_imports}}
end
)
end
),
fun(_use0@1) ->
{Rewritten, New_imports@1} = _use0@1,
{ok, {lists:reverse(Rewritten), New_imports@1}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 572).
?DOC(
" Walk each `components.schemas` entry that is an `Inline(ObjectSchema)` and\n"
" hoist any property whose value is a relative-file `$ref`. The referenced\n"
" schema is added to `components.schemas` under its fragment name, and the\n"
" property is rewritten to a local `#/components/schemas/<fragment>` ref.\n"
"\n"
" Runs after `process_components` so top-level external refs are already\n"
" merged in. Collisions are surfaced rather than silently resolved:\n"
" - if the nested fragment name matches a schema that was originally\n"
" authored inline in the main spec, error (silent-shadowing guard);\n"
" - if two refs pull the same fragment name from different source files\n"
" (whether nested or top-level), error;\n"
" - same-file re-imports remain idempotent.\n"
).
-spec process_nested_property_refs(
oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
list(binary()),
gleam@dict:dict(binary(), binary())
) -> {ok,
{oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
process_nested_property_refs(
Components,
Base_dir,
Parse_file,
Original_local_names,
Top_imported
) ->
Entries = maps:to_list(erlang:element(2, Components)),
Seeded_imports = gleam@dict:fold(
Top_imported,
maps:new(),
fun(D, Frag_name, Source_path) ->
gleam@dict:insert(D, Frag_name, {Source_path, no_target()})
end
),
gleam@result:'try'(
gleam@list:try_fold(
Entries,
{[], Seeded_imports},
fun(Acc, Entry) ->
{Rewritten_acc, Imports} = Acc,
{Name, Schema_ref} = Entry,
case Schema_ref of
{inline,
{object_schema,
Metadata,
Properties,
Required,
Additional_properties,
Min_properties,
Max_properties}} ->
gleam@result:'try'(
rewrite_object_properties(
Properties,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_properties, Props_imports} = _use0,
gleam@result:'try'(
rewrite_additional_properties(
Additional_properties,
Base_dir,
Parse_file,
Props_imports,
Original_local_names
),
fun(_use0@1) ->
{New_additional_properties, New_imports} = _use0@1,
New_obj = {object_schema,
Metadata,
New_properties,
Required,
New_additional_properties,
Min_properties,
Max_properties},
{ok,
{[{Name, {inline, New_obj}} |
Rewritten_acc],
New_imports}}
end
)
end
);
{inline,
{array_schema,
Metadata@1,
Items,
Min_items,
Max_items,
Unique_items}} ->
gleam@result:'try'(
maybe_hoist_ref(
Items,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0@2) ->
{New_items, New_imports@1} = _use0@2,
New_arr = {array_schema,
Metadata@1,
New_items,
Min_items,
Max_items,
Unique_items},
{ok,
{[{Name, {inline, New_arr}} | Rewritten_acc],
New_imports@1}}
end
);
{inline, {all_of_schema, Metadata@2, Branches}} ->
gleam@result:'try'(
rewrite_schema_list(
Branches,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0@3) ->
{New_branches, New_imports@2} = _use0@3,
New_all = {all_of_schema,
Metadata@2,
New_branches},
{ok,
{[{Name, {inline, New_all}} | Rewritten_acc],
New_imports@2}}
end
);
{inline,
{one_of_schema, Metadata@3, Branches@1, Discriminator}} ->
gleam@result:'try'(
rewrite_schema_list(
Branches@1,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0@4) ->
{New_branches@1, New_imports@3} = _use0@4,
New_one = {one_of_schema,
Metadata@3,
New_branches@1,
Discriminator},
{ok,
{[{Name, {inline, New_one}} | Rewritten_acc],
New_imports@3}}
end
);
{inline,
{any_of_schema, Metadata@4, Branches@2, Discriminator@1}} ->
gleam@result:'try'(
rewrite_schema_list(
Branches@2,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0@5) ->
{New_branches@2, New_imports@4} = _use0@5,
New_any = {any_of_schema,
Metadata@4,
New_branches@2,
Discriminator@1},
{ok,
{[{Name, {inline, New_any}} | Rewritten_acc],
New_imports@4}}
end
);
_ ->
{ok, {[{Name, Schema_ref} | Rewritten_acc], Imports}}
end
end
),
fun(_use0@6) ->
{Rewritten, Imports@1} = _use0@6,
Merged = gleam@list:fold(
Rewritten,
maps:new(),
fun(D@1, Pair) ->
gleam@dict:insert(
D@1,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
Merged@1 = gleam@dict:fold(
Imports@1,
Merged,
fun(D@2, Frag_name@1, Pair@1) ->
{_, Target_schema} = Pair@1,
case {gleam@dict:has_key(D@2, Frag_name@1), Target_schema} of
{true, _} ->
D@2;
{false, {inline, _}} ->
gleam@dict:insert(D@2, Frag_name@1, Target_schema);
{false, _} ->
D@2
end
end
),
{ok,
{{components,
Merged@1,
erlang:element(3, Components),
erlang:element(4, Components),
erlang:element(5, Components),
erlang:element(6, Components),
erlang:element(7, Components),
erlang:element(8, Components),
erlang:element(9, Components),
erlang:element(10, Components)},
Imports@1}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1970).
?DOC(
" Helper used by `parser.parse_file` to compute a spec's base directory.\n"
" Empty-string (current working directory) is returned when the filepath\n"
" module can't extract a parent — callers can then pass `Some(\"\")` which\n"
" resolves relative refs against CWD.\n"
).
-spec base_dir_of(binary()) -> gleam@option:option(binary()).
base_dir_of(Path) ->
case filepath:directory_name(Path) of
<<""/utf8>> ->
{some, <<"."/utf8>>};
Dir ->
{some, Dir}
end.
-file("src/oaspec/openapi/external_loader.gleam", 1054).
?DOC(
" Rewrite each PathItem inside a Callback.entries dict. The entries\n"
" map URL-expression strings to `RefOr(PathItem)`; inline PathItems\n"
" recurse via `rewrite_path_item`, Ref entries pass through.\n"
).
-spec rewrite_callback_entries(
gleam@dict:dict(binary(), oaspec@openapi@spec:ref_or(oaspec@openapi@spec:path_item(oaspec@openapi@spec:unresolved()))),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{gleam@dict:dict(binary(), oaspec@openapi@spec:ref_or(oaspec@openapi@spec:path_item(oaspec@openapi@spec:unresolved()))),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_callback_entries(
Entries,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
Pairs = maps:to_list(Entries),
gleam@result:'try'(
gleam@list:try_fold(
Pairs,
{[], Imports},
fun(Acc, Entry) ->
{Collected, Imports@1} = Acc,
{Url_expr, Ref_or_item} = Entry,
case Ref_or_item of
{value, Path_item} ->
gleam@result:'try'(
rewrite_path_item(
Path_item,
Base_dir,
Parse_file,
Imports@1,
Original_local_names
),
fun(_use0) ->
{New_path_item, New_imports} = _use0,
{ok,
{[{Url_expr, {value, New_path_item}} |
Collected],
New_imports}}
end
);
{ref, _} ->
{ok, {[{Url_expr, Ref_or_item} | Collected], Imports@1}}
end
end
),
fun(_use0@1) ->
{Rewritten, New_imports@1} = _use0@1,
New_map = gleam@list:fold(
Rewritten,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
{ok, {New_map, New_imports@1}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 816).
?DOC(
" Rewrite both the path-level `parameters` list and every populated\n"
" method slot on a `PathItem`. Shared by the top-level paths walker\n"
" and the callback walker so callbacks recurse into the same helper.\n"
).
-spec rewrite_path_item(
oaspec@openapi@spec:path_item(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{oaspec@openapi@spec:path_item(oaspec@openapi@spec:unresolved()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_path_item(
Path_item,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
gleam@result:'try'(
rewrite_parameter_list(
erlang:element(12, Path_item),
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_parameters, Imports_after_params} = _use0,
rewrite_path_item_methods(
{path_item,
erlang:element(2, Path_item),
erlang:element(3, Path_item),
erlang:element(4, Path_item),
erlang:element(5, Path_item),
erlang:element(6, Path_item),
erlang:element(7, Path_item),
erlang:element(8, Path_item),
erlang:element(9, Path_item),
erlang:element(10, Path_item),
erlang:element(11, Path_item),
New_parameters,
erlang:element(13, Path_item)},
Base_dir,
Parse_file,
Imports_after_params,
Original_local_names
)
end
).
-file("src/oaspec/openapi/external_loader.gleam", 847).
?DOC(
" Rewrite every populated HTTP-method slot on a `PathItem`. Each inline\n"
" `Operation` has its own `parameters`, `request_body`, and `responses`\n"
" dicts rewritten.\n"
).
-spec rewrite_path_item_methods(
oaspec@openapi@spec:path_item(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{oaspec@openapi@spec:path_item(oaspec@openapi@spec:unresolved()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_path_item_methods(
Path_item,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
gleam@result:'try'(
rewrite_optional_operation(
erlang:element(4, Path_item),
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{Get_op, Imports1} = _use0,
gleam@result:'try'(
rewrite_optional_operation(
erlang:element(5, Path_item),
Base_dir,
Parse_file,
Imports1,
Original_local_names
),
fun(_use0@1) ->
{Post_op, Imports2} = _use0@1,
gleam@result:'try'(
rewrite_optional_operation(
erlang:element(6, Path_item),
Base_dir,
Parse_file,
Imports2,
Original_local_names
),
fun(_use0@2) ->
{Put_op, Imports3} = _use0@2,
gleam@result:'try'(
rewrite_optional_operation(
erlang:element(7, Path_item),
Base_dir,
Parse_file,
Imports3,
Original_local_names
),
fun(_use0@3) ->
{Delete_op, Imports4} = _use0@3,
gleam@result:'try'(
rewrite_optional_operation(
erlang:element(8, Path_item),
Base_dir,
Parse_file,
Imports4,
Original_local_names
),
fun(_use0@4) ->
{Patch_op, Imports5} = _use0@4,
gleam@result:'try'(
rewrite_optional_operation(
erlang:element(9, Path_item),
Base_dir,
Parse_file,
Imports5,
Original_local_names
),
fun(_use0@5) ->
{Head_op, Imports6} = _use0@5,
gleam@result:'try'(
rewrite_optional_operation(
erlang:element(
10,
Path_item
),
Base_dir,
Parse_file,
Imports6,
Original_local_names
),
fun(_use0@6) ->
{Options_op,
Imports7} = _use0@6,
gleam@result:'try'(
rewrite_optional_operation(
erlang:element(
11,
Path_item
),
Base_dir,
Parse_file,
Imports7,
Original_local_names
),
fun(_use0@7) ->
{Trace_op,
Final_imports} = _use0@7,
{ok,
{{path_item,
erlang:element(
2,
Path_item
),
erlang:element(
3,
Path_item
),
Get_op,
Post_op,
Put_op,
Delete_op,
Patch_op,
Head_op,
Options_op,
Trace_op,
erlang:element(
12,
Path_item
),
erlang:element(
13,
Path_item
)},
Final_imports}}
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/oaspec/openapi/external_loader.gleam", 929).
-spec rewrite_optional_operation(
gleam@option:option(oaspec@openapi@spec:operation(oaspec@openapi@spec:unresolved())),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{gleam@option:option(oaspec@openapi@spec:operation(oaspec@openapi@spec:unresolved())),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_optional_operation(
Op,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
case Op of
{some, Operation} ->
gleam@result:'try'(
rewrite_operation(
Operation,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_op, New_imports} = _use0,
{ok, {{some, New_op}, New_imports}}
end
);
none ->
{ok, {none, Imports}}
end.
-file("src/oaspec/openapi/external_loader.gleam", 957).
-spec rewrite_operation(
oaspec@openapi@spec:operation(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{oaspec@openapi@spec:operation(oaspec@openapi@spec:unresolved()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_operation(
Operation,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
gleam@result:'try'(
rewrite_parameter_list(
erlang:element(6, Operation),
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_params, Imports_after_params} = _use0,
gleam@result:'try'(
rewrite_optional_request_body(
erlang:element(7, Operation),
Base_dir,
Parse_file,
Imports_after_params,
Original_local_names
),
fun(_use0@1) ->
{New_body, Imports_after_body} = _use0@1,
gleam@result:'try'(
rewrite_response_map(
erlang:element(8, Operation),
Base_dir,
Parse_file,
Imports_after_body,
Original_local_names
),
fun(_use0@2) ->
{New_responses, Imports_after_responses} = _use0@2,
gleam@result:'try'(
rewrite_callback_map(
erlang:element(11, Operation),
Base_dir,
Parse_file,
Imports_after_responses,
Original_local_names
),
fun(_use0@3) ->
{New_callbacks, Final_imports} = _use0@3,
{ok,
{{operation,
erlang:element(2, Operation),
erlang:element(3, Operation),
erlang:element(4, Operation),
erlang:element(5, Operation),
New_params,
New_body,
New_responses,
erlang:element(9, Operation),
erlang:element(10, Operation),
New_callbacks,
erlang:element(12, Operation),
erlang:element(13, Operation)},
Final_imports}}
end
)
end
)
end
)
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1015).
?DOC(
" Walk `operation.callbacks`: for each callback, rewrite every inline\n"
" PathItem in its `entries` dict via the same `rewrite_path_item`\n"
" helper used at the top level. `Ref` entries pointing at external\n"
" PathItem objects pass through unchanged.\n"
).
-spec rewrite_callback_map(
gleam@dict:dict(binary(), oaspec@openapi@spec:callback(oaspec@openapi@spec:unresolved())),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()}),
list(binary())
) -> {ok,
{gleam@dict:dict(binary(), oaspec@openapi@spec:callback(oaspec@openapi@spec:unresolved())),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
rewrite_callback_map(
Callbacks,
Base_dir,
Parse_file,
Imports,
Original_local_names
) ->
Entries = maps:to_list(Callbacks),
gleam@result:'try'(
gleam@list:try_fold(
Entries,
{[], Imports},
fun(Acc, Entry) ->
{Collected, Imports@1} = Acc,
{Name, Callback} = Entry,
gleam@result:'try'(
rewrite_callback_entries(
erlang:element(2, Callback),
Base_dir,
Parse_file,
Imports@1,
Original_local_names
),
fun(_use0) ->
{New_entries, New_imports} = _use0,
New_callback = {callback, New_entries},
{ok, {[{Name, New_callback} | Collected], New_imports}}
end
)
end
),
fun(_use0@1) ->
{Rewritten, New_imports@1} = _use0@1,
New_callbacks = gleam@list:fold(
Rewritten,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
{ok, {New_callbacks, New_imports@1}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 728).
?DOC(
" Walk `spec.paths`: for each inline `PathItem`, rewrite its path-level\n"
" `parameters` list, every populated HTTP method `Operation` (via\n"
" `rewrite_operation`), and feed any newly imported schemas into\n"
" `components.schemas`. `RefOr.Ref` path items passing pointers to\n"
" external files are left untouched.\n"
).
-spec process_operation_schemas(
gleam@dict:dict(binary(), oaspec@openapi@spec:ref_or(oaspec@openapi@spec:path_item(oaspec@openapi@spec:unresolved()))),
oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
list(binary()),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()})
) -> {ok,
{gleam@dict:dict(binary(), oaspec@openapi@spec:ref_or(oaspec@openapi@spec:path_item(oaspec@openapi@spec:unresolved()))),
oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved())}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
process_operation_schemas(
Paths,
Components,
Base_dir,
Parse_file,
Original_local_names,
Seeded_imports
) ->
Entries = maps:to_list(Paths),
gleam@result:'try'(
gleam@list:try_fold(
Entries,
{[], Seeded_imports},
fun(Acc, Entry) ->
{Collected, Imports} = Acc,
{Path_key, Ref_or_item} = Entry,
case Ref_or_item of
{value, Path_item} ->
gleam@result:'try'(
rewrite_path_item(
Path_item,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_path_item, New_imports} = _use0,
{ok,
{[{Path_key, {value, New_path_item}} |
Collected],
New_imports}}
end
);
{ref, _} ->
{ok, {[{Path_key, Ref_or_item} | Collected], Imports}}
end
end
),
fun(_use0@1) ->
{Rewritten, Final_imports} = _use0@1,
New_paths = gleam@list:fold(
Rewritten,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
New_schemas = gleam@dict:fold(
Final_imports,
erlang:element(2, Components),
fun(D@1, Frag_name, Pair@1) ->
{_, Target_schema} = Pair@1,
case {gleam@dict:has_key(D@1, Frag_name), Target_schema} of
{true, _} ->
D@1;
{false, {inline, _}} ->
gleam@dict:insert(D@1, Frag_name, Target_schema);
{false, _} ->
D@1
end
end
),
{ok,
{New_paths,
{components,
New_schemas,
erlang:element(3, Components),
erlang:element(4, Components),
erlang:element(5, Components),
erlang:element(6, Components),
erlang:element(7, Components),
erlang:element(8, Components),
erlang:element(9, Components),
erlang:element(10, Components)}}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 1475).
?DOC(
" Walk `components.path_items`: for each inline `PathItem`, rewrite\n"
" its parameters, method operations, and callbacks via the shared\n"
" `rewrite_path_item` helper — the same one used for top-level\n"
" `spec.paths` and callback PathItems. `Ref` entries pointing at\n"
" external PathItem objects pass through unchanged.\n"
).
-spec process_component_path_items(
oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
binary(),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}),
list(binary()),
gleam@dict:dict(binary(), {binary(), oaspec@openapi@schema:schema_ref()})
) -> {ok,
{oaspec@openapi@spec:components(oaspec@openapi@spec:unresolved()),
gleam@dict:dict(binary(), {binary(),
oaspec@openapi@schema:schema_ref()})}} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
process_component_path_items(
Components,
Base_dir,
Parse_file,
Original_local_names,
Seeded_imports
) ->
Entries = maps:to_list(erlang:element(7, Components)),
gleam@result:'try'(
gleam@list:try_fold(
Entries,
{[], Seeded_imports},
fun(Acc, Entry) ->
{Collected, Imports} = Acc,
{Name, Ref_or_item} = Entry,
case Ref_or_item of
{value, Path_item} ->
gleam@result:'try'(
rewrite_path_item(
Path_item,
Base_dir,
Parse_file,
Imports,
Original_local_names
),
fun(_use0) ->
{New_path_item, New_imports} = _use0,
{ok,
{[{Name, {value, New_path_item}} |
Collected],
New_imports}}
end
);
{ref, _} ->
{ok, {[{Name, Ref_or_item} | Collected], Imports}}
end
end
),
fun(_use0@1) ->
{Rewritten, Final_imports} = _use0@1,
New_path_items = gleam@list:fold(
Rewritten,
maps:new(),
fun(D, Pair) ->
gleam@dict:insert(
D,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
),
New_schemas = gleam@dict:fold(
Final_imports,
erlang:element(2, Components),
fun(D@1, Frag_name, Pair@1) ->
{_, Target_schema} = Pair@1,
case {gleam@dict:has_key(D@1, Frag_name), Target_schema} of
{true, _} ->
D@1;
{false, {inline, _}} ->
gleam@dict:insert(D@1, Frag_name, Target_schema);
{false, _} ->
D@1
end
end
),
{ok,
{{components,
New_schemas,
erlang:element(3, Components),
erlang:element(4, Components),
erlang:element(5, Components),
erlang:element(6, Components),
New_path_items,
erlang:element(8, Components),
erlang:element(9, Components),
erlang:element(10, Components)},
Final_imports}}
end
).
-file("src/oaspec/openapi/external_loader.gleam", 71).
?DOC(
" Load every `components.schemas` entry whose value is an external\n"
" filesystem ref, merge the referenced schema into the main spec, and\n"
" rewrite the entry to a local ref. `base_dir` is the directory of the\n"
" file this spec was loaded from (used to resolve relative paths).\n"
"\n"
" If `base_dir` is None (spec loaded from string), external refs are\n"
" treated as unresolvable and passed through unchanged — downstream\n"
" validation still rejects them.\n"
).
-spec resolve_external_component_refs(
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved()),
gleam@option:option(binary()),
fun((binary()) -> {ok,
oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()})
) -> {ok, oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:unresolved())} |
{error, oaspec@openapi@diagnostic:diagnostic()}.
resolve_external_component_refs(Spec, Base_dir, Parse_file) ->
case {erlang:element(5, Spec), Base_dir} of
{{some, Components}, {some, Dir}} ->
Original_local_names = local_schema_names(
maps:to_list(erlang:element(2, Components))
),
gleam@result:'try'(
process_components(
Components,
Dir,
Parse_file,
Original_local_names
),
fun(_use0) ->
{Top_resolved, Top_imported} = _use0,
gleam@result:'try'(
process_whole_object_refs(Top_resolved, Dir, Parse_file),
fun(Whole_resolved) ->
gleam@result:'try'(
process_nested_property_refs(
Whole_resolved,
Dir,
Parse_file,
Original_local_names,
Top_imported
),
fun(_use0@1) ->
{Nested_resolved, Nested_imports} = _use0@1,
gleam@result:'try'(
process_parameter_schemas(
Nested_resolved,
Dir,
Parse_file,
Original_local_names,
Nested_imports
),
fun(_use0@2) ->
{Param_resolved, Param_imports} = _use0@2,
gleam@result:'try'(
process_body_response_schemas(
Param_resolved,
Dir,
Parse_file,
Original_local_names,
Param_imports
),
fun(_use0@3) ->
{Body_resolved,
Body_imports} = _use0@3,
gleam@result:'try'(
process_component_headers(
Body_resolved,
Dir,
Parse_file,
Original_local_names,
Body_imports
),
fun(_use0@4) ->
{Header_resolved,
Header_imports} = _use0@4,
gleam@result:'try'(
process_component_path_items(
Header_resolved,
Dir,
Parse_file,
Original_local_names,
Header_imports
),
fun(_use0@5) ->
{Path_items_resolved,
Path_items_imports} = _use0@5,
gleam@result:'try'(
process_operation_schemas(
erlang:element(
4,
Spec
),
Path_items_resolved,
Dir,
Parse_file,
Original_local_names,
Path_items_imports
),
fun(
_use0@6
) ->
{New_paths,
Op_final_components} = _use0@6,
{ok,
{open_api_spec,
erlang:element(
2,
Spec
),
erlang:element(
3,
Spec
),
New_paths,
{some,
Op_final_components},
erlang:element(
6,
Spec
),
erlang:element(
7,
Spec
),
erlang:element(
8,
Spec
),
erlang:element(
9,
Spec
),
erlang:element(
10,
Spec
),
erlang:element(
11,
Spec
)}}
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
);
{_, _} ->
{ok, Spec}
end.