-module(oaspec@codegen@types). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/oaspec/codegen/types.gleam"). -export([schema_ref_to_type/2, schema_to_gleam_type/2, merge_allof_schemas/2, schema_has_additional_properties/2, schema_has_untyped_additional_properties/2, schema_has_optional_fields/2, schema_ref_is_read_only/2, schema_ref_is_write_only/2, filter_read_only_properties/2, filter_write_only_properties/2, generate/1]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -file("src/oaspec/codegen/types.gleam", 50). ?DOC( " Generate types from component schemas and anonymous types from operations.\n" " Delegates to the IR pipeline: build IR declarations, then render to source.\n" ). -spec generate_types(oaspec@codegen@context:context()) -> binary(). generate_types(Ctx) -> _pipe = oaspec@codegen@ir_build:build_types_module(Ctx), oaspec@codegen@ir_render:render(_pipe). -file("src/oaspec/codegen/types.gleam", 133). ?DOC(" Convert a SchemaRef to a Gleam type string.\n"). -spec schema_ref_to_type( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> binary(). schema_ref_to_type(Ref, _) -> case Ref of {inline, Schema} -> oaspec@codegen@schema_dispatch:schema_type(Schema); {reference, _, Name} -> oaspec@util@naming:schema_to_type_name(Name) end. -file("src/oaspec/codegen/types.gleam", 142). ?DOC( " Convert a schema object to a Gleam type string.\n" " Delegates to schema_dispatch for the centralized type mapping.\n" ). -spec schema_to_gleam_type( oaspec@openapi@schema:schema_object(), oaspec@codegen@context:context() ) -> binary(). schema_to_gleam_type(Schema, _) -> oaspec@codegen@schema_dispatch:schema_type(Schema). -file("src/oaspec/codegen/types.gleam", 73). ?DOC(" Convert a schema to a qualified Gleam type with types. prefix for compound types.\n"). -spec schema_to_gleam_type_qualified( oaspec@openapi@schema:schema_object(), binary(), binary(), oaspec@codegen@context:context() ) -> binary(). schema_to_gleam_type_qualified(Schema_obj, Op_id, Suffix, Ctx) -> case Schema_obj of {array_schema, _, Items, _, _, _} -> case Items of {reference, _, Name} -> <<<<"List(types."/utf8, (oaspec@util@naming:schema_to_type_name(Name))/binary>>/binary, ")"/utf8>>; _ -> schema_to_gleam_type(Schema_obj, Ctx) end; {object_schema, _, _, _, _, _, _} -> Type_name = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary, Suffix/binary>>, <<"types."/utf8, Type_name/binary>>; {one_of_schema, _, Schemas, _} -> All_refs = gleam@list:all(Schemas, fun(S) -> case S of {reference, _, _} -> true; _ -> false end end), case All_refs of true -> Type_name@1 = <<(oaspec@util@naming:schema_to_type_name( Op_id ))/binary, Suffix/binary>>, <<"types."/utf8, Type_name@1/binary>>; false -> schema_to_gleam_type(Schema_obj, Ctx) end; {any_of_schema, _, Schemas@1, _} -> All_refs@1 = gleam@list:all(Schemas@1, fun(S@1) -> case S@1 of {reference, _, _} -> true; _ -> false end end), case All_refs@1 of true -> Type_name@2 = <<(oaspec@util@naming:schema_to_type_name( Op_id ))/binary, Suffix/binary>>, <<"types."/utf8, Type_name@2/binary>>; false -> schema_to_gleam_type(Schema_obj, Ctx) end; {all_of_schema, _, _} -> Type_name@3 = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary, Suffix/binary>>, <<"types."/utf8, Type_name@3/binary>>; _ -> schema_to_gleam_type(Schema_obj, Ctx) end. -file("src/oaspec/codegen/types.gleam", 57). ?DOC( " Convert a SchemaRef to a qualified Gleam type string (with types. prefix).\n" " Used in response_types and request_types where types are in a separate module.\n" ). -spec schema_ref_to_type_qualified( oaspec@openapi@schema:schema_ref(), binary(), binary(), oaspec@codegen@context:context() ) -> binary(). schema_ref_to_type_qualified(Ref, Op_id, Suffix, Ctx) -> case Ref of {inline, Schema_obj} -> schema_to_gleam_type_qualified(Schema_obj, Op_id, Suffix, Ctx); {reference, _, Name} -> <<"types."/utf8, (oaspec@util@naming:schema_to_type_name(Name))/binary>> end. -file("src/oaspec/codegen/types.gleam", 264). ?DOC(" Check if any response variant references the types module.\n"). -spec responses_need_types_import( list({binary(), oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()), binary(), oaspec@openapi@spec:http_method()}), oaspec@codegen@context:context() ) -> boolean(). responses_need_types_import(Operations, _) -> gleam@list:any( Operations, fun(Op) -> {_, Operation, _, _} = Op, Responses = maps:to_list(erlang:element(8, Operation)), gleam@list:any( Responses, fun(Entry) -> {_, Ref_or} = Entry, case Ref_or of {value, Response} -> Content_entries = maps:to_list( erlang:element(3, Response) ), case Content_entries of [] -> false; [_, _ | _] -> false; [{Media_type_name, Media_type}] -> case Media_type_name of <<"text/plain"/utf8>> -> false; <<"application/xml"/utf8>> -> false; <<"text/xml"/utf8>> -> false; <<"application/octet-stream"/utf8>> -> false; _ -> case erlang:element(2, Media_type) of {some, {reference, _, _}} -> true; {some, {inline, {array_schema, _, {reference, _, _}, _, _, _}}} -> true; {some, {inline, {object_schema, _, _, _, _, _, _}}} -> true; {some, {inline, {one_of_schema, _, _, _}}} -> true; {some, {inline, {any_of_schema, _, _, _}}} -> true; {some, {inline, {all_of_schema, _, _}}} -> true; _ -> false end end end; _ -> false end end ) end ). -file("src/oaspec/codegen/types.gleam", 405). ?DOC( " Convert an HTTP status code to a Gleam variant name.\n" " Prefixed with the type name to avoid duplicate constructors across types.\n" ). -spec status_code_to_variant(oaspec@util@http:http_status_code(), binary()) -> binary(). status_code_to_variant(Code, Type_name) -> <>. -file("src/oaspec/codegen/types.gleam", 328). ?DOC(" Generate a response type for an operation.\n"). -spec generate_response_type( gleam@string_tree:string_tree(), binary(), oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()), oaspec@codegen@context:context() ) -> gleam@string_tree:string_tree(). generate_response_type(Sb, Op_id, Operation, Ctx) -> Type_name = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary, "Response"/utf8>>, Responses = oaspec@util@http:sort_response_entries( maps:to_list(erlang:element(8, Operation)) ), case gleam@list:is_empty(Responses) of true -> Sb; false -> Sb@1 = begin _pipe = Sb, oaspec@util@string_extra:line( _pipe, <<<<"pub type "/utf8, Type_name/binary>>/binary, " {"/utf8>> ) end, Sb@3 = gleam@list:fold( Responses, Sb@1, fun(Sb@2, Entry) -> {Status_code, Ref_or} = Entry, case Ref_or of {value, Response} -> Variant_name = status_code_to_variant( Status_code, Type_name ), Content_entries = oaspec@codegen@ir_build:sorted_entries( erlang:element(3, Response) ), case Content_entries of [] -> _pipe@1 = Sb@2, oaspec@util@string_extra:indent( _pipe@1, 1, Variant_name ); [_, _ | _] -> _pipe@2 = Sb@2, oaspec@util@string_extra:indent( _pipe@2, 1, <> ); [{Media_type_name, Media_type}] -> case Media_type_name of <<"text/plain"/utf8>> -> case erlang:element(2, Media_type) of {some, _} -> _pipe@3 = Sb@2, oaspec@util@string_extra:indent( _pipe@3, 1, <> ); none -> _pipe@4 = Sb@2, oaspec@util@string_extra:indent( _pipe@4, 1, Variant_name ) end; <<"application/xml"/utf8>> -> case erlang:element(2, Media_type) of {some, _} -> _pipe@3 = Sb@2, oaspec@util@string_extra:indent( _pipe@3, 1, <> ); none -> _pipe@4 = Sb@2, oaspec@util@string_extra:indent( _pipe@4, 1, Variant_name ) end; <<"text/xml"/utf8>> -> case erlang:element(2, Media_type) of {some, _} -> _pipe@3 = Sb@2, oaspec@util@string_extra:indent( _pipe@3, 1, <> ); none -> _pipe@4 = Sb@2, oaspec@util@string_extra:indent( _pipe@4, 1, Variant_name ) end; <<"application/octet-stream"/utf8>> -> case erlang:element(2, Media_type) of {some, _} -> _pipe@3 = Sb@2, oaspec@util@string_extra:indent( _pipe@3, 1, <> ); none -> _pipe@4 = Sb@2, oaspec@util@string_extra:indent( _pipe@4, 1, Variant_name ) end; _ -> case erlang:element(2, Media_type) of {some, Ref} -> Suffix = <<"Response"/utf8, (oaspec@util@http:status_code_suffix( Status_code ))/binary>>, Inner_type = schema_ref_to_type_qualified( Ref, Op_id, Suffix, Ctx ), _pipe@5 = Sb@2, oaspec@util@string_extra:indent( _pipe@5, 1, <<<<<>/binary, Inner_type/binary>>/binary, ")"/utf8>> ); none -> _pipe@6 = Sb@2, oaspec@util@string_extra:indent( _pipe@6, 1, Variant_name ) end end end; _ -> Sb@2 end end ), _pipe@7 = Sb@3, _pipe@8 = oaspec@util@string_extra:line(_pipe@7, <<"}"/utf8>>), oaspec@util@string_extra:blank_line(_pipe@8) end. -file("src/oaspec/codegen/types.gleam", 305). ?DOC(" Generate response types for all operations.\n"). -spec generate_response_types( oaspec@codegen@context:context(), list({binary(), oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()), binary(), oaspec@openapi@spec:http_method()}) ) -> binary(). generate_response_types(Ctx, Operations) -> Needs_types = responses_need_types_import(Operations, Ctx), Imports = case Needs_types of true -> [<<(erlang:element(5, erlang:element(3, Ctx)))/binary, "/types"/utf8>>]; false -> [] end, Sb = begin _pipe = oaspec@util@string_extra:file_header(<<"0.11.0"/utf8>>), oaspec@util@string_extra:imports(_pipe, Imports) end, Sb@2 = gleam@list:fold( Operations, Sb, fun(Sb@1, Op) -> {Op_id, Operation, _, _} = Op, generate_response_type(Sb@1, Op_id, Operation, Ctx) end ), oaspec@util@string_extra:to_string(Sb@2). -file("src/oaspec/codegen/types.gleam", 418). ?DOC(" Re-export merge_allof_schemas.\n"). -spec merge_allof_schemas( list(oaspec@openapi@schema:schema_ref()), oaspec@codegen@context:context() ) -> oaspec@codegen@allof_merge:merged_all_of(). merge_allof_schemas(Schemas, Ctx) -> oaspec@codegen@allof_merge:merge_allof_schemas(Schemas, Ctx). -file("src/oaspec/codegen/types.gleam", 426). ?DOC(" Check if a schema has typed or untyped additionalProperties that would need Dict.\n"). -spec schema_has_additional_properties( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). schema_has_additional_properties(Schema_ref, Ctx) -> oaspec@codegen@schema_utils:schema_has_additional_properties( Schema_ref, Ctx ). -file("src/oaspec/codegen/types.gleam", 434). ?DOC(" Check if a schema has untyped additionalProperties (needs Dynamic import).\n"). -spec schema_has_untyped_additional_properties( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). schema_has_untyped_additional_properties(Schema_ref, Ctx) -> oaspec@codegen@schema_utils:schema_has_untyped_additional_properties( Schema_ref, Ctx ). -file("src/oaspec/codegen/types.gleam", 442). ?DOC(" Check if a schema has any optional or nullable fields that would need Option.\n"). -spec schema_has_optional_fields( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). schema_has_optional_fields(Schema_ref, Ctx) -> oaspec@codegen@schema_utils:schema_has_optional_fields(Schema_ref, Ctx). -file("src/oaspec/codegen/types.gleam", 447). ?DOC(" Check if a SchemaRef has readOnly metadata, resolving $ref if needed.\n"). -spec schema_ref_is_read_only( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). schema_ref_is_read_only(Ref, Ctx) -> oaspec@codegen@schema_utils:schema_ref_is_read_only(Ref, Ctx). -file("src/oaspec/codegen/types.gleam", 452). ?DOC(" Check if a SchemaRef has writeOnly metadata, resolving $ref if needed.\n"). -spec schema_ref_is_write_only( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). schema_ref_is_write_only(Ref, Ctx) -> oaspec@codegen@schema_utils:schema_ref_is_write_only(Ref, Ctx). -file("src/oaspec/codegen/types.gleam", 458). ?DOC( " Filter readOnly properties from an ObjectSchema for request body context.\n" " Returns a new schema with readOnly properties removed.\n" ). -spec filter_read_only_properties( oaspec@openapi@schema:schema_object(), oaspec@codegen@context:context() ) -> oaspec@openapi@schema:schema_object(). filter_read_only_properties(Schema_obj, Ctx) -> oaspec@codegen@schema_utils:filter_read_only_properties(Schema_obj, Ctx). -file("src/oaspec/codegen/types.gleam", 467). ?DOC( " Filter writeOnly properties from an ObjectSchema for response body context.\n" " Returns a new schema with writeOnly properties removed.\n" ). -spec filter_write_only_properties( oaspec@openapi@schema:schema_object(), oaspec@codegen@context:context() ) -> oaspec@openapi@schema:schema_object(). filter_write_only_properties(Schema_obj, Ctx) -> oaspec@codegen@schema_utils:filter_write_only_properties(Schema_obj, Ctx). -file("src/oaspec/codegen/types.gleam", 496). ?DOC(" Extract the type for an inline request body schema.\n"). -spec extract_inline_request_body_type( oaspec@openapi@schema:schema_object(), binary(), oaspec@codegen@context:context() ) -> binary(). extract_inline_request_body_type(Schema_obj, Op_id, Ctx) -> case Schema_obj of {object_schema, _, _, _, _, _, _} -> Type_name = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary, "RequestBody"/utf8>>, <<"types."/utf8, Type_name/binary>>; {all_of_schema, _, _} -> Type_name@1 = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary, "RequestBody"/utf8>>, <<"types."/utf8, Type_name@1/binary>>; {string_schema, _, _, _, _, _, _} -> <<"String"/utf8>>; {integer_schema, _, _, _, _, _, _, _} -> <<"Int"/utf8>>; {number_schema, _, _, _, _, _, _, _} -> <<"Float"/utf8>>; {boolean_schema, _} -> <<"Bool"/utf8>>; _ -> schema_to_gleam_type(Schema_obj, Ctx) end. -file("src/oaspec/codegen/types.gleam", 476). ?DOC( " Extract the Gleam type for a request body from its content media types.\n" " Uses types. prefix since request body schemas live in the types module.\n" ). -spec extract_request_body_type( oaspec@openapi@spec:request_body(oaspec@openapi@spec:resolved()), binary(), oaspec@codegen@context:context() ) -> binary(). extract_request_body_type(Rb, Op_id, Ctx) -> Content_entries = oaspec@codegen@ir_build:sorted_entries( erlang:element(3, Rb) ), case Content_entries of [{_, Media_type} | _] -> case erlang:element(2, Media_type) of {some, {reference, _, Name}} -> <<"types."/utf8, (oaspec@util@naming:schema_to_type_name(Name))/binary>>; {some, {inline, Schema_obj}} -> extract_inline_request_body_type(Schema_obj, Op_id, Ctx); _ -> <<"String"/utf8>> end; [] -> <<"String"/utf8>> end. -file("src/oaspec/codegen/types.gleam", 182). ?DOC(" Generate a single request type for an operation.\n"). -spec generate_request_type( gleam@string_tree:string_tree(), binary(), oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()), oaspec@codegen@context:context() ) -> gleam@string_tree:string_tree(). generate_request_type(Sb, Op_id, Operation, Ctx) -> Type_name = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary, "Request"/utf8>>, Params = erlang:element(6, Operation), case gleam@list:is_empty(Params) andalso gleam@option:is_none( erlang:element(7, Operation) ) of true -> Sb; false -> Sb@1 = case erlang:element(4, Operation) of {some, Desc} -> _pipe = Sb, oaspec@util@string_extra:doc_comment(_pipe, Desc); none -> Sb end, Sb@2 = begin _pipe@1 = Sb@1, oaspec@util@string_extra:line( _pipe@1, <<<<"pub type "/utf8, Type_name/binary>>/binary, " {"/utf8>> ) end, Sb@3 = begin _pipe@2 = Sb@2, oaspec@util@string_extra:indent( _pipe@2, 1, <> ) end, Param_count = erlang:length(Params), Sb@5 = gleam@list:index_fold( Params, Sb@3, fun(Sb@4, Ref_p, Idx) -> case Ref_p of {value, Param} -> Field_name = oaspec@util@naming:to_snake_case( erlang:element(2, Param) ), Field_type = case erlang:element(6, Param) of {parameter_schema, {inline, {string_schema, _, _, _, _, _, _}}} -> <<"String"/utf8>>; {parameter_schema, {inline, {integer_schema, _, _, _, _, _, _, _}}} -> <<"Int"/utf8>>; {parameter_schema, {inline, {number_schema, _, _, _, _, _, _, _}}} -> <<"Float"/utf8>>; {parameter_schema, {inline, {boolean_schema, _}}} -> <<"Bool"/utf8>>; {parameter_schema, {inline, {array_schema, _, Items, _, _, _}}} -> Item_type = case Items of {inline, {string_schema, _, _, _, _, _, _}} -> <<"String"/utf8>>; {inline, {integer_schema, _, _, _, _, _, _, _}} -> <<"Int"/utf8>>; {inline, {number_schema, _, _, _, _, _, _, _}} -> <<"Float"/utf8>>; {inline, {boolean_schema, _}} -> <<"Bool"/utf8>>; {reference, _, Name} -> oaspec@util@naming:schema_to_type_name( Name ); _ -> <<"String"/utf8>> end, <<<<"List("/utf8, Item_type/binary>>/binary, ")"/utf8>>; {parameter_schema, {reference, _, Name@1}} -> <<"types."/utf8, (oaspec@util@naming:schema_to_type_name( Name@1 ))/binary>>; _ -> <<"String"/utf8>> end, Final_type = case erlang:element(5, Param) of true -> Field_type; false -> <<<<"Option("/utf8, Field_type/binary>>/binary, ")"/utf8>> end, Has_more = Idx < (Param_count - 1), Has_body = gleam@option:is_some( erlang:element(7, Operation) ), Trailing = case Has_more orelse Has_body of true -> <<","/utf8>>; false -> <<""/utf8>> end, _pipe@3 = Sb@4, oaspec@util@string_extra:indent( _pipe@3, 2, <<<<<>/binary, Final_type/binary>>/binary, Trailing/binary>> ); _ -> Sb@4 end end ), Sb@6 = case erlang:element(7, Operation) of {some, {value, Rb}} -> Body_type = extract_request_body_type(Rb, Op_id, Ctx), Wrapped = case erlang:element(4, Rb) of true -> Body_type; false -> <<<<"Option("/utf8, Body_type/binary>>/binary, ")"/utf8>> end, _pipe@4 = Sb@5, oaspec@util@string_extra:indent( _pipe@4, 2, <<"body: "/utf8, Wrapped/binary>> ); _ -> Sb@5 end, _pipe@5 = Sb@6, _pipe@6 = oaspec@util@string_extra:indent(_pipe@5, 1, <<")"/utf8>>), _pipe@7 = oaspec@util@string_extra:line(_pipe@6, <<"}"/utf8>>), oaspec@util@string_extra:blank_line(_pipe@7) end. -file("src/oaspec/codegen/types.gleam", 147). ?DOC(" Generate request types for all operations.\n"). -spec generate_request_types( oaspec@codegen@context:context(), list({binary(), oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()), binary(), oaspec@openapi@spec:http_method()}) ) -> binary(). generate_request_types(Ctx, Operations) -> Needs_option = oaspec@codegen@import_analysis:operations_have_optional_params( Operations ) orelse oaspec@codegen@import_analysis:operations_have_optional_body( Operations ), Needs_types = oaspec@codegen@import_analysis:operations_need_typed_schemas( Operations ), Base_imports = case Needs_types of true -> [<<(erlang:element(5, erlang:element(3, Ctx)))/binary, "/types"/utf8>>]; false -> [] end, Imports = case Needs_option of true -> [<<"gleam/option.{type Option}"/utf8>> | Base_imports]; false -> Base_imports end, Sb = begin _pipe = oaspec@util@string_extra:file_header(<<"0.11.0"/utf8>>), oaspec@util@string_extra:imports(_pipe, Imports) end, Sb@2 = gleam@list:fold( Operations, Sb, fun(Sb@1, Op) -> {Op_id, Operation, _, _} = Op, generate_request_type(Sb@1, Op_id, Operation, Ctx) end ), oaspec@util@string_extra:to_string(Sb@2). -file("src/oaspec/codegen/types.gleam", 23). ?DOC(" Generate type definitions from OpenAPI schemas.\n"). -spec generate(oaspec@codegen@context:context()) -> list(oaspec@codegen@context:generated_file()). generate(Ctx) -> Operations = oaspec@openapi@operations:collect_operations(Ctx), Types_content = generate_types(Ctx), Request_types_content = generate_request_types(Ctx, Operations), Response_types_content = generate_response_types(Ctx, Operations), [{generated_file, <<"types.gleam"/utf8>>, Types_content, shared_target}, {generated_file, <<"request_types.gleam"/utf8>>, Request_types_content, shared_target}, {generated_file, <<"response_types.gleam"/utf8>>, Response_types_content, shared_target}].