Current section

Files

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

src/oaspec@openapi@diagnostic.erl

-module(oaspec@openapi@diagnostic).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/openapi/diagnostic.gleam").
-export([file_error/1, yaml_error/2, parse_limit_exceeded/3, missing_field/3, invalid_value/3, resolve_error/4, capability/6, validation/5, validation_error_both/3, validation_error_server/3, validation_warning_both/3, errors_only/1, warnings_only/1, filter_by_mode/2, to_string/1, render/2, to_short_string/1]).
-export_type([phase/0, severity/0, target/0, source_loc/0, diagnostic/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 phase() :: phase_parse |
phase_normalize |
phase_resolve |
phase_capability_check |
phase_validate |
phase_codegen.
-type severity() :: severity_error | severity_warning.
-type target() :: target_both | target_client | target_server.
-type source_loc() :: {source_loc, integer(), integer()} | no_source_loc.
-type diagnostic() :: {diagnostic,
binary(),
phase(),
severity(),
target(),
binary(),
source_loc(),
binary(),
gleam@option:option(binary())}.
-file("src/oaspec/openapi/diagnostic.gleam", 55).
-spec file_error(binary()) -> diagnostic().
file_error(Detail) ->
{diagnostic,
<<"file_error"/utf8>>,
phase_parse,
severity_error,
target_both,
<<""/utf8>>,
no_source_loc,
Detail,
none}.
-file("src/oaspec/openapi/diagnostic.gleam", 68).
-spec yaml_error(binary(), source_loc()) -> diagnostic().
yaml_error(Detail, Loc) ->
{diagnostic,
<<"yaml_error"/utf8>>,
phase_parse,
severity_error,
target_both,
<<""/utf8>>,
Loc,
Detail,
none}.
-file("src/oaspec/openapi/diagnostic.gleam", 85).
?DOC(
" Parser refused the input because it exceeded a configured DoS\n"
" limit (see `parser.ParseLimits`). The message names the limit, the\n"
" configured cap, and the actual value so callers can either bump\n"
" the limit (when they trust the source) or reject the input.\n"
).
-spec parse_limit_exceeded(binary(), integer(), integer()) -> diagnostic().
parse_limit_exceeded(Limit, Configured, Actual) ->
{diagnostic,
<<"parse_limit_exceeded"/utf8>>,
phase_parse,
severity_error,
target_both,
<<""/utf8>>,
no_source_loc,
<<<<<<<<<<<<"Parser limit '"/utf8, Limit/binary>>/binary,
"' exceeded: configured cap "/utf8>>/binary,
(erlang:integer_to_binary(Configured))/binary>>/binary,
", actual "/utf8>>/binary,
(erlang:integer_to_binary(Actual))/binary>>/binary,
". The input was refused before parsing to bound denial-of-service exposure (issue #553)."/utf8>>,
{some,
<<<<"If the source is trusted, raise '"/utf8, Limit/binary>>/binary,
"' via parser.ParseLimits and call parse_string_with_limits with the larger cap. Untrusted sources should keep the default cap."/utf8>>}}.
-file("src/oaspec/openapi/diagnostic.gleam", 116).
-spec missing_field(binary(), binary(), source_loc()) -> diagnostic().
missing_field(Path, Field, Loc) ->
{diagnostic,
<<"missing_field"/utf8>>,
phase_parse,
severity_error,
target_both,
Path,
Loc,
<<"Missing required field: "/utf8, Field/binary>>,
{some, <<"Check your OpenAPI spec structure."/utf8>>}}.
-file("src/oaspec/openapi/diagnostic.gleam", 133).
-spec invalid_value(binary(), binary(), source_loc()) -> diagnostic().
invalid_value(Path, Detail, Loc) ->
{diagnostic,
<<"invalid_value"/utf8>>,
phase_parse,
severity_error,
target_both,
Path,
Loc,
Detail,
none}.
-file("src/oaspec/openapi/diagnostic.gleam", 154).
-spec resolve_error(
binary(),
binary(),
gleam@option:option(binary()),
source_loc()
) -> diagnostic().
resolve_error(Path, Detail, Hint, Loc) ->
{diagnostic,
<<"resolve_error"/utf8>>,
phase_resolve,
severity_error,
target_both,
Path,
Loc,
Detail,
Hint}.
-file("src/oaspec/openapi/diagnostic.gleam", 181).
?DOC(
" Issue #411: capability checks now thread a YAML `SourceLoc` through\n"
" every diagnostic. Pass `NoSourceLoc` only when the caller genuinely\n"
" has no LocationIndex available (e.g. tests that bypass the parser\n"
" path); production code always has one because it goes through\n"
" `parser.parse_file_with_locations` / `generate.generate_with_locations`.\n"
).
-spec capability(
binary(),
binary(),
severity(),
target(),
gleam@option:option(binary()),
source_loc()
) -> diagnostic().
capability(Path, Detail, Severity, Target, Hint, Loc) ->
{diagnostic,
<<"capability"/utf8>>,
phase_capability_check,
Severity,
Target,
Path,
Loc,
Detail,
Hint}.
-file("src/oaspec/openapi/diagnostic.gleam", 205).
-spec validation(
binary(),
binary(),
severity(),
target(),
gleam@option:option(binary())
) -> diagnostic().
validation(Path, Detail, Severity, Target, Hint) ->
{diagnostic,
<<"validation"/utf8>>,
phase_validate,
Severity,
Target,
Path,
no_source_loc,
Detail,
Hint}.
-file("src/oaspec/openapi/diagnostic.gleam", 232).
?DOC(
" Issue #416: severity / target shortcut builders for the common\n"
" `validation` shapes used in `internal/codegen/validate`. The\n"
" project review noted that the 5-field validation/5 constructor\n"
" gets called 28+ times with the same severity / target pair on\n"
" almost every site; these helpers collapse the common shape and\n"
" let call sites focus on the variable bits (path / detail / hint).\n"
" SeverityError + TargetBoth — the most common shape across\n"
" validate.gleam.\n"
).
-spec validation_error_both(binary(), binary(), gleam@option:option(binary())) -> diagnostic().
validation_error_both(Path, Detail, Hint) ->
validation(Path, Detail, severity_error, target_both, Hint).
-file("src/oaspec/openapi/diagnostic.gleam", 248).
?DOC(
" SeverityError + TargetServer — the second most common shape\n"
" (server-only feature restrictions).\n"
).
-spec validation_error_server(binary(), binary(), gleam@option:option(binary())) -> diagnostic().
validation_error_server(Path, Detail, Hint) ->
validation(Path, Detail, severity_error, target_server, Hint).
-file("src/oaspec/openapi/diagnostic.gleam", 264).
?DOC(
" SeverityWarning + TargetBoth — used when a spec shape is\n"
" supported but the user should know about it.\n"
).
-spec validation_warning_both(binary(), binary(), gleam@option:option(binary())) -> diagnostic().
validation_warning_both(Path, Detail, Hint) ->
validation(Path, Detail, severity_warning, target_both, Hint).
-file("src/oaspec/openapi/diagnostic.gleam", 283).
?DOC(" Filter to only errors (not warnings).\n").
-spec errors_only(list(diagnostic())) -> list(diagnostic()).
errors_only(Issues) ->
gleam@list:filter(
Issues,
fun(E) -> erlang:element(4, E) =:= severity_error end
).
-file("src/oaspec/openapi/diagnostic.gleam", 288).
?DOC(" Filter to only warnings (not errors).\n").
-spec warnings_only(list(diagnostic())) -> list(diagnostic()).
warnings_only(Issues) ->
gleam@list:filter(
Issues,
fun(E) -> erlang:element(4, E) =:= severity_warning end
).
-file("src/oaspec/openapi/diagnostic.gleam", 293).
?DOC(" Filter diagnostics to those relevant for the selected generation mode.\n").
-spec filter_by_mode(list(diagnostic()), oaspec@config:generate_mode()) -> list(diagnostic()).
filter_by_mode(Issues, Mode) ->
case Mode of
client ->
gleam@list:filter(
Issues,
fun(E) -> erlang:element(5, E) /= target_server end
);
server ->
gleam@list:filter(
Issues,
fun(E@1) -> erlang:element(5, E@1) /= target_client end
);
both ->
Issues
end.
-file("src/oaspec/openapi/diagnostic.gleam", 309).
?DOC(" Convert a diagnostic to a human-readable string.\n").
-spec to_string(diagnostic()) -> binary().
to_string(D) ->
Phase_str = case erlang:element(3, D) of
phase_parse ->
<<"Parse"/utf8>>;
phase_normalize ->
<<"Normalize"/utf8>>;
phase_resolve ->
<<"Resolve"/utf8>>;
phase_capability_check ->
<<"CapabilityCheck"/utf8>>;
phase_validate ->
<<"Validate"/utf8>>;
phase_codegen ->
<<"Codegen"/utf8>>
end,
Severity_str = case erlang:element(4, D) of
severity_error ->
<<"Error"/utf8>>;
severity_warning ->
<<"Warning"/utf8>>
end,
Loc_str = case erlang:element(7, D) of
{source_loc, Line, Column} ->
<<<<<<<<" (line "/utf8, (erlang:integer_to_binary(Line))/binary>>/binary,
", column "/utf8>>/binary,
(erlang:integer_to_binary(Column))/binary>>/binary,
")"/utf8>>;
no_source_loc ->
<<""/utf8>>
end,
Pointer_str = case erlang:element(6, D) of
<<""/utf8>> ->
<<""/utf8>>;
P ->
<<" at "/utf8, P/binary>>
end,
Hint_str = case erlang:element(9, D) of
{some, H} ->
<<" "/utf8, H/binary>>;
none ->
<<""/utf8>>
end,
<<<<<<<<<<<<<<<<"["/utf8, Phase_str/binary>>/binary, "] "/utf8>>/binary,
Severity_str/binary>>/binary,
Pointer_str/binary>>/binary,
": "/utf8>>/binary,
(erlang:element(8, D))/binary>>/binary,
Loc_str/binary>>/binary,
Hint_str/binary>>.
-file("src/oaspec/openapi/diagnostic.gleam", 357).
?DOC(
" Render a diagnostic with an editor-clickable `path:line:column:`\n"
" prefix when the spec file path and a `SourceLoc` are both known.\n"
" Falls back to plain `to_string` otherwise.\n"
"\n"
" Issue #411: CI runs that process several specs need a breadcrumb to\n"
" which file produced an error; editors recognise the `path:line:col:`\n"
" prefix and let users jump straight to the offending YAML line.\n"
).
-spec render(diagnostic(), gleam@option:option(binary())) -> binary().
render(D, File_path) ->
case {File_path, erlang:element(7, D)} of
{{some, Path}, {source_loc, Line, Column}} ->
<<<<<<<<<<<<Path/binary, ":"/utf8>>/binary,
(erlang:integer_to_binary(Line))/binary>>/binary,
":"/utf8>>/binary,
(erlang:integer_to_binary(Column))/binary>>/binary,
": "/utf8>>/binary,
(to_string(D))/binary>>;
{{some, Path@1}, no_source_loc} ->
<<<<Path@1/binary, ": "/utf8>>/binary, (to_string(D))/binary>>;
{none, _} ->
to_string(D)
end.
-file("src/oaspec/openapi/diagnostic.gleam", 373).
?DOC(" Convert a diagnostic to a short string (for backward-compatible display).\n").
-spec to_short_string(diagnostic()) -> binary().
to_short_string(D) ->
Prefix = case erlang:element(4, D) of
severity_error ->
<<"Error"/utf8>>;
severity_warning ->
<<"Warning"/utf8>>
end,
Loc_str = case erlang:element(7, D) of
{source_loc, Line, Column} ->
<<<<<<<<" (line "/utf8, (erlang:integer_to_binary(Line))/binary>>/binary,
", column "/utf8>>/binary,
(erlang:integer_to_binary(Column))/binary>>/binary,
")"/utf8>>;
no_source_loc ->
<<""/utf8>>
end,
case erlang:element(2, D) of
<<"file_error"/utf8>> ->
erlang:element(8, D);
<<"yaml_error"/utf8>> ->
<<(erlang:element(8, D))/binary, Loc_str/binary>>;
<<"missing_field"/utf8>> ->
Location = oaspec@internal@openapi@diagnostic_format:pointer_to_human(
erlang:element(6, D)
),
Field = gleam@string:replace(
erlang:element(8, D),
<<"Missing required field: "/utf8>>,
<<""/utf8>>
),
<<<<<<<<"Missing required field '"/utf8, Field/binary>>/binary,
"' at "/utf8>>/binary,
Location/binary>>/binary,
(case erlang:element(9, D) of
{some, H} ->
<<". "/utf8, H/binary>>;
none ->
<<""/utf8>>
end)/binary>>;
<<"invalid_value"/utf8>> ->
Location@1 = oaspec@internal@openapi@diagnostic_format:pointer_to_human(
erlang:element(6, D)
),
<<<<<<"Invalid value at "/utf8, Location@1/binary>>/binary,
": "/utf8>>/binary,
(erlang:element(8, D))/binary>>;
_ ->
<<<<<<Prefix/binary, (case erlang:element(6, D) of
<<""/utf8>> ->
<<""/utf8>>;
P ->
<<" at "/utf8,
(oaspec@internal@openapi@diagnostic_format:pointer_to_human(
P
))/binary>>
end)/binary>>/binary, ": "/utf8>>/binary, (erlang:element(
8,
D
))/binary>>
end.