-module(oaspec@internal@openapi@parser_schema). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/oaspec/internal/openapi/parser_schema.gleam"). -export([parse_schema_ref/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(false). -file("src/oaspec/internal/openapi/parser_schema.gleam", 179). ?DOC(false). -spec is_null_branch(yay:node_()) -> boolean(). is_null_branch(Node) -> case yay:extract_optional_string(Node, <<"$ref"/utf8>>) of {ok, {some, _}} -> false; _ -> case yay:select_sugar(Node, <<"type"/utf8>>) of {ok, {node_str, <<"null"/utf8>>}} -> true; _ -> false end end. -file("src/oaspec/internal/openapi/parser_schema.gleam", 196). ?DOC(false). -spec partition_null_branches(list(yay:node_())) -> {list(yay:node_()), boolean()}. partition_null_branches(Items) -> {Non_null, Has_null} = gleam@list:fold( Items, {[], false}, fun(Acc, Item) -> {Kept, Seen} = Acc, case is_null_branch(Item) of true -> {Kept, true}; false -> {[Item | Kept], Seen} end end ), {lists:reverse(Non_null), Has_null}. -file("src/oaspec/internal/openapi/parser_schema.gleam", 211). ?DOC(false). -spec detect_unsupported_keywords(yay:node_()) -> list(binary()). detect_unsupported_keywords(Node) -> Keywords = [<<"$defs"/utf8>>, <<"prefixItems"/utf8>>, <<"if"/utf8>>, <<"then"/utf8>>, <<"else"/utf8>>, <<"dependentSchemas"/utf8>>, <<"unevaluatedProperties"/utf8>>, <<"unevaluatedItems"/utf8>>, <<"contentEncoding"/utf8>>, <<"contentMediaType"/utf8>>, <<"contentSchema"/utf8>>, <<"not"/utf8>>], gleam@list:filter( Keywords, fun(Keyword) -> gleam@result:is_ok(yay:select_sugar(Node, Keyword)) end ). -file("src/oaspec/internal/openapi/parser_schema.gleam", 481). ?DOC(false). -spec parse_discriminator( yay:node_(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@schema:discriminator()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_discriminator(Node, Index) -> gleam@result:'try'( begin _pipe = yay:select_sugar(Node, <<"discriminator"/utf8>>), gleam@result:map_error( _pipe, fun(_capture) -> oaspec@internal@openapi@parser_error:missing_field_from_selector( _capture, <<"schema"/utf8>>, <<"discriminator"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"schema"/utf8>>, <<"discriminator"/utf8>> ) ) end ) end, fun(Disc_node) -> gleam@result:'try'( begin _pipe@1 = yay:extract_string( Disc_node, <<"propertyName"/utf8>> ), gleam@result:map_error( _pipe@1, fun(_capture@1) -> oaspec@internal@openapi@parser_error:missing_field_from_extraction( _capture@1, <<"discriminator"/utf8>>, <<"propertyName"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, <<"discriminator"/utf8>>, <<"propertyName"/utf8>> ) ) end ) end, fun(Property_name) -> Mapping = case yay:extract_string_map( Disc_node, <<"mapping"/utf8>> ) of {ok, M} -> M; _ -> maps:new() end, {ok, {discriminator, Property_name, Mapping}} end ) end ). -file("src/oaspec/internal/openapi/parser_schema.gleam", 38). ?DOC(false). -spec parse_schema_object( yay:node_(), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@schema:schema_object()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_schema_object(Node, Path, Index) -> Nullable = begin _pipe = yay:extract_optional_bool(Node, <<"nullable"/utf8>>), _pipe@1 = gleam@result:unwrap(_pipe, none), gleam@option:unwrap(_pipe@1, false) end, Description = begin _pipe@2 = yay:extract_optional_string(Node, <<"description"/utf8>>), gleam@result:unwrap(_pipe@2, none) end, Deprecated = begin _pipe@3 = yay:extract_optional_bool(Node, <<"deprecated"/utf8>>), _pipe@4 = gleam@result:unwrap(_pipe@3, none), gleam@option:unwrap(_pipe@4, false) end, Title = begin _pipe@5 = yay:extract_optional_string(Node, <<"title"/utf8>>), gleam@result:unwrap(_pipe@5, none) end, Read_only = begin _pipe@6 = yay:extract_optional_bool(Node, <<"readOnly"/utf8>>), _pipe@7 = gleam@result:unwrap(_pipe@6, none), gleam@option:unwrap(_pipe@7, false) end, Write_only = begin _pipe@8 = yay:extract_optional_bool(Node, <<"writeOnly"/utf8>>), _pipe@9 = gleam@result:unwrap(_pipe@8, none), gleam@option:unwrap(_pipe@9, false) end, Default = oaspec@internal@openapi@value:extract_optional( Node, <<"default"/utf8>> ), Example = oaspec@internal@openapi@value:extract_optional( Node, <<"example"/utf8>> ), Const_value = oaspec@internal@openapi@value:extract_optional( Node, <<"const"/utf8>> ), Unsupported_keywords = detect_unsupported_keywords(Node), Metadata = {schema_metadata, Description, Nullable, Deprecated, Title, Read_only, Write_only, Default, Example, Const_value, none, Unsupported_keywords, false, user_authored}, case yay:select_sugar(Node, <<"allOf"/utf8>>) of {ok, {node_seq, Items}} -> gleam@result:'try'( gleam@list:try_map( Items, fun(_capture) -> parse_schema_ref( _capture, <>, Index ) end ), fun(Schemas) -> {ok, {all_of_schema, Metadata, Schemas}} end ); _ -> case yay:select_sugar(Node, <<"oneOf"/utf8>>) of {ok, {node_seq, Items@1}} -> {Non_null_items, Has_null} = partition_null_branches( Items@1 ), Metadata@1 = case Has_null of true -> {schema_metadata, erlang:element(2, Metadata), true, erlang:element(4, Metadata), erlang:element(5, Metadata), erlang:element(6, Metadata), erlang:element(7, Metadata), erlang:element(8, Metadata), erlang:element(9, Metadata), erlang:element(10, Metadata), erlang:element(11, Metadata), erlang:element(12, Metadata), erlang:element(13, Metadata), erlang:element(14, Metadata)}; false -> Metadata end, gleam@result:'try'( gleam@list:try_map( Non_null_items, fun(_capture@1) -> parse_schema_ref( _capture@1, <>, Index ) end ), fun(Schemas@1) -> gleam@result:'try'( case gleam@result:is_ok( yay:select_sugar( Node, <<"discriminator"/utf8>> ) ) of true -> gleam@result:'try'( parse_discriminator(Node, Index), fun(D) -> {ok, {some, D}} end ); false -> {ok, none} end, fun(Discriminator) -> {ok, {one_of_schema, Metadata@1, Schemas@1, Discriminator}} end ) end ); _ -> case yay:select_sugar(Node, <<"anyOf"/utf8>>) of {ok, {node_seq, Items@2}} -> {Non_null_items@1, Has_null@1} = partition_null_branches( Items@2 ), Metadata@2 = case Has_null@1 of true -> {schema_metadata, erlang:element(2, Metadata), true, erlang:element(4, Metadata), erlang:element(5, Metadata), erlang:element(6, Metadata), erlang:element(7, Metadata), erlang:element(8, Metadata), erlang:element(9, Metadata), erlang:element(10, Metadata), erlang:element(11, Metadata), erlang:element(12, Metadata), erlang:element(13, Metadata), erlang:element(14, Metadata)}; false -> Metadata end, gleam@result:'try'( gleam@list:try_map( Non_null_items@1, fun(_capture@2) -> parse_schema_ref( _capture@2, <>, Index ) end ), fun(Schemas@2) -> gleam@result:'try'( case gleam@result:is_ok( yay:select_sugar( Node, <<"discriminator"/utf8>> ) ) of true -> gleam@result:'try'( parse_discriminator( Node, Index ), fun(D@1) -> {ok, {some, D@1}} end ); false -> {ok, none} end, fun(Discriminator@1) -> {ok, {any_of_schema, Metadata@2, Schemas@2, Discriminator@1}} end ) end ); _ -> parse_typed_schema(Node, Metadata, Path, Index) end end end. -file("src/oaspec/internal/openapi/parser_schema.gleam", 223). ?DOC(false). -spec parse_typed_schema( yay:node_(), oaspec@internal@openapi@schema:schema_metadata(), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@schema:schema_object()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_typed_schema(Node, Metadata, Path, Index) -> gleam@result:'try'(case yay:select_sugar(Node, <<"type"/utf8>>) of {ok, {node_seq, Type_nodes}} -> Type_strs = gleam@list:filter_map( Type_nodes, fun(N) -> case N of {node_str, S} -> {ok, S}; _ -> {error, nil} end end ), Has_null = gleam@list:contains(Type_strs, <<"null"/utf8>>), Non_null_types = gleam@list:filter( Type_strs, fun(S@1) -> S@1 /= <<"null"/utf8>> end ), case Non_null_types of [Single] -> {ok, {Single, {schema_metadata, erlang:element(2, Metadata), erlang:element(3, Metadata) orelse Has_null, erlang:element(4, Metadata), erlang:element(5, Metadata), erlang:element(6, Metadata), erlang:element(7, Metadata), erlang:element(8, Metadata), erlang:element(9, Metadata), erlang:element(10, Metadata), erlang:element(11, Metadata), erlang:element(12, Metadata), erlang:element(13, Metadata), erlang:element(14, Metadata)}}}; [] -> {ok, {<<"object"/utf8>>, {schema_metadata, erlang:element(2, Metadata), erlang:element(3, Metadata) orelse Has_null, erlang:element(4, Metadata), erlang:element(5, Metadata), erlang:element(6, Metadata), erlang:element(7, Metadata), erlang:element(8, Metadata), erlang:element(9, Metadata), erlang:element(10, Metadata), erlang:element(11, Metadata), erlang:element(12, Metadata), erlang:element(13, Metadata), erlang:element(14, Metadata)}}}; _ -> Updated_meta = {schema_metadata, erlang:element(2, Metadata), erlang:element(3, Metadata) orelse Has_null, erlang:element(4, Metadata), erlang:element(5, Metadata), erlang:element(6, Metadata), erlang:element(7, Metadata), erlang:element(8, Metadata), erlang:element(9, Metadata), erlang:element(10, Metadata), {some, Non_null_types}, erlang:element(12, Metadata), erlang:element(13, Metadata), erlang:element(14, Metadata)}, Primary = case Non_null_types of [First | _] -> First; [] -> <<"object"/utf8>> end, {ok, {Primary, Updated_meta}} end; {ok, {node_str, <<"null"/utf8>>}} -> {ok, {<<"object"/utf8>>, {schema_metadata, erlang:element(2, Metadata), true, erlang:element(4, Metadata), erlang:element(5, Metadata), erlang:element(6, Metadata), erlang:element(7, Metadata), erlang:element(8, Metadata), erlang:element(9, Metadata), erlang:element(10, Metadata), erlang:element(11, Metadata), erlang:element(12, Metadata), erlang:element(13, Metadata), erlang:element(14, Metadata)}}}; {ok, {node_str, Type_name}} -> {ok, {Type_name, Metadata}}; _ -> Type_name@1 = begin _pipe = yay:extract_optional_string(Node, <<"type"/utf8>>), _pipe@1 = gleam@result:unwrap(_pipe, none), gleam@option:unwrap(_pipe@1, <<"object"/utf8>>) end, {ok, {Type_name@1, Metadata}} end, fun(_use0) -> {Type_str, Metadata@1} = _use0, Format = begin _pipe@2 = yay:extract_optional_string(Node, <<"format"/utf8>>), gleam@result:unwrap(_pipe@2, none) end, case Type_str of <<"string"/utf8>> -> Enum_values = case yay:extract_string_list( Node, <<"enum"/utf8>> ) of {ok, Values} -> Values; _ -> [] end, Min_length = begin _pipe@3 = yay:extract_optional_int( Node, <<"minLength"/utf8>> ), gleam@result:unwrap(_pipe@3, none) end, Max_length = begin _pipe@4 = yay:extract_optional_int( Node, <<"maxLength"/utf8>> ), gleam@result:unwrap(_pipe@4, none) end, Pattern = begin _pipe@5 = yay:extract_optional_string( Node, <<"pattern"/utf8>> ), gleam@result:unwrap(_pipe@5, none) end, {ok, {string_schema, Metadata@1, Format, Enum_values, Min_length, Max_length, Pattern}}; <<"integer"/utf8>> -> Minimum = begin _pipe@6 = yay:extract_optional_int( Node, <<"minimum"/utf8>> ), gleam@result:unwrap(_pipe@6, none) end, Maximum = begin _pipe@7 = yay:extract_optional_int( Node, <<"maximum"/utf8>> ), gleam@result:unwrap(_pipe@7, none) end, Exclusive_minimum = begin _pipe@8 = yay:extract_optional_int( Node, <<"exclusiveMinimum"/utf8>> ), gleam@result:unwrap(_pipe@8, none) end, Exclusive_maximum = begin _pipe@9 = yay:extract_optional_int( Node, <<"exclusiveMaximum"/utf8>> ), gleam@result:unwrap(_pipe@9, none) end, Multiple_of = begin _pipe@10 = yay:extract_optional_int( Node, <<"multipleOf"/utf8>> ), gleam@result:unwrap(_pipe@10, none) end, {ok, {integer_schema, Metadata@1, Format, Minimum, Maximum, Exclusive_minimum, Exclusive_maximum, Multiple_of}}; <<"number"/utf8>> -> Minimum@1 = begin _pipe@11 = yay:extract_optional_float( Node, <<"minimum"/utf8>> ), gleam@result:unwrap(_pipe@11, none) end, Maximum@1 = begin _pipe@12 = yay:extract_optional_float( Node, <<"maximum"/utf8>> ), gleam@result:unwrap(_pipe@12, none) end, Exclusive_minimum@1 = begin _pipe@13 = yay:extract_optional_float( Node, <<"exclusiveMinimum"/utf8>> ), gleam@result:unwrap(_pipe@13, none) end, Exclusive_maximum@1 = begin _pipe@14 = yay:extract_optional_float( Node, <<"exclusiveMaximum"/utf8>> ), gleam@result:unwrap(_pipe@14, none) end, Multiple_of@1 = begin _pipe@15 = yay:extract_optional_float( Node, <<"multipleOf"/utf8>> ), gleam@result:unwrap(_pipe@15, none) end, {ok, {number_schema, Metadata@1, Format, Minimum@1, Maximum@1, Exclusive_minimum@1, Exclusive_maximum@1, Multiple_of@1}}; <<"boolean"/utf8>> -> {ok, {boolean_schema, Metadata@1}}; <<"array"/utf8>> -> gleam@result:'try'( case yay:select_sugar(Node, <<"items"/utf8>>) of {ok, Items_node} -> parse_schema_ref( Items_node, <>, Index ); _ -> {error, oaspec@openapi@diagnostic:missing_field( Path, <<"items"/utf8>>, oaspec@internal@openapi@location_index:lookup_field( Index, Path, <<"items"/utf8>> ) )} end, fun(Items) -> Min_items = begin _pipe@16 = yay:extract_optional_int( Node, <<"minItems"/utf8>> ), gleam@result:unwrap(_pipe@16, none) end, Max_items = begin _pipe@17 = yay:extract_optional_int( Node, <<"maxItems"/utf8>> ), gleam@result:unwrap(_pipe@17, none) end, Unique_items = begin _pipe@18 = yay:extract_optional_bool( Node, <<"uniqueItems"/utf8>> ), _pipe@19 = gleam@result:unwrap(_pipe@18, none), gleam@option:unwrap(_pipe@19, false) end, {ok, {array_schema, Metadata@1, Items, Min_items, Max_items, Unique_items}} end ); <<"object"/utf8>> -> gleam@result:'try'( parse_properties(Node, Path, Index), fun(Properties) -> Required = case yay:extract_string_list( Node, <<"required"/utf8>> ) of {ok, R} -> R; _ -> [] end, gleam@result:'try'( case yay:select_sugar( Node, <<"additionalProperties"/utf8>> ) of {ok, {node_bool, true}} -> {ok, untyped}; {ok, {node_bool, false}} -> {ok, forbidden}; {ok, Ap_node} -> gleam@result:'try'( parse_schema_ref( Ap_node, <>, Index ), fun(Sr) -> {ok, {typed, Sr}} end ); _ -> {ok, unspecified} end, fun(Additional_properties) -> Min_properties = begin _pipe@20 = yay:extract_optional_int( Node, <<"minProperties"/utf8>> ), gleam@result:unwrap(_pipe@20, none) end, Max_properties = begin _pipe@21 = yay:extract_optional_int( Node, <<"maxProperties"/utf8>> ), gleam@result:unwrap(_pipe@21, none) end, {ok, {object_schema, Metadata@1, Properties, Required, Additional_properties, Min_properties, Max_properties}} end ) end ); Unrecognized -> {error, oaspec@openapi@diagnostic:invalid_value( <>, <<<<"Unrecognized schema type '"/utf8, Unrecognized/binary>>/binary, "'. Supported types: string, integer, number, boolean, array, object."/utf8>>, oaspec@internal@openapi@location_index:lookup( Index, <> ) )} end end). -file("src/oaspec/internal/openapi/parser_schema.gleam", 454). ?DOC(false). -spec parse_properties( yay:node_(), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, gleam@dict:dict(binary(), oaspec@internal@openapi@schema:schema_ref())} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_properties(Node, Path, Index) -> case yay:select_sugar(Node, <<"properties"/utf8>>) of {ok, {node_map, Entries}} -> gleam@list:try_fold( Entries, maps:new(), fun(Acc, Entry) -> {Key_node, Value_node} = Entry, case Key_node of {node_str, Prop_name} -> gleam@result:'try'( parse_schema_ref( Value_node, <<<>/binary, Prop_name/binary>>, Index ), fun(Schema_ref) -> {ok, gleam@dict:insert( Acc, Prop_name, Schema_ref )} end ); _ -> {ok, Acc} end end ); _ -> {ok, maps:new()} end. -file("src/oaspec/internal/openapi/parser_schema.gleam", 23). ?DOC(false). -spec parse_schema_ref( yay:node_(), binary(), oaspec@internal@openapi@location_index:location_index() ) -> {ok, oaspec@internal@openapi@schema:schema_ref()} | {error, oaspec@openapi@diagnostic:diagnostic()}. parse_schema_ref(Node, Path, Index) -> case yay:extract_optional_string(Node, <<"$ref"/utf8>>) of {ok, {some, Ref}} -> {ok, oaspec@internal@openapi@schema:make_reference(Ref)}; _ -> gleam@result:'try'( parse_schema_object(Node, Path, Index), fun(Schema_obj) -> {ok, {inline, Schema_obj}} end ) end.