-module(oaspec@codegen@validate). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/oaspec/codegen/validate.gleam"). -export([errors_only/1, warnings_only/1, filter_by_mode/2, error_to_string/1, validate/1]). -export_type([severity/0, target/0, validation_error/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type severity() :: severity_error | severity_warning. -type target() :: target_both | target_client | target_server. -type validation_error() :: {validation_error, binary(), binary(), severity(), target()}. -file("src/oaspec/codegen/validate.gleam", 54). ?DOC(" Filter to only errors (not warnings).\n"). -spec errors_only(list(validation_error())) -> list(validation_error()). errors_only(Issues) -> gleam@list:filter( Issues, fun(E) -> erlang:element(4, E) =:= severity_error end ). -file("src/oaspec/codegen/validate.gleam", 59). ?DOC(" Filter to only warnings (not errors).\n"). -spec warnings_only(list(validation_error())) -> list(validation_error()). warnings_only(Issues) -> gleam@list:filter( Issues, fun(E) -> erlang:element(4, E) =:= severity_warning end ). -file("src/oaspec/codegen/validate.gleam", 64). ?DOC(" Filter validation issues to those relevant for the selected generation mode.\n"). -spec filter_by_mode(list(validation_error()), oaspec@config:generate_mode()) -> list(validation_error()). 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/codegen/validate.gleam", 76). ?DOC(" Convert a validation error to a human-readable string.\n"). -spec error_to_string(validation_error()) -> binary(). error_to_string(Error) -> Prefix = case erlang:element(4, Error) of severity_error -> <<"Error"/utf8>>; severity_warning -> <<"Warning"/utf8>> end, <<<<<<<>/binary, (erlang:element(2, Error))/binary>>/binary, ": "/utf8>>/binary, (erlang:element(3, Error))/binary>>. -file("src/oaspec/codegen/validate.gleam", 133). ?DOC(" Extract parameter names from path template, e.g. \"/items/{id}\" -> [\"id\"].\n"). -spec extract_path_template_names(binary()) -> list(binary()). extract_path_template_names(Path) -> Re@1 = case gleam@regexp:from_string(<<"\\{([^}]+)\\}"/utf8>>) of {ok, Re} -> Re; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"oaspec/codegen/validate"/utf8>>, function => <<"extract_path_template_names"/utf8>>, line => 134, value => _assert_fail, start => 4455, 'end' => 4510, pattern_start => 4466, pattern_end => 4472}) end, _pipe = gleam@regexp:scan(Re@1, Path), gleam@list:filter_map(_pipe, fun(Match) -> case erlang:element(3, Match) of [{some, Name}] -> {ok, Name}; _ -> {error, nil} end end). -file("src/oaspec/codegen/validate.gleam", 101). ?DOC( " Validate that all {param} templates in the path have a corresponding\n" " path parameter definition. Reports unbound templates that would produce\n" " invalid generated code with literal {param} in URLs.\n" ). -spec validate_path_template_params( binary(), binary(), list(oaspec@openapi@spec:parameter()) ) -> list(validation_error()). validate_path_template_params(Op_id, Path, Params) -> Template_names = extract_path_template_names(Path), Path_param_names = gleam@list:filter_map( Params, fun(P) -> case erlang:element(3, P) of in_path -> {ok, erlang:element(2, P)}; _ -> {error, nil} end end ), gleam@list:filter_map( Template_names, fun(Name) -> case gleam@list:contains(Path_param_names, Name) of true -> {error, nil}; false -> {ok, {validation_error, <>, <<<<<<<<"Path template parameter '{"/utf8, Name/binary>>/binary, "}' in '"/utf8>>/binary, Path/binary>>/binary, "' has no corresponding parameter definition."/utf8>>, severity_error, target_both}} end end ). -file("src/oaspec/codegen/validate.gleam", 301). -spec validate_server_cookie_param( binary(), oaspec@openapi@spec:parameter(), oaspec@codegen@context:context() ) -> list(validation_error()). validate_server_cookie_param(Path, Param, Ctx) -> _ = Path, _ = Param, _ = Ctx, []. -file("src/oaspec/codegen/validate.gleam", 388). -spec resolve_schema_object( gleam@option:option(oaspec@openapi@schema:schema_ref()), oaspec@codegen@context:context() ) -> gleam@option:option(oaspec@openapi@schema:schema_object()). resolve_schema_object(Schema_ref, Ctx) -> case Schema_ref of {some, {inline, Schema_obj}} -> {some, Schema_obj}; {some, Schema_ref@1} -> case oaspec@openapi@resolver:resolve_schema_ref( Schema_ref@1, erlang:element(2, Ctx) ) of {ok, Schema_obj@1} -> {some, Schema_obj@1}; {error, _} -> none end; none -> none end. -file("src/oaspec/codegen/validate.gleam", 272). -spec deep_object_server_leaf_supported( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). deep_object_server_leaf_supported(Schema_ref, Ctx) -> case Schema_ref of {inline, {string_schema, _, _, _, _, _, _}} -> true; {inline, {integer_schema, _, _, _, _, _, _, _}} -> true; {inline, {number_schema, _, _, _, _, _, _, _}} -> true; {inline, {boolean_schema, _}} -> true; {inline, {array_schema, _, {inline, {string_schema, _, _, _, _, _, _}}, _, _, _}} -> true; {inline, {array_schema, _, {inline, {integer_schema, _, _, _, _, _, _, _}}, _, _, _}} -> true; {inline, {array_schema, _, {inline, {number_schema, _, _, _, _, _, _, _}}, _, _, _}} -> true; {inline, {array_schema, _, {inline, {boolean_schema, _}}, _, _, _}} -> true; {reference, _, _} -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; {some, {array_schema, _, {inline, {string_schema, _, _, _, _, _, _}}, _, _, _}} -> true; {some, {array_schema, _, {inline, {integer_schema, _, _, _, _, _, _, _}}, _, _, _}} -> true; {some, {array_schema, _, {inline, {number_schema, _, _, _, _, _, _, _}}, _, _, _}} -> true; {some, {array_schema, _, {inline, {boolean_schema, _}}, _, _, _}} -> true; _ -> false end; _ -> false end. -file("src/oaspec/codegen/validate.gleam", 243). -spec validate_server_deep_object_param( binary(), oaspec@openapi@spec:parameter(), oaspec@codegen@context:context() ) -> list(validation_error()). validate_server_deep_object_param(Path, Param, Ctx) -> case {erlang:element(3, Param), erlang:element(7, Param), resolve_schema_object(erlang:element(6, Param), Ctx)} of {in_query, {some, deep_object_style}, {some, {object_schema, _, Properties, _, _, _, _, _}}} -> _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Prop_name, Prop_ref} = Entry, case deep_object_server_leaf_supported(Prop_ref, Ctx) of true -> []; false -> [{validation_error, <<<>/binary, Prop_name/binary>>, <<"deepObject properties are only supported for inline primitive scalars and inline primitive array leaves in server code generation."/utf8>>, severity_error, target_server}] end end ); {_, _, _} -> [] end. -file("src/oaspec/codegen/validate.gleam", 198). -spec validate_server_structured_param( binary(), oaspec@openapi@spec:parameter(), oaspec@codegen@context:context() ) -> list(validation_error()). validate_server_structured_param(Path, Param, Ctx) -> case erlang:element(6, erlang:element(3, Ctx)) of client -> []; _ -> Schema_obj = resolve_schema_object(erlang:element(6, Param), Ctx), Array_errors = case {erlang:element(3, Param), Schema_obj} of {in_query, {some, {array_schema, _, {inline, {string_schema, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_query, {some, {array_schema, _, {inline, {integer_schema, _, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_query, {some, {array_schema, _, {inline, {number_schema, _, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_query, {some, {array_schema, _, {inline, {boolean_schema, _}}, _, _, _}}} -> []; {in_query, {some, {array_schema, _, _, _, _, _}}} -> [{validation_error, Path, <<"Query array parameters are only supported for inline primitive items in server code generation."/utf8>>, severity_error, target_server}]; {in_header, {some, {array_schema, _, {inline, {string_schema, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_header, {some, {array_schema, _, {inline, {integer_schema, _, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_header, {some, {array_schema, _, {inline, {number_schema, _, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_header, {some, {array_schema, _, {inline, {boolean_schema, _}}, _, _, _}}} -> []; {in_header, {some, {array_schema, _, _, _, _, _}}} -> [{validation_error, Path, <<"Header array parameters are only supported for inline primitive items in server code generation."/utf8>>, severity_error, target_server}]; {_, _} -> [] end, Deep_object_errors = validate_server_deep_object_param( Path, Param, Ctx ), lists:append([Array_errors, Deep_object_errors]) end. -file("src/oaspec/codegen/validate.gleam", 359). ?DOC(" Validate that a deepObject parameter has no nested object properties.\n"). -spec validate_deep_object_no_nested_objects( binary(), oaspec@openapi@spec:parameter(), oaspec@codegen@context:context() ) -> list(validation_error()). validate_deep_object_no_nested_objects(Path, Param, Ctx) -> case resolve_schema_object(erlang:element(6, Param), Ctx) of {some, {object_schema, _, Properties, _, _, _, _, _}} -> _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Prop_name, Prop_ref} = Entry, case resolve_schema_object({some, Prop_ref}, Ctx) of {some, {object_schema, _, _, _, _, _, _, _}} -> [{validation_error, <<<>/binary, Prop_name/binary>>, <<"Nested object properties in deepObject parameters are not supported. Only one level of object nesting is supported (e.g., filter[name]=value)."/utf8>>, severity_error, target_both}]; {some, {all_of_schema, _, _}} -> [{validation_error, <<<>/binary, Prop_name/binary>>, <<"Nested object properties in deepObject parameters are not supported. Only one level of object nesting is supported (e.g., filter[name]=value)."/utf8>>, severity_error, target_both}]; {some, {one_of_schema, _, _, _}} -> [{validation_error, <<<>/binary, Prop_name/binary>>, <<"Nested object properties in deepObject parameters are not supported. Only one level of object nesting is supported (e.g., filter[name]=value)."/utf8>>, severity_error, target_both}]; {some, {any_of_schema, _, _, _}} -> [{validation_error, <<<>/binary, Prop_name/binary>>, <<"Nested object properties in deepObject parameters are not supported. Only one level of object nesting is supported (e.g., filter[name]=value)."/utf8>>, severity_error, target_both}]; _ -> [] end end ); _ -> [] end. -file("src/oaspec/codegen/validate.gleam", 314). ?DOC( " Check if a parameter has a complex schema (object, oneOf, allOf, anyOf)\n" " that is not handled by deepObject style.\n" ). -spec validate_complex_param_schema( binary(), oaspec@openapi@spec:parameter(), oaspec@codegen@context:context() ) -> list(validation_error()). validate_complex_param_schema(Path, Param, Ctx) -> case erlang:element(7, Param) of {some, deep_object_style} -> validate_deep_object_no_nested_objects(Path, Param, Ctx); _ -> case resolve_schema_object(erlang:element(6, Param), Ctx) of {some, {object_schema, _, _, _, _, _, _, _}} -> case erlang:element(3, Param) of in_path -> case erlang:element(6, erlang:element(3, Ctx)) of client -> []; _ -> [{validation_error, Path, <<"Complex path parameters are not supported for server code generation."/utf8>>, severity_error, target_server}] end; _ -> [{validation_error, Path, <<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>, severity_error, target_both}] end; {some, {all_of_schema, _, _}} -> case erlang:element(3, Param) of in_path -> case erlang:element(6, erlang:element(3, Ctx)) of client -> []; _ -> [{validation_error, Path, <<"Complex path parameters are not supported for server code generation."/utf8>>, severity_error, target_server}] end; _ -> [{validation_error, Path, <<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>, severity_error, target_both}] end; {some, {one_of_schema, _, _, _}} -> case erlang:element(3, Param) of in_path -> case erlang:element(6, erlang:element(3, Ctx)) of client -> []; _ -> [{validation_error, Path, <<"Complex path parameters are not supported for server code generation."/utf8>>, severity_error, target_server}] end; _ -> [{validation_error, Path, <<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>, severity_error, target_both}] end; {some, {any_of_schema, _, _, _}} -> case erlang:element(3, Param) of in_path -> case erlang:element(6, erlang:element(3, Ctx)) of client -> []; _ -> [{validation_error, Path, <<"Complex path parameters are not supported for server code generation."/utf8>>, severity_error, target_server}] end; _ -> [{validation_error, Path, <<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>, severity_error, target_both}] end; _ -> [] end end. -file("src/oaspec/codegen/validate.gleam", 147). ?DOC( " Validate parameters for unsupported serialization styles.\n" " Supported: form (default), deepObject (query+object), exploded array.\n" " Unsupported: matrix, label, simple, spaceDelimited, pipeDelimited.\n" ). -spec validate_parameters( binary(), list(oaspec@openapi@spec:parameter()), oaspec@codegen@context:context() ) -> list(validation_error()). validate_parameters(Op_id, Params, Ctx) -> gleam@list:flat_map( Params, fun(P) -> Path = <<<>/binary, (erlang:element(2, P))/binary>>, Style_errors = case erlang:element(7, P) of {some, matrix_style} -> [{validation_error, Path, <<"Parameter style is not supported. Supported styles: form, deepObject, simple."/utf8>>, severity_error, target_both}]; {some, label_style} -> [{validation_error, Path, <<"Parameter style is not supported. Supported styles: form, deepObject, simple."/utf8>>, severity_error, target_both}]; {some, space_delimited_style} -> [{validation_error, Path, <<"Parameter style is not supported. Supported styles: form, deepObject, simple."/utf8>>, severity_error, target_both}]; {some, pipe_delimited_style} -> [{validation_error, Path, <<"Parameter style is not supported. Supported styles: form, deepObject, simple."/utf8>>, severity_error, target_both}]; _ -> [] end, Content_errors = case erlang:element(6, P) of none -> [{validation_error, Path, <<"Parameters using 'content' instead of 'schema' are not supported."/utf8>>, severity_error, target_both}]; _ -> [] end, Complex_schema_errors = validate_complex_param_schema(Path, P, Ctx), Server_structured_param_errors = validate_server_structured_param( Path, P, Ctx ), Cookie_errors = validate_server_cookie_param(Path, P, Ctx), lists:append( [Style_errors, Content_errors, Complex_schema_errors, Server_structured_param_errors, Cookie_errors] ) end ). -file("src/oaspec/codegen/validate.gleam", 522). ?DOC( " Validate that application/x-www-form-urlencoded uses an object schema.\n" " Non-object schemas produce empty form bodies in the generated code.\n" ). -spec validate_form_urlencoded_schema( binary(), gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()), oaspec@codegen@context:context() ) -> list(validation_error()). validate_form_urlencoded_schema(Op_id, Content, Ctx) -> case gleam_stdlib:map_get( Content, <<"application/x-www-form-urlencoded"/utf8>> ) of {ok, Media_type} -> case resolve_schema_object(erlang:element(2, Media_type), Ctx) of {some, {object_schema, _, _, _, _, _, _, _}} -> []; {some, _} -> [{validation_error, <>, <<"application/x-www-form-urlencoded request bodies must use an object schema."/utf8>>, severity_error, target_both}]; none -> [] end; {error, _} -> [] end. -file("src/oaspec/codegen/validate.gleam", 602). -spec validate_server_request_body_content_types( binary(), list(binary()), oaspec@codegen@context:context() ) -> list(validation_error()). validate_server_request_body_content_types(Op_id, Content_keys, Ctx) -> case erlang:element(6, erlang:element(3, Ctx)) of client -> []; _ -> Non_json_but_supported = gleam@list:filter( Content_keys, fun(Key) -> (((Key /= <<"application/json"/utf8>>) andalso (Key /= <<"application/x-www-form-urlencoded"/utf8>>)) andalso (Key /= <<"multipart/form-data"/utf8>>)) andalso oaspec@util@content_type:is_supported_request( oaspec@util@content_type:from_string(Key) ) end ), gleam@list:map( Non_json_but_supported, fun(Media_type) -> {validation_error, <>, <<<<"Content type '"/utf8, Media_type/binary>>/binary, "' is not supported for server code generation. Server router only supports application/json request bodies with typed decoding."/utf8>>, severity_error, target_server} end ) end. -file("src/oaspec/codegen/validate.gleam", 703). -spec form_urlencoded_server_array_item_supported( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). form_urlencoded_server_array_item_supported(Schema_ref, Ctx) -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; _ -> false end. -file("src/oaspec/codegen/validate.gleam", 681). -spec form_urlencoded_server_field_supported( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context(), integer() ) -> boolean(). form_urlencoded_server_field_supported(Schema_ref, Ctx, Depth) -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; {some, {array_schema, _, Items, _, _, _}} -> form_urlencoded_server_array_item_supported(Items, Ctx); {some, {object_schema, _, Properties, _, _, _, _, _}} when Depth < 5 -> _pipe = maps:to_list(Properties), gleam@list:all( _pipe, fun(Entry) -> {_, Child_schema} = Entry, form_urlencoded_server_field_supported( Child_schema, Ctx, Depth + 1 ) end ); _ -> false end. -file("src/oaspec/codegen/validate.gleam", 550). ?DOC( " Validate that request body content types are supported for server codegen.\n" " Server router only handles application/json with typed decode; other types\n" " that pass the general is_supported_request check (multipart/form-data,\n" " application/x-www-form-urlencoded) are passed as raw String which breaks\n" " the typed body contract.\n" ). -spec validate_server_form_urlencoded_request_body( binary(), gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()), list(binary()), oaspec@codegen@context:context() ) -> list(validation_error()). validate_server_form_urlencoded_request_body(Op_id, Content, Content_keys, Ctx) -> case erlang:element(6, erlang:element(3, Ctx)) of client -> []; _ -> case gleam_stdlib:map_get( Content, <<"application/x-www-form-urlencoded"/utf8>> ) of {ok, Media_type} -> Content_type_errors = case erlang:length(Content_keys) > 1 of true -> [{validation_error, <>, <<"application/x-www-form-urlencoded request bodies are only supported as the sole request content type for server code generation."/utf8>>, severity_error, target_server}]; false -> [] end, Field_errors = case resolve_schema_object( erlang:element(2, Media_type), Ctx ) of {some, {object_schema, _, Properties, _, _, _, _, _}} -> _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Field_name, Field_schema} = Entry, case form_urlencoded_server_field_supported( Field_schema, Ctx, 0 ) of true -> []; false -> [{validation_error, <<<>/binary, Field_name/binary>>, <<"application/x-www-form-urlencoded server request bodies only support primitive scalars, primitive arrays, and nested objects with primitive leaves (max 5 levels)."/utf8>>, severity_error, target_server}] end end ); _ -> [] end, lists:append(Content_type_errors, Field_errors); {error, _} -> [] end end. -file("src/oaspec/codegen/validate.gleam", 728). -spec multipart_server_array_item_supported( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). multipart_server_array_item_supported(Schema_ref, Ctx) -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; _ -> false end. -file("src/oaspec/codegen/validate.gleam", 716). -spec multipart_server_field_supported( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). multipart_server_field_supported(Schema_ref, Ctx) -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; {some, {array_schema, _, Items, _, _, _}} -> multipart_server_array_item_supported(Items, Ctx); _ -> false end. -file("src/oaspec/codegen/validate.gleam", 631). -spec validate_server_multipart_request_body( binary(), gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()), list(binary()), oaspec@codegen@context:context() ) -> list(validation_error()). validate_server_multipart_request_body(Op_id, Content, Content_keys, Ctx) -> case erlang:element(6, erlang:element(3, Ctx)) of client -> []; _ -> case gleam_stdlib:map_get(Content, <<"multipart/form-data"/utf8>>) of {ok, Media_type} -> Content_type_errors = case erlang:length(Content_keys) > 1 of true -> [{validation_error, <>, <<"multipart/form-data request bodies are only supported as the sole request content type for server code generation."/utf8>>, severity_error, target_server}]; false -> [] end, Field_errors = case resolve_schema_object( erlang:element(2, Media_type), Ctx ) of {some, {object_schema, _, Properties, _, _, _, _, _}} -> _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Field_name, Field_schema} = Entry, case multipart_server_field_supported( Field_schema, Ctx ) of true -> []; false -> [{validation_error, <<<>/binary, Field_name/binary>>, <<"multipart/form-data server request bodies only support primitive scalar fields."/utf8>>, severity_error, target_server}] end end ); _ -> [] end, lists:append(Content_type_errors, Field_errors); {error, _} -> [] end end. -file("src/oaspec/codegen/validate.gleam", 741). -spec multipart_field_is_stringifiable( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). multipart_field_is_stringifiable(Schema_ref, Ctx) -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; _ -> false end. -file("src/oaspec/codegen/validate.gleam", 482). -spec validate_multipart_request_body_fields( binary(), gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()), oaspec@codegen@context:context() ) -> list(validation_error()). validate_multipart_request_body_fields(Op_id, Content, Ctx) -> case gleam_stdlib:map_get(Content, <<"multipart/form-data"/utf8>>) of {ok, Media_type} -> case resolve_schema_object(erlang:element(2, Media_type), Ctx) of {some, {object_schema, _, Properties, _, _, _, _, _}} -> _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Field_name, Field_schema} = Entry, case multipart_field_is_stringifiable( Field_schema, Ctx ) of true -> []; false -> [{validation_error, <<<>/binary, Field_name/binary>>, <<"multipart/form-data fields must be string, integer, number, boolean, binary, or string enums."/utf8>>, severity_error, target_both}] end end ); {some, _} -> [{validation_error, <>, <<"multipart/form-data request bodies must use an object schema."/utf8>>, severity_error, target_both}]; none -> [] end; {error, _} -> [] end. -file("src/oaspec/codegen/validate.gleam", 912). ?DOC( " Validate security schemes for unsupported types.\n" " All scheme types are now supported: apiKey, HTTP (any scheme), OAuth2,\n" " and OpenID Connect.\n" ). -spec validate_security_schemes(oaspec@codegen@context:context()) -> list(validation_error()). validate_security_schemes(_) -> []. -file("src/oaspec/codegen/validate.gleam", 919). ?DOC( " Check for AST fields that are parsed but not used by codegen, emitting\n" " warnings so users are aware their spec contains features we preserve but\n" " do not generate code for.\n" ). -spec validate_preserved_but_unused(oaspec@codegen@context:context()) -> list(validation_error()). validate_preserved_but_unused(Ctx) -> Webhook_warnings = case gleam@dict:is_empty( erlang:element(8, erlang:element(2, Ctx)) ) of true -> []; false -> [{validation_error, <<"webhooks"/utf8>>, <<"Webhooks are parsed but not used by code generation."/utf8>>, severity_warning, target_both}] end, Operations = oaspec@codegen@types:collect_operations(Ctx), Response_warnings = gleam@list:flat_map( Operations, fun(Op) -> {Op_id, Operation, _, _} = Op, Entries = maps:to_list(erlang:element(8, Operation)), gleam@list:flat_map( Entries, fun(Entry) -> {Status_code, Response} = Entry, Base_path = <<<>/binary, Status_code/binary>>, Multi_content_warnings = case {erlang:element( 6, erlang:element(3, Ctx) ), erlang:length(maps:to_list(erlang:element(3, Response)))} of {client, _} -> []; {_, N} when N > 1 -> [{validation_error, <>, <<"Multiple response content types are not fully supported for server code generation. Generated server responses lose the content-type header."/utf8>>, severity_warning, target_server}]; {_, _} -> [] end, Header_warnings = case gleam@dict:is_empty( erlang:element(4, Response) ) of true -> []; false -> [{validation_error, <>, <<"Response headers are parsed but not used by code generation."/utf8>>, severity_warning, target_both}] end, Link_warnings = case gleam@dict:is_empty( erlang:element(5, Response) ) of true -> []; false -> [{validation_error, <>, <<"Response links are parsed but not used by code generation."/utf8>>, severity_warning, target_both}] end, Content_entries = maps:to_list(erlang:element(3, Response)), Encoding_warnings = gleam@list:flat_map( Content_entries, fun(Ce) -> {Media_type_name, Media_type} = Ce, case gleam@dict:is_empty( erlang:element(5, Media_type) ) of true -> []; false -> [{validation_error, <<<<<>/binary, Media_type_name/binary>>/binary, ".encoding"/utf8>>, <<"MediaType encoding is parsed but not used by code generation."/utf8>>, severity_warning, target_both}] end end ), lists:append( [Multi_content_warnings, Header_warnings, Link_warnings, Encoding_warnings] ) end ) end ), lists:append([Webhook_warnings, Response_warnings]). -file("src/oaspec/codegen/validate.gleam", 847). ?DOC(" Recursively validate a SchemaObject, descending into all sub-schemas.\n"). -spec validate_schema_recursive( binary(), oaspec@openapi@schema:schema_object(), oaspec@codegen@context:context() ) -> list(validation_error()). validate_schema_recursive(Path, Schema_obj, Ctx) -> case Schema_obj of {object_schema, _, Properties, _, Additional_properties, Additional_properties_untyped, _, _} -> Ap_errors = [], _ = Additional_properties_untyped, Typed_ap_errors = case Additional_properties of {some, Ap_ref} -> validate_schema_ref_recursive( <>, Ap_ref, Ctx ); none -> [] end, Prop_errors = begin _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Prop_name, Prop_ref} = Entry, Prop_path = <<<>/binary, Prop_name/binary>>, validate_schema_ref_recursive(Prop_path, Prop_ref, Ctx) end ) end, lists:append([Ap_errors, Typed_ap_errors, Prop_errors]); {array_schema, _, Items, _, _, _} -> validate_schema_ref_recursive( <>, Items, Ctx ); {one_of_schema, _, Schemas, _} -> gleam@list:flat_map( Schemas, fun(S_ref) -> validate_schema_ref_recursive( <>, S_ref, Ctx ) end ); {any_of_schema, _, Schemas@1, _} -> gleam@list:flat_map( Schemas@1, fun(S_ref@1) -> validate_schema_ref_recursive( <>, S_ref@1, Ctx ) end ); {all_of_schema, _, Schemas@2} -> gleam@list:flat_map( Schemas@2, fun(S_ref@2) -> validate_schema_ref_recursive( <>, S_ref@2, Ctx ) end ); _ -> [] end. -file("src/oaspec/codegen/validate.gleam", 808). ?DOC( " Recursively validate a SchemaRef at any depth.\n" " References are checked for resolvability against the spec's components.\n" ). -spec validate_schema_ref_recursive( binary(), oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> list(validation_error()). validate_schema_ref_recursive(Path, Schema_ref, Ctx) -> case Schema_ref of {reference, Ref, _} -> case gleam_stdlib:string_starts_with(Ref, <<"#/"/utf8>>) of false -> [{validation_error, Path, <<<<"External $ref '"/utf8, Ref/binary>>/binary, "' is not supported. Only local references (#/components/...) are supported."/utf8>>, severity_error, target_both}]; true -> case oaspec@openapi@resolver:resolve_schema_ref( Schema_ref, erlang:element(2, Ctx) ) of {ok, _} -> []; {error, _} -> [{validation_error, Path, <<<<"Unresolved schema reference: '"/utf8, Ref/binary>>/binary, "'. The referenced schema does not exist in components."/utf8>>, severity_error, target_both}] end end; {inline, Schema_obj} -> validate_schema_recursive(Path, Schema_obj, Ctx) end. -file("src/oaspec/codegen/validate.gleam", 404). ?DOC(" Validate request body for unsupported patterns.\n"). -spec validate_request_body( binary(), gleam@option:option(oaspec@openapi@spec:request_body()), oaspec@codegen@context:context() ) -> list(validation_error()). validate_request_body(Op_id, Request_body, Ctx) -> case Request_body of none -> []; {some, Rb} -> Content_keys = maps:keys(erlang:element(3, Rb)), Unsupported = gleam@list:filter( Content_keys, fun(Key) -> not oaspec@util@content_type:is_supported_request( oaspec@util@content_type:from_string(Key) ) end ), Content_type_errors = case Unsupported of [] -> []; [Media_type | _] -> [{validation_error, <>, <<<<"Content type '"/utf8, Media_type/binary>>/binary, "' is not supported. Supported request content types: application/json, multipart/form-data, application/x-www-form-urlencoded."/utf8>>, severity_error, target_both}] end, Schema_errors = begin _pipe = maps:to_list(erlang:element(3, Rb)), gleam@list:flat_map( _pipe, fun(Entry) -> {_, Media_type@1} = Entry, case erlang:element(2, Media_type@1) of {some, Schema_ref} -> validate_schema_ref_recursive( <>, Schema_ref, Ctx ); none -> [] end end ) end, Multipart_field_errors = validate_multipart_request_body_fields( Op_id, erlang:element(3, Rb), Ctx ), Form_urlencoded_errors = validate_form_urlencoded_schema( Op_id, erlang:element(3, Rb), Ctx ), Server_form_urlencoded_errors = validate_server_form_urlencoded_request_body( Op_id, erlang:element(3, Rb), Content_keys, Ctx ), Server_multipart_errors = validate_server_multipart_request_body( Op_id, erlang:element(3, Rb), Content_keys, Ctx ), Server_body_errors = validate_server_request_body_content_types( Op_id, Content_keys, Ctx ), lists:append( [Content_type_errors, Schema_errors, Multipart_field_errors, Form_urlencoded_errors, Server_form_urlencoded_errors, Server_multipart_errors, Server_body_errors] ) end. -file("src/oaspec/codegen/validate.gleam", 752). ?DOC(" Validate response schemas and content types.\n"). -spec validate_responses( binary(), gleam@dict:dict(binary(), oaspec@openapi@spec:response()), oaspec@codegen@context:context() ) -> list(validation_error()). validate_responses(Op_id, Responses, Ctx) -> Entries = maps:to_list(Responses), gleam@list:flat_map( Entries, fun(Entry) -> {Status_code, Response} = Entry, Content_entries = maps:to_list(erlang:element(3, Response)), gleam@list:flat_map( Content_entries, fun(Ce) -> {Media_type_name, Media_type} = Ce, Path = <<<>/binary, Status_code/binary>>, Content_type_errors = case oaspec@util@content_type:is_supported_response( oaspec@util@content_type:from_string(Media_type_name) ) of true -> []; false -> [{validation_error, Path, <<<<"Response content type '"/utf8, Media_type_name/binary>>/binary, "' is not supported. Supported response content types: application/json, text/plain, application/octet-stream, application/xml, text/xml."/utf8>>, severity_error, target_both}] end, Schema_errors = case erlang:element(2, Media_type) of {some, Schema_ref} -> validate_schema_ref_recursive(Path, Schema_ref, Ctx); none -> [] end, lists:append(Content_type_errors, Schema_errors) end ) end ). -file("src/oaspec/codegen/validate.gleam", 85). ?DOC(" Validate all operations for unsupported patterns.\n"). -spec validate_operations(oaspec@codegen@context:context()) -> list(validation_error()). validate_operations(Ctx) -> Operations = oaspec@codegen@types:collect_operations(Ctx), gleam@list:flat_map( Operations, fun(Op) -> {Op_id, Operation, Path, _} = Op, Path_errors = validate_path_template_params( Op_id, Path, erlang:element(6, Operation) ), Param_errors = validate_parameters( Op_id, erlang:element(6, Operation), Ctx ), Body_errors = validate_request_body( Op_id, erlang:element(7, Operation), Ctx ), Response_errors = validate_responses( Op_id, erlang:element(8, Operation), Ctx ), lists:append( [Path_errors, Param_errors, Body_errors, Response_errors] ) end ). -file("src/oaspec/codegen/validate.gleam", 791). ?DOC(" Validate component schemas recursively.\n"). -spec validate_component_schemas(oaspec@codegen@context:context()) -> list(validation_error()). validate_component_schemas(Ctx) -> Schemas = case erlang:element(5, erlang:element(2, Ctx)) of {some, Components} -> maps:to_list(erlang:element(2, Components)); none -> [] end, gleam@list:flat_map( Schemas, fun(Entry) -> {Name, Schema_ref} = Entry, validate_schema_ref_recursive( <<"components.schemas."/utf8, Name/binary>>, Schema_ref, Ctx ) end ). -file("src/oaspec/codegen/validate.gleam", 45). ?DOC( " Validate the parsed spec for unsupported patterns.\n" " Returns a list of errors; empty list means validation passed.\n" " Name collisions and duplicate operationIds are handled by the dedup pass\n" " before validation, so they are no longer checked here.\n" ). -spec validate(oaspec@codegen@context:context()) -> list(validation_error()). validate(Ctx) -> Op_errors = validate_operations(Ctx), Schema_errors = validate_component_schemas(Ctx), Security_errors = validate_security_schemes(Ctx), Preserved_warnings = validate_preserved_but_unused(Ctx), lists:append( [Op_errors, Schema_errors, Security_errors, Preserved_warnings] ).