Packages
oaspec
0.27.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/internal/codegen/client_response.gleam
import gleam/option.{Some}
import oaspec/internal/codegen/context.{type Context}
import oaspec/internal/openapi/schema.{Inline, Reference}
import oaspec/internal/openapi/spec
import oaspec/internal/util/content_type
import oaspec/internal/util/http
import oaspec/internal/util/naming
import oaspec/internal/util/string_extra as se
/// Generate response handling for a single content type.
pub fn generate_single_content_response(
sb: se.StringBuilder,
status_code: http.HttpStatusCode,
variant_name: String,
media_type_name: String,
media_type: spec.MediaType,
op_id: String,
ctx: Context,
) -> se.StringBuilder {
case content_type.from_string(media_type_name) {
content_type.TextPlain
| content_type.ApplicationXml
| content_type.TextXml
| content_type.ApplicationOctetStream ->
case media_type.schema {
Some(_) ->
sb
|> se.indent(
4,
http.status_code_to_int_pattern(status_code)
<> " -> Ok("
<> variant_name
<> "(resp.body))",
)
_ ->
sb
|> se.indent(
4,
http.status_code_to_int_pattern(status_code)
<> " -> Ok("
<> variant_name
<> ")",
)
}
_ ->
case media_type.schema {
Some(schema_ref) -> {
let decode_expr =
get_response_decode_expr(schema_ref, op_id, status_code, ctx)
sb
|> se.indent(
4,
http.status_code_to_int_pattern(status_code) <> " -> {",
)
|> se.indent(5, "case " <> decode_expr <> " {")
|> se.indent(6, "Ok(decoded) -> Ok(" <> variant_name <> "(decoded))")
|> se.indent(
6,
"Error(_) -> Error(DecodeError(detail: \"Failed to decode response body\"))",
)
|> se.indent(5, "}")
|> se.indent(4, "}")
}
_ ->
sb
|> se.indent(
4,
http.status_code_to_int_pattern(status_code)
<> " -> Ok("
<> variant_name
<> ")",
)
}
}
}
/// Generate response handling for multiple content types.
/// Since the response variant uses String for multi-content (to stay type-safe),
/// all branches return resp.body directly.
pub fn generate_multi_content_response(
sb: se.StringBuilder,
status_code: http.HttpStatusCode,
variant_name: String,
_content_entries: List(#(String, spec.MediaType)),
_op_id: String,
_ctx: Context,
) -> se.StringBuilder {
// Multi-content response type is always String, so just return resp.body
sb
|> se.indent(
4,
http.status_code_to_int_pattern(status_code)
<> " -> Ok("
<> variant_name
<> "(resp.body))",
)
}
/// Get the decode expression for a response body.
pub fn get_response_decode_expr(
schema_ref: schema.SchemaRef,
op_id: String,
status_code: http.HttpStatusCode,
_ctx: Context,
) -> String {
case schema_ref {
Reference(name:, ..) -> {
"decode.decode_" <> naming.to_snake_case(name) <> "(resp.body)"
}
Inline(schema.ArraySchema(items:, ..)) ->
case items {
Reference(name:, ..) -> {
"decode.decode_" <> naming.to_snake_case(name) <> "_list(resp.body)"
}
Inline(inner) -> {
let inner_decoder = inline_schema_to_decoder(inner)
"json.parse(resp.body, decode.list(" <> inner_decoder <> "))"
}
}
Inline(schema.StringSchema(..)) ->
"json.parse(resp.body, dyn_decode.string)"
Inline(schema.IntegerSchema(..)) -> "json.parse(resp.body, dyn_decode.int)"
Inline(schema.NumberSchema(..)) -> "json.parse(resp.body, dyn_decode.float)"
Inline(schema.BooleanSchema(..)) -> "json.parse(resp.body, dyn_decode.bool)"
Inline(_) -> {
let fn_name =
"decode_"
<> naming.to_snake_case(op_id)
<> "_response_"
<> naming.to_snake_case(http.status_code_suffix(status_code))
"decode." <> fn_name <> "(resp.body)"
}
}
}
/// Convert an inline schema to a decoder expression for use in generated client.
/// Uses dyn_decode (gleam/dynamic/decode) to avoid collision with the generated
/// decode module.
pub fn inline_schema_to_decoder(s: schema.SchemaObject) -> String {
case s {
schema.StringSchema(..) -> "dyn_decode.string"
schema.IntegerSchema(..) -> "dyn_decode.int"
schema.NumberSchema(..) -> "dyn_decode.float"
schema.BooleanSchema(..) -> "dyn_decode.bool"
_ -> "dyn_decode.string"
}
}