Packages
oaspec
0.24.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.0
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.1
0.58.0
0.57.0
0.56.0
0.55.0
0.54.0
0.53.0
0.52.0
0.51.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.3
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.1.3
Generate Gleam code from OpenAPI 3.x specifications
Current section
Files
Jump to
Current section
Files
src/oaspec@codegen@server.erl
-module(oaspec@codegen@server).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/codegen/server.gleam").
-export([generate/1]).
-export_type([header_spec/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 header_spec() :: {header_spec, binary(), binary(), binary(), boolean()}.
-file("src/oaspec/codegen/server.gleam", 135).
?DOC(" Generate a single delegator function in `handlers_generated.gleam`.\n").
-spec generate_handler_delegator(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved())
) -> gleam@string_tree:string_tree().
generate_handler_delegator(Sb, Op_id, Operation) ->
Fn_name = oaspec@util@naming:operation_to_function_name(Op_id),
Request_type = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
"Request"/utf8>>,
Response_type = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
"Response"/utf8>>,
Has_params = not gleam@list:is_empty(erlang:element(6, Operation)) orelse gleam@option:is_some(
erlang:element(7, Operation)
),
case Has_params of
true ->
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:line(
_pipe,
<<<<<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(state: handlers.State, req: request_types."/utf8>>/binary,
Request_type/binary>>/binary,
") -> response_types."/utf8>>/binary,
Response_type/binary>>/binary,
" {"/utf8>>
),
_pipe@2 = oaspec@util@string_extra:indent(
_pipe@1,
1,
<<<<"handlers."/utf8, Fn_name/binary>>/binary,
"(state, req)"/utf8>>
),
_pipe@3 = oaspec@util@string_extra:line(_pipe@2, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@3);
false ->
_pipe@4 = Sb,
_pipe@5 = oaspec@util@string_extra:line(
_pipe@4,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(state: handlers.State) -> response_types."/utf8>>/binary,
Response_type/binary>>/binary,
" {"/utf8>>
),
_pipe@6 = oaspec@util@string_extra:indent(
_pipe@5,
1,
<<<<"handlers."/utf8, Fn_name/binary>>/binary, "(state)"/utf8>>
),
_pipe@7 = oaspec@util@string_extra:line(_pipe@6, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@7)
end.
-file("src/oaspec/codegen/server.gleam", 112).
?DOC(
" Generate `handlers_generated.gleam`, a sealed delegator that the\n"
" router imports. Each operation forwards to `handlers.<op_name>(req)`.\n"
" Issue #247: this file carries the `// DO NOT EDIT` banner and is\n"
" always overwritten so router/handler wiring stays in sync with the\n"
" spec without touching the user's `handlers.gleam`.\n"
).
-spec generate_handlers_generated(
oaspec@codegen@context:context(),
list({binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
oaspec@openapi@spec:http_method()})
) -> binary().
generate_handlers_generated(Ctx, Operations) ->
Pkg = oaspec@config:package(oaspec@codegen@context:config(Ctx)),
Sb = begin
_pipe = oaspec@util@string_extra:file_header(<<"0.24.0"/utf8>>),
oaspec@util@string_extra:imports(
_pipe,
[<<Pkg/binary, "/handlers"/utf8>>,
<<Pkg/binary, "/request_types"/utf8>>,
<<Pkg/binary, "/response_types"/utf8>>]
)
end,
Sb@2 = gleam@list:fold(
Operations,
Sb,
fun(Sb@1, Op) ->
{Op_id, Operation, _, _} = Op,
generate_handler_delegator(Sb@1, Op_id, Operation)
end
),
oaspec@util@string_extra:to_string(Sb@2).
-file("src/oaspec/codegen/server.gleam", 179).
?DOC(" Generate a single handler stub.\n").
-spec generate_handler(
gleam@string_tree:string_tree(),
binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_handler(Sb, Op_id, Operation, _) ->
Fn_name = oaspec@util@naming:operation_to_function_name(Op_id),
Request_type = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
"Request"/utf8>>,
Response_type = <<(oaspec@util@naming:schema_to_type_name(Op_id))/binary,
"Response"/utf8>>,
Sb@1 = case erlang:element(3, Operation) of
{some, Summary} ->
_pipe = Sb,
oaspec@util@string_extra:doc_comment(_pipe, Summary);
_ ->
Sb
end,
Sb@2 = case erlang:element(4, Operation) of
{some, Desc} ->
_pipe@1 = Sb@1,
oaspec@util@string_extra:doc_comment(_pipe@1, Desc);
_ ->
Sb@1
end,
Has_params = not gleam@list:is_empty(erlang:element(6, Operation)) orelse gleam@option:is_some(
erlang:element(7, Operation)
),
Sb@3 = case Has_params of
true ->
_pipe@2 = Sb@2,
oaspec@util@string_extra:line(
_pipe@2,
<<<<<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(state: State, req: request_types."/utf8>>/binary,
Request_type/binary>>/binary,
") -> response_types."/utf8>>/binary,
Response_type/binary>>/binary,
" {"/utf8>>
);
false ->
_pipe@3 = Sb@2,
oaspec@util@string_extra:line(
_pipe@3,
<<<<<<<<"pub fn "/utf8, Fn_name/binary>>/binary,
"(state: State) -> response_types."/utf8>>/binary,
Response_type/binary>>/binary,
" {"/utf8>>
)
end,
Sb@4 = begin
_pipe@4 = Sb@3,
oaspec@util@string_extra:indent(_pipe@4, 1, <<"let _ = state"/utf8>>)
end,
Sb@5 = case Has_params of
true ->
_pipe@5 = Sb@4,
oaspec@util@string_extra:indent(_pipe@5, 1, <<"let _ = req"/utf8>>);
false ->
Sb@4
end,
_pipe@6 = Sb@5,
_pipe@7 = oaspec@util@string_extra:indent(
_pipe@6,
1,
<<<<"panic as \"unimplemented: "/utf8, Fn_name/binary>>/binary,
"\""/utf8>>
),
_pipe@8 = oaspec@util@string_extra:line(_pipe@7, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@8).
-file("src/oaspec/codegen/server.gleam", 65).
?DOC(
" Generate the user-owned `handlers.gleam`. Emitted only on first\n"
" generation; the writer's `SkipIfExists` mode prevents subsequent\n"
" runs from clobbering the user's implementation. The `// DO NOT EDIT`\n"
" banner is intentionally absent — the user owns this file.\n"
).
-spec generate_handlers(
oaspec@codegen@context:context(),
list({binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
oaspec@openapi@spec:http_method()})
) -> binary().
generate_handlers(Ctx, Operations) ->
Sb = begin
_pipe = oaspec@util@string_extra:line(
oaspec@util@string_extra:new(),
<<"//// Implement these handler functions. This file is emitted once"/utf8>>
),
_pipe@1 = oaspec@util@string_extra:line(
_pipe,
<<"//// by `oaspec generate` and skipped on subsequent runs, so your"/utf8>>
),
_pipe@2 = oaspec@util@string_extra:line(
_pipe@1,
<<"//// edits survive regeneration. Router wiring lives in"/utf8>>
),
_pipe@3 = oaspec@util@string_extra:line(
_pipe@2,
<<"//// `handlers_generated.gleam`, which delegates here."/utf8>>
),
_pipe@4 = oaspec@util@string_extra:blank_line(_pipe@3),
_pipe@5 = oaspec@util@string_extra:imports(
_pipe@4,
[<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/request_types"/utf8>>,
<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/response_types"/utf8>>]
),
_pipe@6 = oaspec@util@string_extra:doc_comment(
_pipe@5,
<<"Application state passed to every handler."/utf8>>
),
_pipe@7 = oaspec@util@string_extra:doc_comment(
_pipe@6,
<<"Add fields here for DB connections, config, loggers, etc. Construct a value of this type in your `main` and pass it to `router.route` as the first argument."/utf8>>
),
_pipe@8 = oaspec@util@string_extra:line(
_pipe@7,
<<"pub type State {"/utf8>>
),
_pipe@9 = oaspec@util@string_extra:indent(_pipe@8, 1, <<"State"/utf8>>),
_pipe@10 = oaspec@util@string_extra:line(_pipe@9, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@10)
end,
Sb@2 = gleam@list:fold(
Operations,
Sb,
fun(Sb@1, Op) ->
{Op_id, Operation, _, _} = Op,
generate_handler(Sb@1, Op_id, Operation, Ctx)
end
),
oaspec@util@string_extra:to_string(Sb@2).
-file("src/oaspec/codegen/server.gleam", 979).
-spec route_arg_name(binary(), boolean()) -> binary().
route_arg_name(Name, Used) ->
gleam@bool:guard(Used, Name, fun() -> <<"_"/utf8, Name/binary>> end).
-file("src/oaspec/codegen/server.gleam", 984).
-spec generate_cookie_lookup(gleam@string_tree:string_tree()) -> gleam@string_tree:string_tree().
generate_cookie_lookup(Sb) ->
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:doc_comment(
_pipe,
<<"Extract a cookie value from the Cookie header."/utf8>>
),
_pipe@2 = oaspec@util@string_extra:line(
_pipe@1,
<<"fn cookie_lookup(headers: Dict(String, String), key: String) -> Result(String, Nil) {"/utf8>>
),
_pipe@3 = oaspec@util@string_extra:indent(
_pipe@2,
1,
<<"case dict.get(headers, \"cookie\") {"/utf8>>
),
_pipe@4 = oaspec@util@string_extra:indent(_pipe@3, 2, <<"Ok(raw) ->"/utf8>>),
_pipe@5 = oaspec@util@string_extra:indent(
_pipe@4,
3,
<<"list.find_map(string.split(raw, \";\"), fn(part) {"/utf8>>
),
_pipe@6 = oaspec@util@string_extra:indent(
_pipe@5,
4,
<<"let trimmed = string.trim(part)"/utf8>>
),
_pipe@7 = oaspec@util@string_extra:indent(
_pipe@6,
4,
<<"case string.split_once(trimmed, on: \"=\") {"/utf8>>
),
_pipe@8 = oaspec@util@string_extra:indent(
_pipe@7,
5,
<<"Ok(#(cookie_key, cookie_value)) ->"/utf8>>
),
_pipe@9 = oaspec@util@string_extra:indent(
_pipe@8,
6,
<<"case string.trim(cookie_key) == key {"/utf8>>
),
_pipe@10 = oaspec@util@string_extra:indent(
_pipe@9,
7,
<<"True -> uri.percent_decode(string.trim(cookie_value))"/utf8>>
),
_pipe@11 = oaspec@util@string_extra:indent(
_pipe@10,
7,
<<"False -> Error(Nil)"/utf8>>
),
_pipe@12 = oaspec@util@string_extra:indent(_pipe@11, 6, <<"}"/utf8>>),
_pipe@13 = oaspec@util@string_extra:indent(
_pipe@12,
5,
<<"Error(_) -> Error(Nil)"/utf8>>
),
_pipe@14 = oaspec@util@string_extra:indent(_pipe@13, 4, <<"}"/utf8>>),
_pipe@15 = oaspec@util@string_extra:indent(_pipe@14, 3, <<"})"/utf8>>),
_pipe@16 = oaspec@util@string_extra:indent(
_pipe@15,
2,
<<"Error(_) -> Error(Nil)"/utf8>>
),
_pipe@17 = oaspec@util@string_extra:indent(_pipe@16, 1, <<"}"/utf8>>),
_pipe@18 = oaspec@util@string_extra:line(_pipe@17, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@18).
-file("src/oaspec/codegen/server.gleam", 1402).
?DOC(
" True when this query param is treated as a list (explode=true array).\n"
" Matches the helper used by `query_required_expr` so the open/close\n"
" scaffolding stays in sync with the value expression.\n"
).
-spec query_param_explode_array(
oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved())
) -> boolean().
query_param_explode_array(P) ->
case oaspec@openapi@spec:parameter_schema(P) of
{some, {inline, {array_schema, _, _, _, _, _}}} ->
case erlang:element(8, P) of
{some, false} ->
false;
_ ->
true
end;
_ ->
false
end.
-file("src/oaspec/codegen/server.gleam", 1482).
-spec array_int_parse_expr(
binary(),
oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()),
binary()
) -> binary().
array_int_parse_expr(Raw_var, P, Delim) ->
case erlang:element(8, P) of
{some, false} ->
<<<<<<<<"list.try_map(list.map(string.split("/utf8, Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Delim/binary>>/binary,
"\"), string.trim), int.parse)"/utf8>>;
_ ->
<<<<"list.try_map(list.map("/utf8, Raw_var/binary>>/binary,
", string.trim), int.parse)"/utf8>>
end.
-file("src/oaspec/codegen/server.gleam", 1498).
-spec array_float_parse_expr(
binary(),
oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()),
binary()
) -> binary().
array_float_parse_expr(Raw_var, P, Delim) ->
case erlang:element(8, P) of
{some, false} ->
<<<<<<<<"list.try_map(list.map(string.split("/utf8, Raw_var/binary>>/binary,
", \""/utf8>>/binary,
Delim/binary>>/binary,
"\"), string.trim), float.parse)"/utf8>>;
_ ->
<<<<"list.try_map(list.map("/utf8, Raw_var/binary>>/binary,
", string.trim), float.parse)"/utf8>>
end.
-file("src/oaspec/codegen/server.gleam", 1419).
?DOC(
" Open the secondary parse case for required query params that need it.\n"
" - scalar Integer/Number → `case int.parse(<raw>) { Ok(<raw>_parsed) -> {`\n"
" - array of Integer/Number with explode=true → `case list.try_map(<raw>, int.parse) { Ok(<raw>_parsed_list) -> {`\n"
" - array of Integer/Number with explode=false → split first, then try_map.\n"
" String / Bool / array-of-string / array-of-bool need no extra case.\n"
).
-spec query_required_open_parse_case(
gleam@string_tree:string_tree(),
oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
query_required_open_parse_case(Sb, P, Ctx) ->
Raw_var = <<(oaspec@util@naming:to_snake_case(erlang:element(2, P)))/binary,
"_raw"/utf8>>,
Delim = case erlang:element(7, P) of
{some, pipe_delimited_style} ->
<<"|"/utf8>>;
{some, space_delimited_style} ->
<<" "/utf8>>;
_ ->
<<","/utf8>>
end,
case oaspec@openapi@spec:parameter_schema(P) of
{some, {inline, {integer_schema, _, _, _, _, _, _, _}}} ->
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:indent(
_pipe,
3,
<<<<"case int.parse("/utf8, Raw_var/binary>>/binary,
") {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@1,
4,
<<<<"Ok("/utf8, Raw_var/binary>>/binary, "_parsed) -> {"/utf8>>
);
{some, {inline, {number_schema, _, _, _, _, _, _, _}}} ->
_pipe@2 = Sb,
_pipe@3 = oaspec@util@string_extra:indent(
_pipe@2,
3,
<<<<"case float.parse("/utf8, Raw_var/binary>>/binary,
") {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@3,
4,
<<<<"Ok("/utf8, Raw_var/binary>>/binary, "_parsed) -> {"/utf8>>
);
{some,
{inline,
{array_schema,
_,
{inline, {integer_schema, _, _, _, _, _, _, _}},
_,
_,
_}}} ->
Parse_expr = array_int_parse_expr(Raw_var, P, Delim),
_pipe@4 = Sb,
_pipe@5 = oaspec@util@string_extra:indent(
_pipe@4,
3,
<<<<"case "/utf8, Parse_expr/binary>>/binary, " {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@5,
4,
<<<<"Ok("/utf8, Raw_var/binary>>/binary,
"_parsed_list) -> {"/utf8>>
);
{some,
{inline,
{array_schema,
_,
{inline, {number_schema, _, _, _, _, _, _, _}},
_,
_,
_}}} ->
Parse_expr@1 = array_float_parse_expr(Raw_var, P, Delim),
_pipe@6 = Sb,
_pipe@7 = oaspec@util@string_extra:indent(
_pipe@6,
3,
<<<<"case "/utf8, Parse_expr@1/binary>>/binary, " {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@7,
4,
<<<<"Ok("/utf8, Raw_var/binary>>/binary,
"_parsed_list) -> {"/utf8>>
);
{some, Ref} ->
case oaspec@codegen@server_request_decode:schema_ref_string_enum(
Ref,
Ctx
) of
{some, {Type_name, Values}} ->
_pipe@8 = Sb,
_pipe@9 = oaspec@util@string_extra:indent(
_pipe@8,
3,
<<<<"case "/utf8,
(oaspec@codegen@server_request_decode:enum_match_result_expr(
Raw_var,
Type_name,
Values
))/binary>>/binary,
" {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@9,
4,
<<<<"Ok("/utf8, Raw_var/binary>>/binary,
"_parsed) -> {"/utf8>>
);
none ->
Sb
end;
_ ->
Sb
end.
-file("src/oaspec/codegen/server.gleam", 1556).
?DOC(
" For headers / cookies, open a numeric parse case if the param schema is a\n"
" scalar Integer/Number. (Header/cookie array parsing is currently a single\n"
" inline `list.map(string.split(...))` expression and stays unsafe; that's\n"
" out of scope for the Issue #263 fix and tracked separately.)\n"
).
-spec single_value_required_open_parse_case(
gleam@string_tree:string_tree(),
oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved())
) -> gleam@string_tree:string_tree().
single_value_required_open_parse_case(Sb, P) ->
Raw_var = <<(oaspec@util@naming:to_snake_case(erlang:element(2, P)))/binary,
"_raw"/utf8>>,
case oaspec@openapi@spec:parameter_schema(P) of
{some, {inline, {integer_schema, _, _, _, _, _, _, _}}} ->
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:indent(
_pipe,
3,
<<<<"case int.parse("/utf8, Raw_var/binary>>/binary,
") {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@1,
4,
<<<<"Ok("/utf8, Raw_var/binary>>/binary, "_parsed) -> {"/utf8>>
);
{some, {inline, {number_schema, _, _, _, _, _, _, _}}} ->
_pipe@2 = Sb,
_pipe@3 = oaspec@util@string_extra:indent(
_pipe@2,
3,
<<<<"case float.parse("/utf8, Raw_var/binary>>/binary,
") {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@3,
4,
<<<<"Ok("/utf8, Raw_var/binary>>/binary, "_parsed) -> {"/utf8>>
);
{some,
{inline,
{array_schema,
_,
{inline, {integer_schema, _, _, _, _, _, _, _}},
_,
_,
_}}} ->
_pipe@4 = Sb,
_pipe@5 = oaspec@util@string_extra:indent(
_pipe@4,
3,
<<<<"case list.try_map(list.map(string.split("/utf8,
Raw_var/binary>>/binary,
", \",\"), string.trim), int.parse) {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@5,
4,
<<<<"Ok("/utf8, Raw_var/binary>>/binary,
"_parsed_list) -> {"/utf8>>
);
{some,
{inline,
{array_schema,
_,
{inline, {number_schema, _, _, _, _, _, _, _}},
_,
_,
_}}} ->
_pipe@6 = Sb,
_pipe@7 = oaspec@util@string_extra:indent(
_pipe@6,
3,
<<<<"case list.try_map(list.map(string.split("/utf8,
Raw_var/binary>>/binary,
", \",\"), string.trim), float.parse) {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@7,
4,
<<<<"Ok("/utf8, Raw_var/binary>>/binary,
"_parsed_list) -> {"/utf8>>
);
_ ->
Sb
end.
-file("src/oaspec/codegen/server.gleam", 1647).
?DOC(
" Build the Gleam source expression for a `ServerResponse` carrying an\n"
" RFC 7807-shaped `application/problem+json` body. Used by every\n"
" router-side error-emit site so clients receive a structured JSON\n"
" response instead of plain `Bad Request` / `Not Found` text — a\n"
" schema-conformant default for specs that declare 4xx responses with\n"
" `application/problem+json` content (issue #307).\n"
"\n"
" The body is emitted as a literal JSON string at codegen time —\n"
" stable, allocation-free, and safe to embed because `detail` is a\n"
" short codegen-controlled phrase (no user input is interpolated).\n"
" Specs that need a different Problem encoding can still override at\n"
" the framework adapter layer.\n"
).
-spec problem_response_expr(integer(), binary()) -> binary().
problem_response_expr(Status, Detail) ->
Body = <<<<"\"{\\\"type\\\":\\\"about:blank\\\",\\\"title\\\":\\\""/utf8,
Detail/binary>>/binary,
"\\\"}\""/utf8>>,
<<<<<<<<"ServerResponse(status: "/utf8,
(erlang:integer_to_binary(Status))/binary>>/binary,
", body: TextBody("/utf8>>/binary,
Body/binary>>/binary,
"), headers: [#(\"content-type\", \"application/problem+json\")])"/utf8>>.
-file("src/oaspec/codegen/server.gleam", 1515).
?DOC(" Close the matching parse case for a required query param.\n").
-spec close_query_required_parse_case(
gleam@string_tree:string_tree(),
oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
close_query_required_parse_case(Sb, P, Ctx) ->
Close = fun(Sb@1) -> _pipe = Sb@1,
_pipe@1 = oaspec@util@string_extra:indent(_pipe, 4, <<"}"/utf8>>),
_pipe@2 = oaspec@util@string_extra:indent(
_pipe@1,
4,
<<"_ -> "/utf8,
(problem_response_expr(400, <<"invalid query parameter"/utf8>>))/binary>>
),
oaspec@util@string_extra:indent(_pipe@2, 3, <<"}"/utf8>>) end,
case oaspec@openapi@spec:parameter_schema(P) of
{some, {inline, {integer_schema, _, _, _, _, _, _, _}}} ->
Close(Sb);
{some, {inline, {number_schema, _, _, _, _, _, _, _}}} ->
Close(Sb);
{some,
{inline,
{array_schema,
_,
{inline, {integer_schema, _, _, _, _, _, _, _}},
_,
_,
_}}} ->
Close(Sb);
{some,
{inline,
{array_schema,
_,
{inline, {number_schema, _, _, _, _, _, _, _}},
_,
_,
_}}} ->
Close(Sb);
{some, Ref} ->
case oaspec@codegen@server_request_decode:schema_ref_string_enum(
Ref,
Ctx
) of
{some, _} ->
Close(Sb);
none ->
Sb
end;
_ ->
Sb
end.
-file("src/oaspec/codegen/server.gleam", 1598).
-spec close_single_value_required_parse_case(
gleam@string_tree:string_tree(),
oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
close_single_value_required_parse_case(Sb, P, Ctx) ->
Close = fun(Sb@1) -> _pipe = Sb@1,
_pipe@1 = oaspec@util@string_extra:indent(_pipe, 4, <<"}"/utf8>>),
_pipe@2 = oaspec@util@string_extra:indent(
_pipe@1,
4,
<<"_ -> "/utf8,
(problem_response_expr(400, <<"invalid header or cookie"/utf8>>))/binary>>
),
oaspec@util@string_extra:indent(_pipe@2, 3, <<"}"/utf8>>) end,
case oaspec@openapi@spec:parameter_schema(P) of
{some, {inline, {integer_schema, _, _, _, _, _, _, _}}} ->
Close(Sb);
{some, {inline, {number_schema, _, _, _, _, _, _, _}}} ->
Close(Sb);
{some,
{inline,
{array_schema,
_,
{inline, {integer_schema, _, _, _, _, _, _, _}},
_,
_,
_}}} ->
Close(Sb);
{some,
{inline,
{array_schema,
_,
{inline, {number_schema, _, _, _, _, _, _, _}},
_,
_,
_}}} ->
Close(Sb);
{some, Ref} ->
case oaspec@codegen@server_request_decode:schema_ref_string_enum(
Ref,
Ctx
) of
{some, _} ->
Close(Sb);
none ->
Sb
end;
_ ->
Sb
end.
-file("src/oaspec/codegen/server.gleam", 1662).
?DOC(
" Close a required-param lookup case (`case dict.get(...) { Ok(...) -> { ... }`).\n"
" The catch-all `_ ->` arm covers both `Error(_)` and `Ok([])` (empty list)\n"
" for query params, plus the bare `Error(_)` for header/cookie lookups.\n"
).
-spec close_lookup_case(gleam@string_tree:string_tree()) -> gleam@string_tree:string_tree().
close_lookup_case(Sb) ->
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:indent(_pipe, 4, <<"}"/utf8>>),
_pipe@2 = oaspec@util@string_extra:indent(
_pipe@1,
4,
<<"_ -> "/utf8,
(problem_response_expr(400, <<"missing or invalid parameter"/utf8>>))/binary>>
),
oaspec@util@string_extra:indent(_pipe@2, 3, <<"}"/utf8>>).
-file("src/oaspec/codegen/server.gleam", 1680).
?DOC(
" Check if an operation's request body needs guard validation.\n"
" True when the body is required, JSON-compatible, references a named schema,\n"
" and that schema has constraint-based validators.\n"
"\n"
" Issue #292: inline request body schemas with constraints are NOT covered\n"
" here because guards.gleam only generates validators for named component\n"
" schemas. Extending guard generation to anonymous inline schemas is\n"
" tracked as a follow-up.\n"
).
-spec operation_needs_guard_validation(
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
oaspec@codegen@context:context()
) -> boolean().
operation_needs_guard_validation(Operation, Ctx) ->
case erlang:element(7, Operation) of
{some, {value, Rb}} ->
erlang:element(4, Rb) andalso begin
Content_entries = maps:to_list(erlang:element(3, Rb)),
gleam@list:any(
Content_entries,
fun(Entry) ->
oaspec@util@content_type:is_json_compatible(
erlang:element(1, Entry)
)
end
)
andalso case Content_entries of
[{_, Mt}] ->
case erlang:element(2, Mt) of
{some, {reference, _, Name}} ->
oaspec@codegen@guards:schema_has_validator(
Name,
Ctx
);
_ ->
false
end;
_ ->
false
end
end;
_ ->
false
end.
-file("src/oaspec/codegen/server.gleam", 1897).
?DOC(" True if any response of any operation declares at least one header.\n").
-spec operations_have_response_headers(
list({binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
oaspec@openapi@spec:http_method()})
) -> boolean().
operations_have_response_headers(Operations) ->
gleam@list:any(
Operations,
fun(Op) ->
{_, Operation, _, _} = Op,
gleam@list:any(
maps:to_list(erlang:element(8, Operation)),
fun(Entry) ->
{_, Ref_or} = Entry,
case Ref_or of
{value, Response} ->
not gleam@dict:is_empty(erlang:element(4, Response));
_ ->
false
end
end
)
end
).
-file("src/oaspec/codegen/server.gleam", 1991).
?DOC(
" Mirror of `header_schema_to_type` in ir_build — kept here so the\n"
" router emission and the response_types record stay in sync without a\n"
" cross-module dependency on the IR layer.\n"
).
-spec response_header_field_type(
gleam@option:option(oaspec@openapi@schema:schema_ref())
) -> binary().
response_header_field_type(Schema_opt) ->
case Schema_opt of
{some, {inline, {integer_schema, _, _, _, _, _, _, _}}} ->
<<"Int"/utf8>>;
{some, {inline, {number_schema, _, _, _, _, _, _, _}}} ->
<<"Float"/utf8>>;
{some, {inline, {boolean_schema, _}}} ->
<<"Bool"/utf8>>;
{some, {inline, {string_schema, _, _, _, _, _, _}}} ->
<<"String"/utf8>>;
{some, {reference, _, Name}} ->
<<"types."/utf8,
(oaspec@util@naming:schema_to_type_name(Name))/binary>>;
_ ->
<<"String"/utf8>>
end.
-file("src/oaspec/codegen/server.gleam", 1969).
-spec sorted_header_specs(
gleam@dict:dict(binary(), oaspec@openapi@spec:header())
) -> list(header_spec()).
sorted_header_specs(Headers) ->
_pipe = Headers,
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun(A, B) ->
gleam@string:compare(erlang:element(1, A), erlang:element(1, B))
end
),
gleam@list:map(
_pipe@2,
fun(Entry) ->
{Header_name, Header} = Entry,
Field_name = oaspec@util@naming:to_snake_case(Header_name),
Field_type = response_header_field_type(erlang:element(4, Header)),
{header_spec,
Header_name,
Field_name,
Field_type,
erlang:element(3, Header)}
end
).
-file("src/oaspec/codegen/server.gleam", 1915).
?DOC(
" True if any response header field has the given Gleam type.\n"
" Used to decide which primitive `gleam/<type>` import the generated\n"
" router needs for header value stringification (issue #306).\n"
).
-spec operations_have_response_header_of_type(
list({binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
oaspec@openapi@spec:http_method()}),
binary()
) -> boolean().
operations_have_response_header_of_type(Operations, Type_name) ->
gleam@list:any(
Operations,
fun(Op) ->
{_, Operation, _, _} = Op,
gleam@list:any(
maps:to_list(erlang:element(8, Operation)),
fun(Entry) ->
{_, Ref_or} = Entry,
case Ref_or of
{value, Response} ->
gleam@list:any(
sorted_header_specs(erlang:element(4, Response)),
fun(Spec) ->
erlang:element(4, Spec) =:= Type_name
end
);
_ ->
false
end
end
)
end
).
-file("src/oaspec/codegen/server.gleam", 1936).
?DOC(
" True if any response header is optional. Optional headers emit\n"
" `case hdrs.<field> { Some(v) -> ... None -> [] }` and need `gleam/option`.\n"
).
-spec operations_have_optional_response_header(
list({binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
oaspec@openapi@spec:http_method()})
) -> boolean().
operations_have_optional_response_header(Operations) ->
gleam@list:any(
Operations,
fun(Op) ->
{_, Operation, _, _} = Op,
gleam@list:any(
maps:to_list(erlang:element(8, Operation)),
fun(Entry) ->
{_, Ref_or} = Entry,
case Ref_or of
{value, Response} ->
gleam@list:any(
sorted_header_specs(erlang:element(4, Response)),
fun(Spec) -> not erlang:element(5, Spec) end
);
_ ->
false
end
end
)
end
).
-file("src/oaspec/codegen/server.gleam", 2075).
-spec append_chunks(binary(), list(binary())) -> binary().
append_chunks(Prefix, Header_chunks) ->
case Header_chunks of
[] ->
Prefix;
_ ->
case Prefix of
<<""/utf8>> ->
gleam@string:join(Header_chunks, <<", "/utf8>>);
_ ->
<<<<Prefix/binary, ", "/utf8>>/binary,
(gleam@string:join(Header_chunks, <<", "/utf8>>))/binary>>
end
end.
-file("src/oaspec/codegen/server.gleam", 2118).
?DOC(
" Convert a typed header value into a `String` for the wire. Header\n"
" fields are limited to primitives (Int / Float / Bool / String) and\n"
" `types.<X>` aliases (which the codegen treats as String-compatible\n"
" — non-string aliases would fail to compile and signal the user\n"
" that header value coercion is unsupported for that schema).\n"
).
-spec stringify_header_value(binary(), binary()) -> binary().
stringify_header_value(Field_type, Value_expr) ->
case Field_type of
<<"Int"/utf8>> ->
<<<<"int.to_string("/utf8, Value_expr/binary>>/binary, ")"/utf8>>;
<<"Float"/utf8>> ->
<<<<"float.to_string("/utf8, Value_expr/binary>>/binary, ")"/utf8>>;
<<"Bool"/utf8>> ->
<<<<"bool.to_string("/utf8, Value_expr/binary>>/binary, ")"/utf8>>;
_ ->
Value_expr
end.
-file("src/oaspec/codegen/server.gleam", 2094).
?DOC(
" Render one declared response header into a chunk that contributes\n"
" to the `headers:` `list.flatten` call.\n"
"\n"
" Required: `[#(\"Pagination-Cursor\", hdrs.pagination_cursor)]`\n"
" Optional: `case hdrs.pagination_cursor { Some(v) -> [#(...)] None -> [] }`\n"
" Non-string types are stringified via `int.to_string` /\n"
" `float.to_string` / `bool.to_string` (the necessary imports are\n"
" added by the import-analysis pass).\n"
).
-spec header_chunk_expr(header_spec()) -> binary().
header_chunk_expr(Spec) ->
Render_value = fun(Value_expr) ->
<<<<<<<<"[#(\""/utf8, (erlang:element(2, Spec))/binary>>/binary,
"\", "/utf8>>/binary,
(stringify_header_value(erlang:element(4, Spec), Value_expr))/binary>>/binary,
")]"/utf8>>
end,
case erlang:element(5, Spec) of
true ->
Render_value(<<"hdrs."/utf8, (erlang:element(3, Spec))/binary>>);
false ->
<<<<<<<<"case hdrs."/utf8, (erlang:element(3, Spec))/binary>>/binary,
" { Some(v) -> "/utf8>>/binary,
(Render_value(<<"v"/utf8>>))/binary>>/binary,
" None -> [] }"/utf8>>
end.
-file("src/oaspec/codegen/server.gleam", 2052).
?DOC(
" Build the `headers:` argument for a `ServerResponse(...)` call.\n"
"\n"
" - No content-type, no declared headers → `[]`\n"
" - Content-type only → `[#(\"content-type\", \"<type>\")]`\n"
" - Declared headers (with or without content-type) → `list.flatten([...])`\n"
" so optional headers can contribute `[]` and required ones a\n"
" one-tuple list without repeated allocation. The fold keeps the\n"
" spec-declared order (alphabetised by `sorted_header_specs`) so\n"
" regenerated routers stay deterministic.\n"
).
-spec headers_slot_expr(gleam@option:option(binary()), list(header_spec())) -> binary().
headers_slot_expr(Content_type_header, Header_specs) ->
Content_type_chunk = case Content_type_header of
{some, Media_type_name} ->
[<<<<"[#(\"content-type\", \""/utf8, Media_type_name/binary>>/binary,
"\")]"/utf8>>];
none ->
[]
end,
Header_chunks = gleam@list:map(
Header_specs,
fun(Spec) -> header_chunk_expr(Spec) end
),
case {Content_type_chunk, Header_chunks} of
{[], []} ->
<<"[]"/utf8>>;
{[Single_chunk], []} ->
Single_chunk;
{_, _} ->
<<<<"list.flatten(["/utf8,
(begin
_pipe = gleam@string:join(
Content_type_chunk,
<<", "/utf8>>
),
append_chunks(_pipe, Header_chunks)
end)/binary>>/binary,
"])"/utf8>>
end.
-file("src/oaspec/codegen/server.gleam", 2010).
?DOC(
" Emit one arm of the response dispatch `case` block.\n"
"\n"
" Issue #306 introduces the headers plumbing: when `has_headers` is\n"
" True the variant pattern grows an `hdrs` binding and the headers\n"
" list is materialised via `list.flatten` so spec-declared response\n"
" headers are appended to the implicit `content-type` tuple.\n"
).
-spec emit_response_arm(
gleam@string_tree:string_tree(),
binary(),
binary(),
boolean(),
boolean(),
binary(),
gleam@option:option(binary()),
list(header_spec())
) -> gleam@string_tree:string_tree().
emit_response_arm(
Sb,
Variant_name,
Status_int,
Has_data,
Has_headers,
Body_expr,
Content_type_header,
Header_specs
) ->
Pattern_args = case {Has_data, Has_headers} of
{true, true} ->
<<"(data, hdrs)"/utf8>>;
{true, false} ->
<<"(data)"/utf8>>;
{false, true} ->
<<"(hdrs)"/utf8>>;
{false, false} ->
<<""/utf8>>
end,
Headers_expr = headers_slot_expr(Content_type_header, Header_specs),
_pipe = Sb,
oaspec@util@string_extra:indent(
_pipe,
4,
<<<<<<<<<<<<<<<<<<"response_types."/utf8, Variant_name/binary>>/binary,
Pattern_args/binary>>/binary,
" -> ServerResponse(status: "/utf8>>/binary,
Status_int/binary>>/binary,
", body: "/utf8>>/binary,
Body_expr/binary>>/binary,
", headers: "/utf8>>/binary,
Headers_expr/binary>>/binary,
")"/utf8>>
).
-file("src/oaspec/codegen/server.gleam", 2128).
?DOC(" Get the encode function name for a schema reference.\n").
-spec get_encode_function(
gleam@option:option(oaspec@openapi@schema:schema_ref()),
oaspec@codegen@context:context()
) -> binary().
get_encode_function(Schema_ref, _) ->
case Schema_ref of
{some, {reference, _, Name}} ->
<<<<"encode.encode_"/utf8,
(oaspec@util@naming:to_snake_case(Name))/binary>>/binary,
"_json"/utf8>>;
{some, {inline, {array_schema, _, Items, _, _, _}}} ->
case Items of
{reference, _, Name@1} ->
<<<<"fn(items) { json.array(items, encode.encode_"/utf8,
(oaspec@util@naming:to_snake_case(Name@1))/binary>>/binary,
"_json) }"/utf8>>;
{inline, {string_schema, _, _, _, _, _, _}} ->
<<"fn(items) { json.array(items, json.string) }"/utf8>>;
{inline, {integer_schema, _, _, _, _, _, _, _}} ->
<<"fn(items) { json.array(items, json.int) }"/utf8>>;
{inline, {number_schema, _, _, _, _, _, _, _}} ->
<<"fn(items) { json.array(items, json.float) }"/utf8>>;
{inline, {boolean_schema, _}} ->
<<"fn(items) { json.array(items, json.bool) }"/utf8>>;
_ ->
<<"fn(items) { json.array(items, json.string) }"/utf8>>
end;
{some, {inline, {string_schema, _, _, _, _, _, _}}} ->
<<"json.string"/utf8>>;
{some, {inline, {integer_schema, _, _, _, _, _, _, _}}} ->
<<"json.int"/utf8>>;
{some, {inline, {number_schema, _, _, _, _, _, _, _}}} ->
<<"json.float"/utf8>>;
{some, {inline, {boolean_schema, _}}} ->
<<"json.bool"/utf8>>;
_ ->
<<"json.string"/utf8>>
end.
-file("src/oaspec/codegen/server.gleam", 1710).
?DOC(" Generate code to convert a handler response to ServerResponse.\n").
-spec generate_response_conversion(
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_conversion(Sb, Response_type_name, Operation, Ctx) ->
Responses = oaspec@util@http:sort_response_entries(
maps:to_list(erlang:element(8, Operation))
),
case gleam@list:is_empty(Responses) of
true ->
_pipe = Sb,
oaspec@util@string_extra:indent(
_pipe,
3,
<<"ServerResponse(status: 200, body: EmptyBody, headers: [])"/utf8>>
);
false ->
Sb@1 = begin
_pipe@1 = Sb,
oaspec@util@string_extra:indent(
_pipe@1,
3,
<<"case response {"/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 = <<Response_type_name/binary,
(oaspec@util@http:status_code_suffix(
Status_code
))/binary>>,
Status_int = oaspec@util@http:status_code_to_int(
Status_code
),
Content_entries = maps:to_list(
erlang:element(3, Response)
),
Header_specs = sorted_header_specs(
erlang:element(4, Response)
),
Has_headers = not gleam@list:is_empty(Header_specs),
case Content_entries of
[] ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
false,
Has_headers,
<<"EmptyBody"/utf8>>,
none,
Header_specs
);
[{Media_type_name, Media_type}] ->
case oaspec@util@content_type:from_string(
Media_type_name
) of
application_json ->
case erlang:element(2, Media_type) of
{some, _} ->
Encode_fn = get_encode_function(
erlang:element(
2,
Media_type
),
Ctx
),
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
true,
Has_headers,
<<<<"TextBody(json.to_string("/utf8,
Encode_fn/binary>>/binary,
"(data)))"/utf8>>,
{some, Media_type_name},
Header_specs
);
none ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
false,
Has_headers,
<<"EmptyBody"/utf8>>,
none,
Header_specs
)
end;
text_plain ->
case erlang:element(2, Media_type) of
{some, _} ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
true,
Has_headers,
<<"TextBody(data)"/utf8>>,
{some, Media_type_name},
Header_specs
);
none ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
false,
Has_headers,
<<"EmptyBody"/utf8>>,
none,
Header_specs
)
end;
application_xml ->
case erlang:element(2, Media_type) of
{some, _} ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
true,
Has_headers,
<<"TextBody(data)"/utf8>>,
{some, Media_type_name},
Header_specs
);
none ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
false,
Has_headers,
<<"EmptyBody"/utf8>>,
none,
Header_specs
)
end;
text_xml ->
case erlang:element(2, Media_type) of
{some, _} ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
true,
Has_headers,
<<"TextBody(data)"/utf8>>,
{some, Media_type_name},
Header_specs
);
none ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
false,
Has_headers,
<<"EmptyBody"/utf8>>,
none,
Header_specs
)
end;
application_octet_stream ->
case erlang:element(2, Media_type) of
{some, _} ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
true,
Has_headers,
<<"BytesBody(data)"/utf8>>,
{some, Media_type_name},
Header_specs
);
none ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
false,
Has_headers,
<<"EmptyBody"/utf8>>,
none,
Header_specs
)
end;
_ ->
case erlang:element(2, Media_type) of
{some, _} ->
Encode_fn@1 = get_encode_function(
erlang:element(
2,
Media_type
),
Ctx
),
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
true,
Has_headers,
<<<<"TextBody(json.to_string("/utf8,
Encode_fn@1/binary>>/binary,
"(data)))"/utf8>>,
{some, Media_type_name},
Header_specs
);
none ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
false,
Has_headers,
<<"EmptyBody"/utf8>>,
none,
Header_specs
)
end
end;
[{First_media_type, _}, _ | _] ->
emit_response_arm(
Sb@2,
Variant_name,
Status_int,
true,
Has_headers,
<<"TextBody(data)"/utf8>>,
{some, First_media_type},
Header_specs
)
end;
_ ->
Sb@2
end
end
),
_pipe@2 = Sb@3,
oaspec@util@string_extra:indent(_pipe@2, 3, <<"}"/utf8>>)
end.
-file("src/oaspec/codegen/server.gleam", 1053).
?DOC(
" Generate safe request construction wrapped in error handling.\n"
" Path parameter parsing, required query/header/cookie params, and body\n"
" decoding are all validated before calling the handler. Parse failures\n"
" return ServerResponse(status: 400) instead of crashing.\n"
).
-spec generate_safe_request_and_dispatch(
gleam@string_tree:string_tree(),
binary(),
binary(),
binary(),
binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_safe_request_and_dispatch(
Sb,
Request_type_name,
Response_type_name,
Op_id,
Fn_name,
Operation,
_,
Ctx
) ->
Params = gleam@list:filter_map(
erlang:element(6, Operation),
fun(R) -> case R of
{value, P} ->
{ok, P};
_ ->
{error, nil}
end end
),
Deduped_field_names = oaspec@openapi@dedup:dedup_param_field_names(Params),
Params_with_field_names = gleam@list:zip(Params, Deduped_field_names),
Path_params_needing_parse = gleam@list:filter(
Params,
fun(P@1) ->
(erlang:element(3, P@1) =:= in_path) andalso oaspec@codegen@server_request_decode:param_needs_result_unwrap(
P@1
)
end
),
Required_query_params = gleam@list:filter(
Params,
fun(P@2) ->
((erlang:element(3, P@2) =:= in_query) andalso erlang:element(
5,
P@2
))
andalso not oaspec@codegen@server_request_decode:is_deep_object_param(
P@2,
Ctx
)
end
),
Required_header_params = gleam@list:filter(
Params,
fun(P@3) ->
(erlang:element(3, P@3) =:= in_header) andalso erlang:element(
5,
P@3
)
end
),
Required_cookie_params = gleam@list:filter(
Params,
fun(P@4) ->
(erlang:element(3, P@4) =:= in_cookie) andalso erlang:element(
5,
P@4
)
end
),
Needs_body_guard = case erlang:element(7, Operation) of
{some, {value, Rb}} ->
erlang:element(4, Rb) andalso gleam@list:any(
maps:to_list(erlang:element(3, Rb)),
fun(Entry) ->
oaspec@util@content_type:is_json_compatible(
erlang:element(1, Entry)
)
end
);
_ ->
false
end,
Sb@2 = gleam@list:fold(
Path_params_needing_parse,
Sb,
fun(Sb@1, P@5) ->
Var_name = oaspec@util@naming:to_snake_case(erlang:element(2, P@5)),
Parse_expr = oaspec@codegen@server_request_decode:param_parse_expr(
Var_name,
P@5
),
_pipe = Sb@1,
_pipe@1 = oaspec@util@string_extra:indent(
_pipe,
3,
<<<<"case "/utf8, Parse_expr/binary>>/binary, " {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@1,
4,
<<<<"Ok("/utf8, Var_name/binary>>/binary, "_parsed) -> {"/utf8>>
)
end
),
Sb@4 = gleam@list:fold(
Required_query_params,
Sb@2,
fun(Sb@3, P@6) ->
Raw_var = <<(oaspec@util@naming:to_snake_case(
erlang:element(2, P@6)
))/binary,
"_raw"/utf8>>,
Pattern = case query_param_explode_array(P@6) of
true ->
<<<<"Ok([_, ..] as "/utf8, Raw_var/binary>>/binary,
")"/utf8>>;
false ->
<<<<"Ok(["/utf8, Raw_var/binary>>/binary, ", ..])"/utf8>>
end,
_pipe@2 = Sb@3,
_pipe@3 = oaspec@util@string_extra:indent(
_pipe@2,
3,
<<<<"case dict.get(query, \""/utf8,
(erlang:element(2, P@6))/binary>>/binary,
"\") {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@3,
4,
<<Pattern/binary, " -> {"/utf8>>
)
end
),
Sb@6 = gleam@list:fold(
Required_query_params,
Sb@4,
fun(Sb@5, P@7) -> query_required_open_parse_case(Sb@5, P@7, Ctx) end
),
Sb@8 = gleam@list:fold(
Required_header_params,
Sb@6,
fun(Sb@7, P@8) ->
Raw_var@1 = <<(oaspec@util@naming:to_snake_case(
erlang:element(2, P@8)
))/binary,
"_raw"/utf8>>,
Key = string:lowercase(erlang:element(2, P@8)),
_pipe@4 = Sb@7,
_pipe@5 = oaspec@util@string_extra:indent(
_pipe@4,
3,
<<<<"case dict.get(headers, \""/utf8, Key/binary>>/binary,
"\") {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@5,
4,
<<<<"Ok("/utf8, Raw_var@1/binary>>/binary, ") -> {"/utf8>>
)
end
),
Sb@10 = gleam@list:fold(
Required_header_params,
Sb@8,
fun(Sb@9, P@9) -> single_value_required_open_parse_case(Sb@9, P@9) end
),
Sb@12 = gleam@list:fold(
Required_cookie_params,
Sb@10,
fun(Sb@11, P@10) ->
Raw_var@2 = <<(oaspec@util@naming:to_snake_case(
erlang:element(2, P@10)
))/binary,
"_raw"/utf8>>,
_pipe@6 = Sb@11,
_pipe@7 = oaspec@util@string_extra:indent(
_pipe@6,
3,
<<<<"case cookie_lookup(headers, \""/utf8,
(erlang:element(2, P@10))/binary>>/binary,
"\") {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@7,
4,
<<<<"Ok("/utf8, Raw_var@2/binary>>/binary, ") -> {"/utf8>>
)
end
),
Sb@14 = gleam@list:fold(
Required_cookie_params,
Sb@12,
fun(Sb@13, P@11) ->
single_value_required_open_parse_case(Sb@13, P@11)
end
),
Body_schema_ref_name = case erlang:element(7, Operation) of
{some, {value, Rb@1}} ->
Content_entries = maps:to_list(erlang:element(3, Rb@1)),
case Content_entries of
[{_, Media_type}] ->
case erlang:element(2, Media_type) of
{some, {reference, _, Name}} ->
{some, Name};
_ ->
none
end;
_ ->
none
end;
_ ->
none
end,
Needs_guard_validation = (oaspec@config:validate(
oaspec@codegen@context:config(Ctx)
)
andalso Needs_body_guard)
andalso (case Body_schema_ref_name of
{some, Name@1} ->
oaspec@codegen@guards:schema_has_validator(Name@1, Ctx);
none ->
false
end),
Sb@15 = case {Needs_body_guard, erlang:element(7, Operation)} of
{true, {some, {value, Rb@2}}} ->
Content_entries@1 = maps:to_list(erlang:element(3, Rb@2)),
case Content_entries@1 of
[{_, Media_type@1}] ->
case erlang:element(2, Media_type@1) of
{some, {reference, _, Name@2}} ->
Decode_fn = <<<<"decode.decode_"/utf8,
(oaspec@util@naming:to_snake_case(Name@2))/binary>>/binary,
"(body)"/utf8>>,
_pipe@8 = Sb@14,
_pipe@9 = oaspec@util@string_extra:indent(
_pipe@8,
3,
<<<<"case "/utf8, Decode_fn/binary>>/binary,
" {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@9,
4,
<<"Ok(decoded_body) -> {"/utf8>>
);
_ ->
Decode_fn@1 = <<<<"decode.decode_"/utf8,
(oaspec@util@naming:to_snake_case(Op_id))/binary>>/binary,
"_request_body(body)"/utf8>>,
_pipe@10 = Sb@14,
_pipe@11 = oaspec@util@string_extra:indent(
_pipe@10,
3,
<<<<"case "/utf8, Decode_fn@1/binary>>/binary,
" {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@11,
4,
<<"Ok(decoded_body) -> {"/utf8>>
)
end;
_ ->
Sb@14
end;
{_, _} ->
Sb@14
end,
Sb@16 = case {Needs_guard_validation, Body_schema_ref_name} of
{true, {some, Name@3}} ->
Validate_fn = <<<<"guards.validate_"/utf8,
(oaspec@util@naming:to_snake_case(Name@3))/binary>>/binary,
"(decoded_body)"/utf8>>,
_pipe@12 = Sb@15,
_pipe@13 = oaspec@util@string_extra:indent(
_pipe@12,
3,
<<<<"case "/utf8, Validate_fn/binary>>/binary, " {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@13,
4,
<<"Ok(decoded_body) -> {"/utf8>>
);
{_, _} ->
Sb@15
end,
Sb@17 = case erlang:element(7, Operation) of
{some, {value, Rb@3}} ->
case oaspec@codegen@server_request_decode:request_body_uses_form_urlencoded(
Rb@3
) of
true ->
_pipe@14 = Sb@16,
oaspec@util@string_extra:indent(
_pipe@14,
3,
<<"let form_body = parse_form_body(body)"/utf8>>
);
false ->
case oaspec@codegen@server_request_decode:request_body_uses_multipart(
Rb@3
) of
true ->
_pipe@15 = Sb@16,
oaspec@util@string_extra:indent(
_pipe@15,
3,
<<"let multipart_body = parse_multipart_body(body, headers)"/utf8>>
);
false ->
Sb@16
end
end;
_ ->
Sb@16
end,
Sb@18 = begin
_pipe@16 = Sb@17,
oaspec@util@string_extra:indent(
_pipe@16,
3,
<<<<"let request = request_types."/utf8, Request_type_name/binary>>/binary,
"("/utf8>>
)
end,
Sb@20 = gleam@list:fold(
Params_with_field_names,
Sb@18,
fun(Sb@19, Entry@1) ->
{Param, Field_name} = Entry@1,
Trailing = <<","/utf8>>,
Value_expr = case erlang:element(3, Param) of
in_path ->
case oaspec@codegen@server_request_decode:param_needs_result_unwrap(
Param
) of
true ->
<<(oaspec@util@naming:to_snake_case(
erlang:element(2, Param)
))/binary,
"_parsed"/utf8>>;
false ->
Var_name@1 = oaspec@util@naming:to_snake_case(
erlang:element(2, Param)
),
oaspec@codegen@server_request_decode:param_parse_expr(
Var_name@1,
Param
)
end;
in_query ->
Key@1 = erlang:element(2, Param),
Raw_var@3 = <<(oaspec@util@naming:to_snake_case(
erlang:element(2, Param)
))/binary,
"_raw"/utf8>>,
case {oaspec@codegen@server_request_decode:is_deep_object_param(
Param,
Ctx
),
erlang:element(5, Param)} of
{true, true} ->
oaspec@codegen@server_request_decode:deep_object_required_expr(
Key@1,
Param,
Op_id,
Ctx
);
{true, false} ->
oaspec@codegen@server_request_decode:deep_object_optional_expr(
Key@1,
Param,
Op_id,
Ctx
);
{false, true} ->
oaspec@codegen@server_request_decode:query_required_expr(
Raw_var@3,
Param,
Ctx
);
{false, false} ->
oaspec@codegen@server_request_decode:query_optional_expr(
Key@1,
Param,
Ctx
)
end;
in_header ->
Key@2 = string:lowercase(erlang:element(2, Param)),
Raw_var@4 = <<(oaspec@util@naming:to_snake_case(
erlang:element(2, Param)
))/binary,
"_raw"/utf8>>,
case erlang:element(5, Param) of
true ->
oaspec@codegen@server_request_decode:header_required_expr(
Raw_var@4,
Param
);
false ->
oaspec@codegen@server_request_decode:header_optional_expr(
Key@2,
Param
)
end;
in_cookie ->
Key@3 = erlang:element(2, Param),
Raw_var@5 = <<(oaspec@util@naming:to_snake_case(
erlang:element(2, Param)
))/binary,
"_raw"/utf8>>,
case erlang:element(5, Param) of
true ->
oaspec@codegen@server_request_decode:cookie_required_expr(
Raw_var@5,
Param
);
false ->
oaspec@codegen@server_request_decode:cookie_optional_expr(
Key@3,
Param
)
end
end,
_pipe@17 = Sb@19,
oaspec@util@string_extra:indent(
_pipe@17,
4,
<<<<<<Field_name/binary, ": "/utf8>>/binary, Value_expr/binary>>/binary,
Trailing/binary>>
)
end
),
Sb@21 = case Needs_body_guard of
true ->
_pipe@18 = Sb@20,
oaspec@util@string_extra:indent(
_pipe@18,
4,
<<"body: decoded_body,"/utf8>>
);
false ->
case erlang:element(7, Operation) of
{some, {value, Rb@4}} ->
Body_expr = oaspec@codegen@server_request_decode:generate_body_decode_expr(
Rb@4,
Op_id,
Ctx
),
_pipe@19 = Sb@20,
oaspec@util@string_extra:indent(
_pipe@19,
4,
<<<<"body: "/utf8, Body_expr/binary>>/binary, ","/utf8>>
);
_ ->
Sb@20
end
end,
Sb@22 = begin
_pipe@20 = Sb@21,
oaspec@util@string_extra:indent(_pipe@20, 3, <<")"/utf8>>)
end,
Sb@23 = begin
_pipe@21 = Sb@22,
_pipe@22 = oaspec@util@string_extra:indent(
_pipe@21,
3,
<<<<"let response = handlers_generated."/utf8, Fn_name/binary>>/binary,
"(app_state, request)"/utf8>>
),
generate_response_conversion(
_pipe@22,
Response_type_name,
Operation,
Ctx
)
end,
Sb@24 = case Needs_guard_validation of
true ->
_pipe@23 = Sb@23,
_pipe@24 = oaspec@util@string_extra:indent(
_pipe@23,
4,
<<"}"/utf8>>
),
_pipe@25 = oaspec@util@string_extra:indent(
_pipe@24,
4,
<<"Error(errors) -> ServerResponse(status: 422, body: TextBody(json.to_string(json.array(errors, guards.validation_failure_to_json))), headers: [#(\"content-type\", \"application/json\")])"/utf8>>
),
oaspec@util@string_extra:indent(_pipe@25, 3, <<"}"/utf8>>);
false ->
Sb@23
end,
Sb@25 = case Needs_body_guard of
true ->
_pipe@26 = Sb@24,
_pipe@27 = oaspec@util@string_extra:indent(
_pipe@26,
4,
<<"}"/utf8>>
),
_pipe@28 = oaspec@util@string_extra:indent(
_pipe@27,
4,
<<"Error(_) -> "/utf8,
(problem_response_expr(400, <<"invalid request body"/utf8>>))/binary>>
),
oaspec@util@string_extra:indent(_pipe@28, 3, <<"}"/utf8>>);
false ->
Sb@24
end,
Sb@27 = gleam@list:fold(
Required_cookie_params,
Sb@25,
fun(Sb@26, P@12) ->
close_single_value_required_parse_case(Sb@26, P@12, Ctx)
end
),
Sb@29 = gleam@list:fold(
Required_cookie_params,
Sb@27,
fun(Sb@28, _) -> close_lookup_case(Sb@28) end
),
Sb@31 = gleam@list:fold(
Required_header_params,
Sb@29,
fun(Sb@30, P@13) ->
close_single_value_required_parse_case(Sb@30, P@13, Ctx)
end
),
Sb@33 = gleam@list:fold(
Required_header_params,
Sb@31,
fun(Sb@32, _) -> close_lookup_case(Sb@32) end
),
Sb@35 = gleam@list:fold(
Required_query_params,
Sb@33,
fun(Sb@34, P@14) ->
close_query_required_parse_case(Sb@34, P@14, Ctx)
end
),
Sb@37 = gleam@list:fold(
Required_query_params,
Sb@35,
fun(Sb@36, _) -> close_lookup_case(Sb@36) end
),
gleam@list:fold(
Path_params_needing_parse,
Sb@37,
fun(Sb@38, _) -> _pipe@29 = Sb@38,
_pipe@30 = oaspec@util@string_extra:indent(
_pipe@29,
4,
<<"}"/utf8>>
),
_pipe@31 = oaspec@util@string_extra:indent(
_pipe@30,
4,
<<"Error(_) -> "/utf8,
(problem_response_expr(
400,
<<"invalid path parameter"/utf8>>
))/binary>>
),
oaspec@util@string_extra:indent(_pipe@31, 3, <<"}"/utf8>>) end
).
-file("src/oaspec/codegen/server.gleam", 1010).
?DOC(" Generate the body of a single route case branch.\n").
-spec generate_route_body(
gleam@string_tree:string_tree(),
binary(),
binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
boolean(),
oaspec@codegen@context:context()
) -> gleam@string_tree:string_tree().
generate_route_body(Sb, Op_id, Fn_name, Operation, Path, Has_params, Ctx) ->
Type_name = oaspec@util@naming:schema_to_type_name(Op_id),
Response_type_name = <<Type_name/binary, "Response"/utf8>>,
case Has_params of
false ->
_pipe = Sb,
_pipe@1 = oaspec@util@string_extra:indent(
_pipe,
3,
<<<<"let response = handlers_generated."/utf8, Fn_name/binary>>/binary,
"(app_state)"/utf8>>
),
generate_response_conversion(
_pipe@1,
Response_type_name,
Operation,
Ctx
);
true ->
Request_type_name = <<Type_name/binary, "Request"/utf8>>,
generate_safe_request_and_dispatch(
Sb,
Request_type_name,
Response_type_name,
Op_id,
Fn_name,
Operation,
Path,
Ctx
)
end.
-file("src/oaspec/codegen/server.gleam", 2190).
?DOC(" Check if a path segment is a parameter.\n").
-spec is_path_param(binary()) -> boolean().
is_path_param(Segment) ->
case Segment of
<<"{"/utf8, _/binary>> ->
true;
_ ->
false
end.
-file("src/oaspec/codegen/server.gleam", 2198).
?DOC(" Extract parameter name from {name}.\n").
-spec extract_param_name(binary()) -> binary().
extract_param_name(Segment) ->
_pipe = Segment,
_pipe@1 = gleam@string:replace(_pipe, <<"{"/utf8>>, <<""/utf8>>),
gleam@string:replace(_pipe@1, <<"}"/utf8>>, <<""/utf8>>).
-file("src/oaspec/codegen/server.gleam", 2169).
?DOC(" Convert an OpenAPI path to a Gleam pattern match expression.\n").
-spec path_to_pattern(binary()) -> binary().
path_to_pattern(Path) ->
Segments = begin
_pipe = Path,
_pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>),
gleam@list:filter(_pipe@1, fun(S) -> S /= <<""/utf8>> end)
end,
Patterns = gleam@list:map(Segments, fun(Seg) -> case is_path_param(Seg) of
true ->
Param_name = extract_param_name(Seg),
oaspec@util@naming:to_snake_case(Param_name);
false ->
<<<<"\""/utf8, Seg/binary>>/binary, "\""/utf8>>
end end),
<<<<"["/utf8,
(oaspec@util@string_extra:join_with(Patterns, <<", "/utf8>>))/binary>>/binary,
"]"/utf8>>.
-file("src/oaspec/codegen/server.gleam", 238).
?DOC(" Generate a router module that dispatches requests.\n").
-spec generate_router(
oaspec@codegen@context:context(),
list({binary(),
oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()),
binary(),
oaspec@openapi@spec:http_method()})
) -> binary().
generate_router(Ctx, Operations) ->
Has_deep_object = gleam@list:any(
Operations,
fun(Op) ->
{_, Operation, _, _} = Op,
gleam@list:any(
erlang:element(6, Operation),
fun(Ref_p) -> case Ref_p of
{value, P} ->
oaspec@codegen@server_request_decode:is_deep_object_param(
P,
Ctx
);
_ ->
false
end end
)
end
),
Has_deep_object_with_ap = gleam@list:any(
Operations,
fun(Op@1) ->
{_, Operation@1, _, _} = Op@1,
gleam@list:any(
erlang:element(6, Operation@1),
fun(Ref_p@1) -> case Ref_p@1 of
{value, P@1} ->
oaspec@codegen@server_request_decode:is_deep_object_param(
P@1,
Ctx
)
andalso oaspec@codegen@server_request_decode:deep_object_has_additional_properties(
P@1,
Ctx
);
_ ->
false
end end
)
end
),
Needs_deep_object_present = gleam@list:any(
Operations,
fun(Op@2) ->
{_, Operation@2, _, _} = Op@2,
gleam@list:any(
erlang:element(6, Operation@2),
fun(Ref_p@2) -> case Ref_p@2 of
{value, P@2} ->
(oaspec@codegen@server_request_decode:is_deep_object_param(
P@2,
Ctx
)
andalso not erlang:element(5, P@2))
andalso not oaspec@codegen@server_request_decode:deep_object_has_untyped_additional_properties(
P@2,
Ctx
);
_ ->
false
end end
)
end
),
Has_deep_object_untyped_ap = gleam@list:any(
Operations,
fun(Op@3) ->
{_, Operation@3, _, _} = Op@3,
gleam@list:any(
erlang:element(6, Operation@3),
fun(Ref_p@3) -> case Ref_p@3 of
{value, P@3} ->
oaspec@codegen@server_request_decode:is_deep_object_param(
P@3,
Ctx
)
andalso oaspec@codegen@server_request_decode:deep_object_has_untyped_additional_properties(
P@3,
Ctx
);
_ ->
false
end end
)
end
),
Has_form_urlencoded_body = gleam@list:any(
Operations,
fun(Op@4) ->
{_, Operation@4, _, _} = Op@4,
oaspec@codegen@server_request_decode:operation_uses_form_urlencoded_body(
Operation@4
)
end
),
Has_multipart_body = gleam@list:any(
Operations,
fun(Op@5) ->
{_, Operation@5, _, _} = Op@5,
oaspec@codegen@server_request_decode:operation_uses_multipart_body(
Operation@5
)
end
),
Has_nested_form_urlencoded_body = gleam@list:any(
Operations,
fun(Op@6) ->
{_, Operation@6, _, _} = Op@6,
case erlang:element(7, Operation@6) of
{some, {value, Rb}} ->
oaspec@codegen@server_request_decode:form_urlencoded_body_has_nested_object(
Rb,
Ctx
);
_ ->
false
end
end
),
Needs_int = gleam@list:any(
Operations,
fun(Op@7) ->
{_, Operation@7, _, _} = Op@7,
(gleam@list:any(
erlang:element(6, Operation@7),
fun(Ref_p@4) -> case Ref_p@4 of
{value, P@4} ->
oaspec@codegen@server_request_decode:query_schema_needs_int(
oaspec@openapi@spec:parameter_schema(P@4)
)
orelse oaspec@codegen@server_request_decode:deep_object_param_needs_int(
P@4,
Ctx
);
_ ->
false
end end
)
orelse case erlang:element(7, Operation@7) of
{some, {value, Rb@1}} ->
oaspec@codegen@server_request_decode:form_urlencoded_body_needs_int(
Rb@1,
Ctx
);
_ ->
false
end)
orelse case erlang:element(7, Operation@7) of
{some, {value, Rb@2}} ->
oaspec@codegen@server_request_decode:multipart_body_needs_int(
Rb@2,
Ctx
);
_ ->
false
end
end
)
orelse operations_have_response_header_of_type(Operations, <<"Int"/utf8>>),
Needs_float = gleam@list:any(
Operations,
fun(Op@8) ->
{_, Operation@8, _, _} = Op@8,
(gleam@list:any(
erlang:element(6, Operation@8),
fun(Ref_p@5) -> case Ref_p@5 of
{value, P@5} ->
oaspec@codegen@server_request_decode:query_schema_needs_float(
oaspec@openapi@spec:parameter_schema(P@5)
)
orelse oaspec@codegen@server_request_decode:deep_object_param_needs_float(
P@5,
Ctx
);
_ ->
false
end end
)
orelse case erlang:element(7, Operation@8) of
{some, {value, Rb@3}} ->
oaspec@codegen@server_request_decode:form_urlencoded_body_needs_float(
Rb@3,
Ctx
);
_ ->
false
end)
orelse case erlang:element(7, Operation@8) of
{some, {value, Rb@4}} ->
oaspec@codegen@server_request_decode:multipart_body_needs_float(
Rb@4,
Ctx
);
_ ->
false
end
end
)
orelse operations_have_response_header_of_type(Operations, <<"Float"/utf8>>),
Needs_bool = operations_have_response_header_of_type(
Operations,
<<"Bool"/utf8>>
),
Needs_string = ((Has_form_urlencoded_body orelse Has_multipart_body) orelse Has_deep_object_with_ap)
orelse gleam@list:any(
Operations,
fun(Op@9) ->
{_, Operation@9, _, _} = Op@9,
gleam@list:any(
erlang:element(6, Operation@9),
fun(Ref_p@6) -> case Ref_p@6 of
{value, P@6} ->
case erlang:element(3, P@6) of
in_cookie ->
true;
in_query ->
oaspec@codegen@server_request_decode:query_schema_needs_string(
oaspec@openapi@spec:parameter_schema(
P@6
)
)
orelse oaspec@codegen@server_request_decode:deep_object_param_needs_string(
P@6,
Ctx
);
in_header ->
oaspec@codegen@server_request_decode:query_schema_needs_string(
oaspec@openapi@spec:parameter_schema(
P@6
)
)
orelse oaspec@codegen@server_request_decode:deep_object_param_needs_string(
P@6,
Ctx
);
in_path ->
oaspec@codegen@server_request_decode:query_schema_needs_string(
oaspec@openapi@spec:parameter_schema(
P@6
)
)
end;
_ ->
false
end end
)
orelse case erlang:element(7, Operation@9) of
{some, {value, Rb@5}} ->
oaspec@codegen@server_request_decode:form_urlencoded_body_needs_string(
Rb@5,
Ctx
);
_ ->
false
end
end
),
Needs_cookie_lookup = gleam@list:any(
Operations,
fun(Op@10) ->
{_, Operation@10, _, _} = Op@10,
gleam@list:any(
erlang:element(6, Operation@10),
fun(Ref_p@7) -> case Ref_p@7 of
{value, P@7} ->
erlang:element(3, P@7) =:= in_cookie;
_ ->
false
end end
)
end
),
Needs_list_import = ((((Needs_cookie_lookup orelse Has_deep_object) orelse Has_form_urlencoded_body)
orelse Has_multipart_body)
orelse operations_have_response_headers(Operations))
orelse gleam@list:any(
Operations,
fun(Op@11) ->
{_, Operation@11, _, _} = Op@11,
gleam@list:any(
erlang:element(6, Operation@11),
fun(Ref_p@8) -> case Ref_p@8 of
{value, P@8} ->
case {erlang:element(3, P@8),
oaspec@openapi@spec:parameter_schema(P@8)} of
{in_query,
{some,
{inline, {array_schema, _, _, _, _, _}}}} ->
true;
{in_header,
{some,
{inline, {array_schema, _, _, _, _, _}}}} ->
true;
{_, _} ->
false
end;
_ ->
false
end end
)
end
),
Needs_uri_import = Needs_cookie_lookup orelse Has_form_urlencoded_body,
Needs_option = ((oaspec@codegen@import_analysis:operations_have_optional_params(
Operations
)
orelse oaspec@codegen@import_analysis:operations_have_optional_body(
Operations
))
orelse gleam@list:any(
Operations,
fun(Op@12) ->
{_, Operation@12, _, _} = Op@12,
Has_optional_deep_object_fields = gleam@list:any(
erlang:element(6, Operation@12),
fun(Ref_p@9) -> case Ref_p@9 of
{value, P@9} ->
oaspec@codegen@server_request_decode:deep_object_param_has_optional_fields(
P@9,
Ctx
);
_ ->
false
end end
),
Has_optional_form_urlencoded_fields = case erlang:element(
7,
Operation@12
) of
{some, {value, Rb@6}} ->
oaspec@codegen@server_request_decode:form_urlencoded_body_has_optional_fields(
Rb@6,
Ctx
);
_ ->
false
end,
Has_optional_multipart_fields = case erlang:element(7, Operation@12) of
{some, {value, Rb@7}} ->
oaspec@codegen@server_request_decode:multipart_body_has_optional_fields(
Rb@7,
Ctx
);
_ ->
false
end,
(Has_optional_deep_object_fields orelse Has_optional_form_urlencoded_fields)
orelse Has_optional_multipart_fields
end
))
orelse operations_have_optional_response_header(Operations),
Needs_json = gleam@list:any(
Operations,
fun(Op@13) ->
{_, Operation@13, _, _} = Op@13,
Responses = maps:to_list(erlang:element(8, Operation@13)),
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
[{Media_type_name, Media_type}] ->
case oaspec@util@content_type:is_json_compatible(
Media_type_name
) of
true ->
case erlang:element(2, Media_type) of
{some, _} ->
true;
none ->
false
end;
false ->
false
end;
_ ->
false
end;
_ ->
false
end
end
)
end
),
Needs_decode = gleam@list:any(
Operations,
fun(Op@14) ->
{_, Operation@14, _, _} = Op@14,
case erlang:element(7, Operation@14) of
{some, {value, Rb@8}} ->
gleam@list:any(
maps:to_list(erlang:element(3, Rb@8)),
fun(Entry@1) ->
{Content_type, _} = Entry@1,
oaspec@util@content_type:is_json_compatible(
Content_type
)
end
);
_ ->
false
end
end
),
Needs_encode = Needs_json,
Uses_query = gleam@list:any(
Operations,
fun(Op@15) ->
{_, Operation@15, _, _} = Op@15,
gleam@list:any(
erlang:element(6, Operation@15),
fun(Ref_p@10) -> case Ref_p@10 of
{value, P@10} ->
erlang:element(3, P@10) =:= in_query;
_ ->
false
end end
)
end
),
Uses_headers = Has_multipart_body orelse gleam@list:any(
Operations,
fun(Op@16) ->
{_, Operation@16, _, _} = Op@16,
gleam@list:any(
erlang:element(6, Operation@16),
fun(Ref_p@11) -> case Ref_p@11 of
{value, P@11} ->
(erlang:element(3, P@11) =:= in_header) orelse (erlang:element(
3,
P@11
)
=:= in_cookie);
_ ->
false
end end
)
end
),
Uses_body = gleam@list:any(
Operations,
fun(Op@17) ->
{_, Operation@17, _, _} = Op@17,
gleam@option:is_some(erlang:element(7, Operation@17))
end
),
Has_params_ops = gleam@list:any(
Operations,
fun(Op@18) ->
{_, Operation@18, _, _} = Op@18,
not gleam@list:is_empty(erlang:element(6, Operation@18)) orelse gleam@option:is_some(
erlang:element(7, Operation@18)
)
end
),
Std_imports = [<<"gleam/dict.{type Dict}"/utf8>>],
Std_imports@1 = case Needs_list_import of
true ->
lists:append(Std_imports, [<<"gleam/list"/utf8>>]);
false ->
Std_imports
end,
Std_imports@2 = case Needs_uri_import of
true ->
lists:append(Std_imports@1, [<<"gleam/uri"/utf8>>]);
false ->
Std_imports@1
end,
Std_imports@3 = case Needs_int of
true ->
lists:append(Std_imports@2, [<<"gleam/int"/utf8>>]);
false ->
Std_imports@2
end,
Std_imports@4 = case Needs_float of
true ->
lists:append(Std_imports@3, [<<"gleam/float"/utf8>>]);
false ->
Std_imports@3
end,
Std_imports@5 = case Needs_bool of
true ->
lists:append(Std_imports@4, [<<"gleam/bool"/utf8>>]);
false ->
Std_imports@4
end,
Std_imports@6 = case Needs_option of
true ->
lists:append(Std_imports@5, [<<"gleam/option.{None, Some}"/utf8>>]);
false ->
Std_imports@5
end,
Needs_json_for_guards = oaspec@config:validate(
oaspec@codegen@context:config(Ctx)
)
andalso gleam@list:any(
Operations,
fun(Op@19) ->
{_, Operation@19, _, _} = Op@19,
operation_needs_guard_validation(Operation@19, Ctx)
end
),
Std_imports@7 = case Needs_json orelse Needs_json_for_guards of
true ->
lists:append(Std_imports@6, [<<"gleam/json"/utf8>>]);
false ->
Std_imports@6
end,
Std_imports@8 = case Needs_string of
true ->
lists:append(Std_imports@7, [<<"gleam/string"/utf8>>]);
false ->
Std_imports@7
end,
Std_imports@9 = case Has_deep_object_untyped_ap of
true ->
lists:append(Std_imports@8, [<<"gleam/dynamic"/utf8>>]);
false ->
Std_imports@8
end,
Pkg_imports = [<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/handlers"/utf8>>,
<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/handlers_generated"/utf8>>],
Pkg_imports@1 = case (Has_deep_object orelse Has_form_urlencoded_body)
orelse Has_multipart_body of
true ->
lists:append(
Pkg_imports,
[<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/types"/utf8>>]
);
false ->
Pkg_imports
end,
Pkg_imports@2 = case Needs_decode of
true ->
lists:append(
Pkg_imports@1,
[<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/decode"/utf8>>]
);
false ->
Pkg_imports@1
end,
Pkg_imports@3 = case Needs_encode of
true ->
lists:append(
Pkg_imports@2,
[<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/encode"/utf8>>]
);
false ->
Pkg_imports@2
end,
Pkg_imports@4 = case Has_params_ops of
true ->
lists:append(
Pkg_imports@3,
[<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/request_types"/utf8>>,
<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/response_types"/utf8>>]
);
false ->
lists:append(
Pkg_imports@3,
[<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/response_types"/utf8>>]
)
end,
Needs_guards = oaspec@config:validate(oaspec@codegen@context:config(Ctx))
andalso gleam@list:any(
Operations,
fun(Op@20) ->
{_, Operation@20, _, _} = Op@20,
operation_needs_guard_validation(Operation@20, Ctx)
end
),
Pkg_imports@5 = case Needs_guards of
true ->
lists:append(
Pkg_imports@4,
[<<(oaspec@config:package(oaspec@codegen@context:config(Ctx)))/binary,
"/guards"/utf8>>]
);
false ->
Pkg_imports@4
end,
All_imports = lists:append(Std_imports@9, Pkg_imports@5),
Sb = begin
_pipe = oaspec@util@string_extra:file_header(<<"0.24.0"/utf8>>),
oaspec@util@string_extra:imports(_pipe, All_imports)
end,
Sb@1 = begin
_pipe@1 = Sb,
_pipe@2 = oaspec@util@string_extra:doc_comment(
_pipe@1,
<<"Response body payload — text, raw bytes, or no body. The router
emits `BytesBody` for `application/octet-stream` (and other binary
content types) so adapters never have to round-trip bytes through a
String."/utf8>>
),
_pipe@3 = oaspec@util@string_extra:line(
_pipe@2,
<<"pub type ResponseBody {"/utf8>>
),
_pipe@4 = oaspec@util@string_extra:indent(
_pipe@3,
1,
<<"TextBody(String)"/utf8>>
),
_pipe@5 = oaspec@util@string_extra:indent(
_pipe@4,
1,
<<"BytesBody(BitArray)"/utf8>>
),
_pipe@6 = oaspec@util@string_extra:indent(
_pipe@5,
1,
<<"EmptyBody"/utf8>>
),
_pipe@7 = oaspec@util@string_extra:line(_pipe@6, <<"}"/utf8>>),
_pipe@8 = oaspec@util@string_extra:blank_line(_pipe@7),
_pipe@9 = oaspec@util@string_extra:doc_comment(
_pipe@8,
<<"A server response with status code, body, and headers."/utf8>>
),
_pipe@10 = oaspec@util@string_extra:line(
_pipe@9,
<<"pub type ServerResponse {"/utf8>>
),
_pipe@11 = oaspec@util@string_extra:indent(
_pipe@10,
1,
<<"ServerResponse(status: Int, body: ResponseBody, headers: List(#(String, String)))"/utf8>>
),
_pipe@12 = oaspec@util@string_extra:line(_pipe@11, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@12)
end,
Sb@2 = case Needs_deep_object_present of
true ->
_pipe@13 = Sb@1,
_pipe@14 = oaspec@util@string_extra:line(
_pipe@13,
<<"fn deep_object_present(query: Dict(String, List(String)), prefix: String, props: List(String)) -> Bool {"/utf8>>
),
_pipe@15 = oaspec@util@string_extra:indent(
_pipe@14,
1,
<<"list.any(props, fn(prop) { dict.has_key(query, prefix <> \"[\" <> prop <> \"]\") })"/utf8>>
),
_pipe@16 = oaspec@util@string_extra:line(_pipe@15, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@16);
false ->
Sb@1
end,
Sb@3 = case Has_deep_object_with_ap of
true ->
_pipe@17 = Sb@2,
_pipe@18 = oaspec@util@string_extra:line(
_pipe@17,
<<"fn deep_object_present_any(query: Dict(String, List(String)), prefix: String) -> Bool {"/utf8>>
),
_pipe@19 = oaspec@util@string_extra:indent(
_pipe@18,
1,
<<"let prefix_bracket = prefix <> \"[\""/utf8>>
),
_pipe@20 = oaspec@util@string_extra:indent(
_pipe@19,
1,
<<"dict.fold(query, False, fn(found, k, _v) { found || string.starts_with(k, prefix_bracket) })"/utf8>>
),
_pipe@21 = oaspec@util@string_extra:line(_pipe@20, <<"}"/utf8>>),
_pipe@22 = oaspec@util@string_extra:blank_line(_pipe@21),
_pipe@23 = oaspec@util@string_extra:line(
_pipe@22,
<<"fn deep_object_additional_properties(query: Dict(String, List(String)), prefix: String, known_props: List(String)) -> Dict(String, List(String)) {"/utf8>>
),
_pipe@24 = oaspec@util@string_extra:indent(
_pipe@23,
1,
<<"let prefix_bracket = prefix <> \"[\""/utf8>>
),
_pipe@25 = oaspec@util@string_extra:indent(
_pipe@24,
1,
<<"let prefix_len = string.length(prefix_bracket)"/utf8>>
),
_pipe@26 = oaspec@util@string_extra:indent(
_pipe@25,
1,
<<"dict.fold(query, dict.new(), fn(acc, k, v) {"/utf8>>
),
_pipe@27 = oaspec@util@string_extra:indent(
_pipe@26,
2,
<<"case string.starts_with(k, prefix_bracket) && string.ends_with(k, \"]\") {"/utf8>>
),
_pipe@28 = oaspec@util@string_extra:indent(
_pipe@27,
3,
<<"True -> {"/utf8>>
),
_pipe@29 = oaspec@util@string_extra:indent(
_pipe@28,
4,
<<"let prop = string.slice(k, prefix_len, string.length(k) - prefix_len - 1)"/utf8>>
),
_pipe@30 = oaspec@util@string_extra:indent(
_pipe@29,
4,
<<"case list.contains(known_props, prop) { True -> acc False -> dict.insert(acc, prop, v) }"/utf8>>
),
_pipe@31 = oaspec@util@string_extra:indent(
_pipe@30,
3,
<<"}"/utf8>>
),
_pipe@32 = oaspec@util@string_extra:indent(
_pipe@31,
3,
<<"False -> acc"/utf8>>
),
_pipe@33 = oaspec@util@string_extra:indent(
_pipe@32,
2,
<<"}"/utf8>>
),
_pipe@34 = oaspec@util@string_extra:indent(
_pipe@33,
1,
<<"})"/utf8>>
),
_pipe@35 = oaspec@util@string_extra:line(_pipe@34, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@35);
false ->
Sb@2
end,
Sb@4 = case Has_deep_object_untyped_ap of
true ->
_pipe@36 = Sb@3,
_pipe@37 = oaspec@util@string_extra:line(
_pipe@36,
<<"@external(erlang, \"gleam_stdlib\", \"identity\")"/utf8>>
),
_pipe@38 = oaspec@util@string_extra:line(
_pipe@37,
<<"fn coerce_dict(value: Dict(String, List(String))) -> Dict(String, dynamic.Dynamic)"/utf8>>
),
oaspec@util@string_extra:blank_line(_pipe@38);
false ->
Sb@3
end,
Sb@5 = case Has_form_urlencoded_body of
true ->
_pipe@39 = Sb@4,
_pipe@40 = oaspec@util@string_extra:line(
_pipe@39,
<<"fn form_url_decode(value: String) -> String {"/utf8>>
),
_pipe@41 = oaspec@util@string_extra:indent(
_pipe@40,
1,
<<"let value = string.replace(value, \"+\", \" \")"/utf8>>
),
_pipe@42 = oaspec@util@string_extra:indent(
_pipe@41,
1,
<<"case uri.percent_decode(value) { Ok(decoded) -> decoded Error(_) -> value }"/utf8>>
),
_pipe@43 = oaspec@util@string_extra:line(_pipe@42, <<"}"/utf8>>),
_pipe@44 = oaspec@util@string_extra:blank_line(_pipe@43),
_pipe@45 = oaspec@util@string_extra:line(
_pipe@44,
<<"fn parse_form_body(body: String) -> Dict(String, List(String)) {"/utf8>>
),
_pipe@46 = oaspec@util@string_extra:indent(
_pipe@45,
1,
<<"let parts = case body { \"\" -> [] _ -> string.split(body, \"&\") }"/utf8>>
),
_pipe@47 = oaspec@util@string_extra:indent(
_pipe@46,
1,
<<"list.fold(parts, dict.new(), fn(acc, part) {"/utf8>>
),
_pipe@48 = oaspec@util@string_extra:indent(
_pipe@47,
2,
<<"case part {"/utf8>>
),
_pipe@49 = oaspec@util@string_extra:indent(
_pipe@48,
3,
<<"\"\" -> acc"/utf8>>
),
_pipe@50 = oaspec@util@string_extra:indent(
_pipe@49,
3,
<<"_ ->"/utf8>>
),
_pipe@51 = oaspec@util@string_extra:indent(
_pipe@50,
4,
<<"case string.split_once(part, on: \"=\") {"/utf8>>
),
_pipe@52 = oaspec@util@string_extra:indent(
_pipe@51,
5,
<<"Ok(#(raw_key, raw_value)) -> {"/utf8>>
),
_pipe@53 = oaspec@util@string_extra:indent(
_pipe@52,
6,
<<"let key = form_url_decode(raw_key)"/utf8>>
),
_pipe@54 = oaspec@util@string_extra:indent(
_pipe@53,
6,
<<"let value = form_url_decode(raw_value)"/utf8>>
),
_pipe@55 = oaspec@util@string_extra:indent(
_pipe@54,
6,
<<"case dict.get(acc, key) {"/utf8>>
),
_pipe@56 = oaspec@util@string_extra:indent(
_pipe@55,
7,
<<"Ok(existing) -> dict.insert(acc, key, list.append(existing, [value]))"/utf8>>
),
_pipe@57 = oaspec@util@string_extra:indent(
_pipe@56,
7,
<<"Error(_) -> dict.insert(acc, key, [value])"/utf8>>
),
_pipe@58 = oaspec@util@string_extra:indent(
_pipe@57,
6,
<<"}"/utf8>>
),
_pipe@59 = oaspec@util@string_extra:indent(
_pipe@58,
5,
<<"}"/utf8>>
),
_pipe@60 = oaspec@util@string_extra:indent(
_pipe@59,
5,
<<"Error(_) -> {"/utf8>>
),
_pipe@61 = oaspec@util@string_extra:indent(
_pipe@60,
6,
<<"let key = form_url_decode(part)"/utf8>>
),
_pipe@62 = oaspec@util@string_extra:indent(
_pipe@61,
6,
<<"case dict.get(acc, key) {"/utf8>>
),
_pipe@63 = oaspec@util@string_extra:indent(
_pipe@62,
7,
<<"Ok(existing) -> dict.insert(acc, key, list.append(existing, [\"\"]))"/utf8>>
),
_pipe@64 = oaspec@util@string_extra:indent(
_pipe@63,
7,
<<"Error(_) -> dict.insert(acc, key, [\"\"])"/utf8>>
),
_pipe@65 = oaspec@util@string_extra:indent(
_pipe@64,
6,
<<"}"/utf8>>
),
_pipe@66 = oaspec@util@string_extra:indent(
_pipe@65,
5,
<<"}"/utf8>>
),
_pipe@67 = oaspec@util@string_extra:indent(
_pipe@66,
4,
<<"}"/utf8>>
),
_pipe@68 = oaspec@util@string_extra:indent(
_pipe@67,
2,
<<"}"/utf8>>
),
_pipe@69 = oaspec@util@string_extra:indent(
_pipe@68,
1,
<<"})"/utf8>>
),
_pipe@70 = oaspec@util@string_extra:line(_pipe@69, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@70);
false ->
Sb@4
end,
Sb@6 = case Has_multipart_body of
true ->
_pipe@71 = Sb@5,
_pipe@72 = oaspec@util@string_extra:line(
_pipe@71,
<<"fn multipart_boundary(headers: Dict(String, String)) -> Result(String, Nil) {"/utf8>>
),
_pipe@73 = oaspec@util@string_extra:indent(
_pipe@72,
1,
<<"case dict.get(headers, \"content-type\") {"/utf8>>
),
_pipe@74 = oaspec@util@string_extra:indent(
_pipe@73,
2,
<<"Ok(content_type) ->"/utf8>>
),
_pipe@75 = oaspec@util@string_extra:indent(
_pipe@74,
3,
<<"list.find_map(string.split(content_type, \";\"), fn(part) {"/utf8>>
),
_pipe@76 = oaspec@util@string_extra:indent(
_pipe@75,
4,
<<"let trimmed = string.trim(part)"/utf8>>
),
_pipe@77 = oaspec@util@string_extra:indent(
_pipe@76,
4,
<<"case string.starts_with(trimmed, \"boundary=\") {"/utf8>>
),
_pipe@78 = oaspec@util@string_extra:indent(
_pipe@77,
5,
<<"True -> Ok(string.replace(trimmed, \"boundary=\", \"\"))"/utf8>>
),
_pipe@79 = oaspec@util@string_extra:indent(
_pipe@78,
5,
<<"False -> Error(Nil)"/utf8>>
),
_pipe@80 = oaspec@util@string_extra:indent(
_pipe@79,
4,
<<"}"/utf8>>
),
_pipe@81 = oaspec@util@string_extra:indent(
_pipe@80,
3,
<<"})"/utf8>>
),
_pipe@82 = oaspec@util@string_extra:indent(
_pipe@81,
2,
<<"Error(_) -> Error(Nil)"/utf8>>
),
_pipe@83 = oaspec@util@string_extra:indent(
_pipe@82,
1,
<<"}"/utf8>>
),
_pipe@84 = oaspec@util@string_extra:line(_pipe@83, <<"}"/utf8>>),
_pipe@85 = oaspec@util@string_extra:blank_line(_pipe@84),
_pipe@86 = oaspec@util@string_extra:line(
_pipe@85,
<<"fn multipart_name(raw_headers: String) -> Result(String, Nil) {"/utf8>>
),
_pipe@87 = oaspec@util@string_extra:indent(
_pipe@86,
1,
<<"list.find_map(string.split(raw_headers, \"\\r\\n\"), fn(line) {"/utf8>>
),
_pipe@88 = oaspec@util@string_extra:indent(
_pipe@87,
2,
<<"case string.contains(line, \"name=\") {"/utf8>>
),
_pipe@89 = oaspec@util@string_extra:indent(
_pipe@88,
3,
<<"True ->"/utf8>>
),
_pipe@90 = oaspec@util@string_extra:indent(
_pipe@89,
4,
<<"list.find_map(string.split(line, \";\"), fn(part) {"/utf8>>
),
_pipe@91 = oaspec@util@string_extra:indent(
_pipe@90,
5,
<<"let trimmed = string.trim(part)"/utf8>>
),
_pipe@92 = oaspec@util@string_extra:indent(
_pipe@91,
5,
<<"case string.starts_with(trimmed, \"name=\") {"/utf8>>
),
_pipe@93 = oaspec@util@string_extra:indent(
_pipe@92,
6,
<<"True -> Ok(string.replace(string.replace(trimmed, \"name=\", \"\"), \"\\\"\", \"\"))"/utf8>>
),
_pipe@94 = oaspec@util@string_extra:indent(
_pipe@93,
6,
<<"False -> Error(Nil)"/utf8>>
),
_pipe@95 = oaspec@util@string_extra:indent(
_pipe@94,
5,
<<"}"/utf8>>
),
_pipe@96 = oaspec@util@string_extra:indent(
_pipe@95,
4,
<<"})"/utf8>>
),
_pipe@97 = oaspec@util@string_extra:indent(
_pipe@96,
3,
<<"False -> Error(Nil)"/utf8>>
),
_pipe@98 = oaspec@util@string_extra:indent(
_pipe@97,
2,
<<"}"/utf8>>
),
_pipe@99 = oaspec@util@string_extra:indent(
_pipe@98,
1,
<<"})"/utf8>>
),
_pipe@100 = oaspec@util@string_extra:line(_pipe@99, <<"}"/utf8>>),
_pipe@101 = oaspec@util@string_extra:blank_line(_pipe@100),
_pipe@102 = oaspec@util@string_extra:line(
_pipe@101,
<<"fn parse_multipart_body(body: String, headers: Dict(String, String)) -> Dict(String, List(String)) {"/utf8>>
),
_pipe@103 = oaspec@util@string_extra:indent(
_pipe@102,
1,
<<"case multipart_boundary(headers) {"/utf8>>
),
_pipe@104 = oaspec@util@string_extra:indent(
_pipe@103,
2,
<<"Ok(boundary) -> {"/utf8>>
),
_pipe@105 = oaspec@util@string_extra:indent(
_pipe@104,
3,
<<"let delimiter = \"--\" <> boundary"/utf8>>
),
_pipe@106 = oaspec@util@string_extra:indent(
_pipe@105,
3,
<<"let parts = string.split(body, delimiter)"/utf8>>
),
_pipe@107 = oaspec@util@string_extra:indent(
_pipe@106,
3,
<<"list.fold(parts, dict.new(), fn(acc, part) {"/utf8>>
),
_pipe@108 = oaspec@util@string_extra:indent(
_pipe@107,
4,
<<"let normalized_part = part |> string.remove_prefix(\"\\r\\n\") |> string.remove_suffix(\"\\r\\n\")"/utf8>>
),
_pipe@109 = oaspec@util@string_extra:indent(
_pipe@108,
4,
<<"case normalized_part == \"\" || normalized_part == \"--\" {"/utf8>>
),
_pipe@110 = oaspec@util@string_extra:indent(
_pipe@109,
5,
<<"True -> acc"/utf8>>
),
_pipe@111 = oaspec@util@string_extra:indent(
_pipe@110,
5,
<<"False ->"/utf8>>
),
_pipe@112 = oaspec@util@string_extra:indent(
_pipe@111,
6,
<<"case string.split_once(normalized_part, on: \"\\r\\n\\r\\n\") {"/utf8>>
),
_pipe@113 = oaspec@util@string_extra:indent(
_pipe@112,
7,
<<"Ok(#(raw_part_headers, raw_value)) ->"/utf8>>
),
_pipe@114 = oaspec@util@string_extra:indent(
_pipe@113,
8,
<<"case multipart_name(raw_part_headers) {"/utf8>>
),
_pipe@115 = oaspec@util@string_extra:indent(
_pipe@114,
9,
<<"Ok(name) -> {"/utf8>>
),
_pipe@116 = oaspec@util@string_extra:indent(
_pipe@115,
10,
<<"let value = raw_value"/utf8>>
),
_pipe@117 = oaspec@util@string_extra:indent(
_pipe@116,
10,
<<"case dict.get(acc, name) {"/utf8>>
),
_pipe@118 = oaspec@util@string_extra:indent(
_pipe@117,
11,
<<"Ok(existing) -> dict.insert(acc, name, list.append(existing, [value]))"/utf8>>
),
_pipe@119 = oaspec@util@string_extra:indent(
_pipe@118,
11,
<<"Error(_) -> dict.insert(acc, name, [value])"/utf8>>
),
_pipe@120 = oaspec@util@string_extra:indent(
_pipe@119,
10,
<<"}"/utf8>>
),
_pipe@121 = oaspec@util@string_extra:indent(
_pipe@120,
9,
<<"}"/utf8>>
),
_pipe@122 = oaspec@util@string_extra:indent(
_pipe@121,
9,
<<"Error(_) -> acc"/utf8>>
),
_pipe@123 = oaspec@util@string_extra:indent(
_pipe@122,
8,
<<"}"/utf8>>
),
_pipe@124 = oaspec@util@string_extra:indent(
_pipe@123,
7,
<<"Error(_) -> acc"/utf8>>
),
_pipe@125 = oaspec@util@string_extra:indent(
_pipe@124,
6,
<<"}"/utf8>>
),
_pipe@126 = oaspec@util@string_extra:indent(
_pipe@125,
4,
<<"}"/utf8>>
),
_pipe@127 = oaspec@util@string_extra:indent(
_pipe@126,
3,
<<"})"/utf8>>
),
_pipe@128 = oaspec@util@string_extra:indent(
_pipe@127,
2,
<<"}"/utf8>>
),
_pipe@129 = oaspec@util@string_extra:indent(
_pipe@128,
2,
<<"Error(_) -> dict.new()"/utf8>>
),
_pipe@130 = oaspec@util@string_extra:indent(
_pipe@129,
1,
<<"}"/utf8>>
),
_pipe@131 = oaspec@util@string_extra:line(_pipe@130, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@131);
false ->
Sb@5
end,
Sb@7 = case Has_nested_form_urlencoded_body of
true ->
_pipe@132 = Sb@6,
_pipe@133 = oaspec@util@string_extra:line(
_pipe@132,
<<"fn form_object_present(form_body: Dict(String, List(String)), prefix: String, props: List(String)) -> Bool {"/utf8>>
),
_pipe@134 = oaspec@util@string_extra:indent(
_pipe@133,
1,
<<"list.any(props, fn(prop) { dict.has_key(form_body, prefix <> \"[\" <> prop <> \"]\") })"/utf8>>
),
_pipe@135 = oaspec@util@string_extra:line(_pipe@134, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@135);
false ->
Sb@6
end,
Sb@8 = begin
_pipe@136 = Sb@7,
_pipe@137 = oaspec@util@string_extra:doc_comment(
_pipe@136,
<<"Route an incoming request to the appropriate handler."/utf8>>
),
_pipe@138 = oaspec@util@string_extra:line(
_pipe@137,
<<<<<<<<<<<<"pub fn route(app_state: handlers.State, method: String, path: List(String), "/utf8,
(route_arg_name(
<<"query"/utf8>>,
Uses_query
))/binary>>/binary,
": Dict(String, List(String)), "/utf8>>/binary,
(route_arg_name(<<"headers"/utf8>>, Uses_headers))/binary>>/binary,
": Dict(String, String), "/utf8>>/binary,
(route_arg_name(<<"body"/utf8>>, Uses_body))/binary>>/binary,
": String) -> ServerResponse {"/utf8>>
),
oaspec@util@string_extra:indent(
_pipe@138,
1,
<<"case method, path {"/utf8>>
)
end,
Sb@10 = gleam@list:fold(
Operations,
Sb@8,
fun(Sb@9, Op@21) ->
{Op_id, Operation@21, Path, Method} = Op@21,
Fn_name = oaspec@util@naming:operation_to_function_name(Op_id),
Method_str = oaspec@openapi@spec:method_to_string(Method),
Path_pattern = path_to_pattern(Path),
Has_params = not gleam@list:is_empty(
erlang:element(6, Operation@21)
)
orelse gleam@option:is_some(erlang:element(7, Operation@21)),
_pipe@139 = Sb@9,
_pipe@140 = oaspec@util@string_extra:indent(
_pipe@139,
2,
<<<<<<<<"\""/utf8, Method_str/binary>>/binary, "\", "/utf8>>/binary,
Path_pattern/binary>>/binary,
" -> {"/utf8>>
),
_pipe@141 = generate_route_body(
_pipe@140,
Op_id,
Fn_name,
Operation@21,
Path,
Has_params,
Ctx
),
oaspec@util@string_extra:indent(_pipe@141, 2, <<"}"/utf8>>)
end
),
Sb@11 = begin
_pipe@142 = Sb@10,
_pipe@143 = oaspec@util@string_extra:indent(
_pipe@142,
2,
<<"_, _ -> "/utf8,
(problem_response_expr(404, <<"not found"/utf8>>))/binary>>
),
_pipe@144 = oaspec@util@string_extra:indent(_pipe@143, 1, <<"}"/utf8>>),
_pipe@145 = oaspec@util@string_extra:line(_pipe@144, <<"}"/utf8>>),
oaspec@util@string_extra:blank_line(_pipe@145)
end,
Sb@12 = case Needs_cookie_lookup of
true ->
generate_cookie_lookup(Sb@11);
false ->
Sb@11
end,
oaspec@util@string_extra:to_string(Sb@12).
-file("src/oaspec/codegen/server.gleam", 33).
?DOC(
" Generate server stub files.\n"
"\n"
" Issue #247 splits the handler surface in two:\n"
"\n"
" - `handlers.gleam` carries user-editable panic stubs and is emitted\n"
" only on first generation (`SkipIfExists`). Re-running\n"
" `oaspec generate` leaves it alone, so user implementations\n"
" survive regeneration.\n"
" - `handlers_generated.gleam` is a sealed delegator that `router.gleam`\n"
" imports. Each operation forwards to `handlers.<op_name>(req)`. It\n"
" is always overwritten so the wiring stays in lock-step with the\n"
" spec.\n"
).
-spec generate(oaspec@codegen@context:context()) -> list(oaspec@codegen@context:generated_file()).
generate(Ctx) ->
Operations = oaspec@openapi@operations:collect_operations(Ctx),
Handlers_content = generate_handlers(Ctx, Operations),
Handlers_generated_content = generate_handlers_generated(Ctx, Operations),
Router_content = generate_router(Ctx, Operations),
[{generated_file,
<<"handlers.gleam"/utf8>>,
Handlers_content,
server_target,
skip_if_exists},
{generated_file,
<<"handlers_generated.gleam"/utf8>>,
Handlers_generated_content,
server_target,
overwrite},
{generated_file,
<<"router.gleam"/utf8>>,
Router_content,
server_target,
overwrite}].