Packages

OpenAPI code generation for Gleam — parse specs, generate types, routes, clients, and React Query/SWR hooks

Current section

Files

Jump to
nori src nori@codegen@ir_builder.erl
Raw

src/nori@codegen@ir_builder.erl

-module(nori@codegen@ir_builder).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/nori/codegen/ir_builder.gleam").
-export([build/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.
?MODULEDOC(
" Transforms an OpenAPI Document into a language-agnostic CodegenIR.\n"
"\n"
" This is the bridge between the parsed OpenAPI spec and code generators.\n"
).
-file("src/nori/codegen/ir_builder.gleam", 45).
-spec extract_base_url(nori@document:document()) -> gleam@option:option(binary()).
extract_base_url(Doc) ->
case erlang:element(5, Doc) of
[First | _] ->
{some, erlang:element(2, First)};
[] ->
none
end.
-file("src/nori/codegen/ir_builder.gleam", 290).
-spec string_format_to_typeref(gleam@option:option(binary())) -> nori@codegen@ir:type_ref().
string_format_to_typeref(Format) ->
case Format of
{some, <<"date-time"/utf8>>} ->
{primitive, p_date_time};
{some, <<"date"/utf8>>} ->
{primitive, p_date};
{some, <<"binary"/utf8>>} ->
{primitive, p_binary};
_ ->
{primitive, p_string}
end.
-file("src/nori/codegen/ir_builder.gleam", 306).
-spec ref_to_typeref(binary()) -> nori@codegen@ir:type_ref().
ref_to_typeref(Ref_str) ->
case gleam@string:split(Ref_str, <<"/"/utf8>>) of
[_, _, _, Name] ->
{named, Name};
_ ->
{named, Ref_str}
end.
-file("src/nori/codegen/ir_builder.gleam", 383).
-spec parse_http_method(binary()) -> gleam@option:option(nori@codegen@ir:http_method()).
parse_http_method(Method) ->
case Method of
<<"get"/utf8>> ->
{some, get};
<<"post"/utf8>> ->
{some, post};
<<"put"/utf8>> ->
{some, put};
<<"delete"/utf8>> ->
{some, delete};
<<"patch"/utf8>> ->
{some, patch};
<<"head"/utf8>> ->
{some, head};
<<"options"/utf8>> ->
{some, options};
_ ->
none
end.
-file("src/nori/codegen/ir_builder.gleam", 396).
-spec method_to_string(nori@codegen@ir:http_method()) -> binary().
method_to_string(Method) ->
case Method of
get ->
<<"get"/utf8>>;
post ->
<<"post"/utf8>>;
put ->
<<"put"/utf8>>;
delete ->
<<"delete"/utf8>>;
patch ->
<<"patch"/utf8>>;
head ->
<<"head"/utf8>>;
options ->
<<"options"/utf8>>
end.
-file("src/nori/codegen/ir_builder.gleam", 442).
-spec param_location_to_ir(nori@parameter:parameter_location()) -> nori@codegen@ir:param_location().
param_location_to_ir(Loc) ->
case Loc of
in_path ->
path_param;
in_query ->
query_param;
in_header ->
header_param;
in_cookie ->
cookie_param
end.
-file("src/nori/codegen/ir_builder.gleam", 603).
-spec api_key_location_to_ir(nori@security:api_key_location()) -> nori@codegen@ir:api_key_location_i_r().
api_key_location_to_ir(Loc) ->
case Loc of
in_header ->
in_header;
in_query ->
in_query;
in_cookie ->
in_cookie
end.
-file("src/nori/codegen/ir_builder.gleam", 581).
-spec security_scheme_to_ir(binary(), nori@security:security_scheme()) -> nori@codegen@ir:security_scheme_i_r().
security_scheme_to_ir(Name, Scheme) ->
case Scheme of
{http_security_scheme, <<"bearer"/utf8>>, Fmt, _, _} ->
{bearer_auth, Name, Fmt};
{http_security_scheme, <<"basic"/utf8>>, _, _, _} ->
{basic_auth, Name};
{http_security_scheme, _, _, _, _} ->
{basic_auth, Name};
{api_key_security_scheme, Param_name, Loc, _, _} ->
{api_key_auth, Name, Param_name, api_key_location_to_ir(Loc)};
{o_auth2_security_scheme, _, _, _} ->
{o_auth2_auth, Name};
{open_id_connect_security_scheme, Url, _, _} ->
{open_id_connect_auth, Name, Url};
{mutual_tls_security_scheme, _, _} ->
{o_auth2_auth, Name}
end.
-file("src/nori/codegen/ir_builder.gleam", 565).
-spec build_security_schemes(nori@document:document()) -> list(nori@codegen@ir:security_scheme_i_r()).
build_security_schemes(Doc) ->
case erlang:element(8, Doc) of
none ->
[];
{some, Components} ->
_pipe = maps:to_list(erlang:element(8, Components)),
gleam@list:filter_map(
_pipe,
fun(Pair) ->
{Name, Ref_scheme} = Pair,
case Ref_scheme of
{reference, _} ->
{error, nil};
{inline, Scheme} ->
{ok, security_scheme_to_ir(Name, Scheme)}
end
end
)
end.
-file("src/nori/codegen/ir_builder.gleam", 611).
-spec build_security_requirements(
list(gleam@dict:dict(binary(), list(binary())))
) -> list(nori@codegen@ir:security_requirement_i_r()).
build_security_requirements(Reqs) ->
gleam@list:flat_map(Reqs, fun(Req) -> _pipe = maps:to_list(Req),
gleam@list:map(
_pipe,
fun(Pair) ->
{Scheme_name, Scopes} = Pair,
{security_requirement_i_r, Scheme_name, Scopes}
end
) end).
-file("src/nori/codegen/ir_builder.gleam", 629).
?DOC(
" Attempt to extract a string from a gleam/json Json value.\n"
" Json values from enum_values are opaque; we convert via string representation.\n"
).
-spec json_to_string(gleam@json:json()) -> gleam@option:option(binary()).
json_to_string(Json_val) ->
Str = gleam@json:to_string(Json_val),
case gleam_stdlib:string_starts_with(Str, <<"\""/utf8>>) andalso gleam_stdlib:string_ends_with(
Str,
<<"\""/utf8>>
) of
true ->
Trimmed = begin
_pipe = Str,
_pipe@1 = gleam@string:drop_start(_pipe, 1),
gleam@string:drop_end(_pipe@1, 1)
end,
{some, Trimmed};
false ->
{some, Str}
end.
-file("src/nori/codegen/ir_builder.gleam", 299).
-spec ref_schema_to_typeref(nori@reference:ref(nori@schema:schema())) -> nori@codegen@ir:type_ref().
ref_schema_to_typeref(Ref_schema) ->
case Ref_schema of
{reference, Ref_str} ->
ref_to_typeref(Ref_str);
{inline, S} ->
schema_type_to_typeref(S)
end.
-file("src/nori/codegen/ir_builder.gleam", 218).
-spec schema_type_to_typeref(nori@schema:schema()) -> nori@codegen@ir:type_ref().
schema_type_to_typeref(S) ->
case erlang:element(7, S) of
{some, Ref_str} ->
ref_to_typeref(Ref_str);
none ->
case erlang:element(28, S) of
none ->
unknown;
{some, {single_type, Json_type}} ->
json_type_to_typeref(Json_type, S);
{some, {multiple_types, Types}} ->
multiple_types_to_typeref(Types, S)
end
end.
-file("src/nori/codegen/ir_builder.gleam", 263).
-spec multiple_types_to_typeref(
list(nori@schema:json_type()),
nori@schema:schema()
) -> nori@codegen@ir:type_ref().
multiple_types_to_typeref(Types, S) ->
Non_null = gleam@list:filter(Types, fun(T) -> case T of
type_null ->
false;
_ ->
true
end end),
Has_null = gleam@list:any(Types, fun(T@1) -> case T@1 of
type_null ->
true;
_ ->
false
end end),
case {Non_null, Has_null} of
{[Single_type], true} ->
{nullable, json_type_to_typeref(Single_type, S)};
{[Single_type@1], false} ->
json_type_to_typeref(Single_type@1, S);
{_, true} ->
{nullable, unknown};
{_, false} ->
unknown
end.
-file("src/nori/codegen/ir_builder.gleam", 234).
-spec json_type_to_typeref(nori@schema:json_type(), nori@schema:schema()) -> nori@codegen@ir:type_ref().
json_type_to_typeref(Json_type, S) ->
case Json_type of
type_string ->
string_format_to_typeref(erlang:element(48, S));
type_integer ->
{primitive, p_int};
type_number ->
{primitive, p_float};
type_boolean ->
{primitive, p_bool};
type_null ->
{primitive, p_unit};
json_type_array ->
case erlang:element(20, S) of
{some, Items_ref} ->
{array, ref_schema_to_typeref(Items_ref)};
none ->
{array, unknown}
end;
type_object ->
case erlang:element(24, S) of
{some, {additional_properties_schema, Val_ref}} ->
{dict,
{primitive, p_string},
ref_schema_to_typeref(Val_ref)};
_ ->
case gleam@dict:is_empty(erlang:element(22, S)) of
true ->
unknown;
false ->
unknown
end
end
end.
-file("src/nori/codegen/ir_builder.gleam", 116).
-spec build_record_type(binary(), nori@schema:schema()) -> nori@codegen@ir:type_def().
build_record_type(Name, S) ->
Fields = begin
_pipe = maps:to_list(erlang:element(22, S)),
gleam@list:map(
_pipe,
fun(Pair) ->
{Field_name, Ref_schema} = Pair,
Is_required = gleam@list:contains(
erlang:element(46, S),
Field_name
),
{Type_ref, Description, Read_only, Write_only} = case Ref_schema of
{reference, Ref_str} ->
{ref_to_typeref(Ref_str), none, false, false};
{inline, Field_schema} ->
Tr = schema_type_to_typeref(Field_schema),
Ro = gleam@option:unwrap(
erlang:element(56, Field_schema),
false
),
Wo = gleam@option:unwrap(
erlang:element(57, Field_schema),
false
),
{Tr, erlang:element(53, Field_schema), Ro, Wo}
end,
{field,
Field_name,
Type_ref,
Is_required,
Description,
Read_only,
Write_only}
end
)
end,
{record_type, Name, Fields, erlang:element(53, S)}.
-file("src/nori/codegen/ir_builder.gleam", 148).
-spec build_all_of_type(binary(), nori@schema:schema()) -> nori@codegen@ir:type_def().
build_all_of_type(Name, S) ->
Fields = gleam@list:flat_map(
erlang:element(11, S),
fun(Ref_schema) -> case Ref_schema of
{reference, _} ->
[];
{inline, Sub_schema} ->
_pipe = maps:to_list(erlang:element(22, Sub_schema)),
gleam@list:map(
_pipe,
fun(Pair) ->
{Field_name, Prop_ref} = Pair,
Is_required = gleam@list:contains(
erlang:element(46, Sub_schema),
Field_name
),
{Type_ref, Description, Read_only, Write_only} = case Prop_ref of
{reference, Ref_str} ->
{ref_to_typeref(Ref_str),
none,
false,
false};
{inline, Field_schema} ->
Tr = schema_type_to_typeref(Field_schema),
Ro = gleam@option:unwrap(
erlang:element(56, Field_schema),
false
),
Wo = gleam@option:unwrap(
erlang:element(57, Field_schema),
false
),
{Tr,
erlang:element(53, Field_schema),
Ro,
Wo}
end,
{field,
Field_name,
Type_ref,
Is_required,
Description,
Read_only,
Write_only}
end
)
end end
),
{record_type, Name, Fields, erlang:element(53, S)}.
-file("src/nori/codegen/ir_builder.gleam", 189).
-spec build_union_type(
binary(),
list(nori@reference:ref(nori@schema:schema())),
gleam@option:option(nori@schema:discriminator()),
gleam@option:option(binary())
) -> nori@codegen@ir:type_def().
build_union_type(Name, Members, Discriminator, Description) ->
Member_refs = gleam@list:map(Members, fun(Ref_schema) -> case Ref_schema of
{reference, Ref_str} ->
ref_to_typeref(Ref_str);
{inline, Sub_schema} ->
schema_type_to_typeref(Sub_schema)
end end),
Disc = case Discriminator of
{some, D} ->
{some, erlang:element(2, D)};
none ->
none
end,
{union_type, Name, Member_refs, Disc, Description}.
-file("src/nori/codegen/ir_builder.gleam", 69).
-spec schema_to_typedef(binary(), nori@schema:schema()) -> nori@codegen@ir:type_def().
schema_to_typedef(Name, S) ->
case erlang:element(30, S) of
{some, Values} ->
Variants = gleam@list:filter_map(
Values,
fun(Json_val) -> case json_to_string(Json_val) of
{some, Str} ->
{ok, {enum_variant, Str, Str}};
none ->
{error, nil}
end end
),
{enum_type, Name, Variants, erlang:element(53, S)};
none ->
case erlang:element(11, S) of
[_ | _] ->
build_all_of_type(Name, S);
[] ->
case {erlang:element(13, S), erlang:element(12, S)} of
{[_ | _], _} ->
build_union_type(
Name,
erlang:element(13, S),
erlang:element(59, S),
erlang:element(53, S)
);
{_, [_ | _]} ->
build_union_type(
Name,
erlang:element(12, S),
erlang:element(59, S),
erlang:element(53, S)
);
{_, _} ->
case gleam@dict:is_empty(erlang:element(22, S)) of
false ->
build_record_type(Name, S);
true ->
Target = schema_type_to_typeref(S),
{alias_type,
Name,
Target,
erlang:element(53, S)}
end
end
end
end.
-file("src/nori/codegen/ir_builder.gleam", 56).
-spec build_types(nori@document:document()) -> list(nori@codegen@ir:type_def()).
build_types(Doc) ->
case erlang:element(8, Doc) of
none ->
[];
{some, Components} ->
_pipe = maps:to_list(erlang:element(2, Components)),
gleam@list:map(
_pipe,
fun(Pair) ->
{Name, Schema} = Pair,
schema_to_typedef(Name, Schema)
end
)
end.
-file("src/nori/codegen/ir_builder.gleam", 425).
-spec build_param(nori@parameter:parameter()) -> nori@codegen@ir:endpoint_param().
build_param(Param) ->
Location = param_location_to_ir(erlang:element(3, Param)),
Type_ref = case erlang:element(11, Param) of
{some, Ref_schema} ->
ref_schema_to_typeref(Ref_schema);
none ->
unknown
end,
Required = gleam@option:unwrap(erlang:element(5, Param), false),
{endpoint_param,
erlang:element(2, Param),
Location,
Type_ref,
Required,
erlang:element(4, Param)}.
-file("src/nori/codegen/ir_builder.gleam", 412).
-spec build_params(
list(nori@reference:ref(nori@parameter:parameter())),
list(nori@reference:ref(nori@parameter:parameter()))
) -> list(nori@codegen@ir:endpoint_param()).
build_params(Path_level, Op_level) ->
All_params = lists:append(Path_level, Op_level),
gleam@list:filter_map(All_params, fun(Ref_param) -> case Ref_param of
{reference, _} ->
{error, nil};
{inline, Param} ->
{ok, build_param(Param)}
end end).
-file("src/nori/codegen/ir_builder.gleam", 455).
-spec build_request_body(
gleam@option:option(nori@reference:ref(nori@request_body:request_body()))
) -> gleam@option:option(nori@codegen@ir:request_body_i_r()).
build_request_body(Body) ->
case Body of
none ->
none;
{some, {reference, _}} ->
none;
{some, {inline, Rb}} ->
Content_type = <<"application/json"/utf8>>,
case gleam_stdlib:map_get(erlang:element(3, Rb), Content_type) of
{ok, Media_type} ->
Type_ref = case erlang:element(2, Media_type) of
{some, Ref_schema} ->
ref_schema_to_typeref(Ref_schema);
none ->
unknown
end,
Required = gleam@option:unwrap(erlang:element(4, Rb), false),
{some, {request_body_i_r, Content_type, Type_ref, Required}};
{error, _} ->
case maps:to_list(erlang:element(3, Rb)) of
[{Ct, Media_type@1} | _] ->
Type_ref@1 = case erlang:element(2, Media_type@1) of
{some, Ref_schema@1} ->
ref_schema_to_typeref(Ref_schema@1);
none ->
unknown
end,
Required@1 = gleam@option:unwrap(
erlang:element(4, Rb),
false
),
{some,
{request_body_i_r, Ct, Type_ref@1, Required@1}};
[] ->
none
end
end
end.
-file("src/nori/codegen/ir_builder.gleam", 517).
-spec build_response(binary(), nori@response:response()) -> nori@codegen@ir:response_i_r().
build_response(Status_code, Resp) ->
case gleam_stdlib:map_get(
erlang:element(4, Resp),
<<"application/json"/utf8>>
) of
{ok, Media_type} ->
Type_ref = case erlang:element(2, Media_type) of
{some, Ref_schema} ->
{some, ref_schema_to_typeref(Ref_schema)};
none ->
none
end,
{response_i_r,
Status_code,
erlang:element(2, Resp),
{some, <<"application/json"/utf8>>},
Type_ref};
{error, _} ->
case maps:to_list(erlang:element(4, Resp)) of
[{Ct, Media_type@1} | _] ->
Type_ref@1 = case erlang:element(2, Media_type@1) of
{some, Ref_schema@1} ->
{some, ref_schema_to_typeref(Ref_schema@1)};
none ->
none
end,
{response_i_r,
Status_code,
erlang:element(2, Resp),
{some, Ct},
Type_ref@1};
[] ->
{response_i_r,
Status_code,
erlang:element(2, Resp),
none,
none}
end
end.
-file("src/nori/codegen/ir_builder.gleam", 504).
-spec build_responses(
gleam@dict:dict(binary(), nori@reference:ref(nori@response:response()))
) -> list(nori@codegen@ir:response_i_r()).
build_responses(Responses) ->
_pipe = maps:to_list(Responses),
gleam@list:filter_map(
_pipe,
fun(Pair) ->
{Status_code, Ref_response} = Pair,
case Ref_response of
{reference, _} ->
{error, nil};
{inline, Resp} ->
{ok, build_response(Status_code, Resp)}
end
end
).
-file("src/nori/codegen/ir_builder.gleam", 351).
-spec build_endpoint(
binary(),
nori@codegen@ir:http_method(),
nori@operation:operation(),
list(nori@reference:ref(nori@parameter:parameter()))
) -> nori@codegen@ir:endpoint().
build_endpoint(Path, Method, Op, Path_level_params) ->
Operation_id = gleam@option:unwrap(
erlang:element(6, Op),
<<<<(method_to_string(Method))/binary, "_"/utf8>>/binary, Path/binary>>
),
Params = build_params(Path_level_params, erlang:element(7, Op)),
Request_body = build_request_body(erlang:element(8, Op)),
Responses = build_responses(erlang:element(9, Op)),
Deprecated = gleam@option:unwrap(erlang:element(11, Op), false),
Endpoint_security = case erlang:element(12, Op) of
none ->
none;
{some, Reqs} ->
{some, build_security_requirements(Reqs)}
end,
{endpoint,
Operation_id,
Method,
Path,
erlang:element(3, Op),
erlang:element(4, Op),
erlang:element(2, Op),
Params,
Request_body,
Responses,
Deprecated,
Endpoint_security}.
-file("src/nori/codegen/ir_builder.gleam", 335).
-spec build_endpoints_from_path_item(binary(), nori@operation:path_item()) -> list(nori@codegen@ir:endpoint()).
build_endpoints_from_path_item(Path, Item) ->
Path_level_params = erlang:element(14, Item),
_pipe = nori@paths:get_operations(Item),
gleam@list:filter_map(
_pipe,
fun(Pair) ->
{Method_str, Op} = Pair,
case parse_http_method(Method_str) of
{some, Method} ->
{ok, build_endpoint(Path, Method, Op, Path_level_params)};
none ->
{error, nil}
end
end
).
-file("src/nori/codegen/ir_builder.gleam", 318).
-spec build_endpoints(nori@document:document()) -> list(nori@codegen@ir:endpoint()).
build_endpoints(Doc) ->
case erlang:element(6, Doc) of
none ->
[];
{some, Paths_dict} ->
_pipe = maps:to_list(Paths_dict),
gleam@list:flat_map(
_pipe,
fun(Pair) ->
{Path, Path_item_ref} = Pair,
case Path_item_ref of
{reference, _} ->
[];
{inline, Path_item} ->
build_endpoints_from_path_item(Path, Path_item)
end
end
)
end.
-file("src/nori/codegen/ir_builder.gleam", 22).
?DOC(" Builds a CodegenIR from a parsed OpenAPI Document.\n").
-spec build(nori@document:document()) -> nori@codegen@ir:codegen_i_r().
build(Doc) ->
Types = build_types(Doc),
Endpoints = build_endpoints(Doc),
Base_url = extract_base_url(Doc),
Security_schemes = build_security_schemes(Doc),
Global_security = build_security_requirements(erlang:element(9, Doc)),
{codegen_i_r,
erlang:element(2, erlang:element(3, Doc)),
erlang:element(7, erlang:element(3, Doc)),
Base_url,
Types,
Endpoints,
Security_schemes,
Global_security}.