Packages
oaspec
0.22.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/schema_utils.gleam
/// Shared schema analysis utilities used by types.gleam and ir_build.gleam.
/// Extracted to break the import-cycle that previously forced ir_build.gleam
/// to mirror these helpers from types.gleam.
import gleam/dict
import gleam/list
import oaspec/codegen/context.{type Context}
import oaspec/openapi/resolver
import oaspec/openapi/schema.{
type SchemaObject, type SchemaRef, AllOfSchema, Inline, ObjectSchema,
Reference, Typed, Untyped,
}
/// Check if a schema has typed or untyped additionalProperties that would need Dict.
pub fn schema_has_additional_properties(
schema_ref: SchemaRef,
ctx: Context,
) -> Bool {
case schema_ref {
Inline(ObjectSchema(additional_properties: Typed(_), ..)) -> True
Inline(ObjectSchema(additional_properties: Untyped, ..)) -> True
Inline(AllOfSchema(schemas:, ..)) ->
list.any(schemas, fn(s) { schema_has_additional_properties(s, ctx) })
Reference(..) ->
case resolver.resolve_schema_ref(schema_ref, context.spec(ctx)) {
Ok(schema_obj) ->
schema_has_additional_properties(Inline(schema_obj), ctx)
// nolint: thrown_away_error -- unresolved refs are treated as not having additionalProperties; the resolver reports the ref error separately
Error(_) -> False
}
_ -> False
}
}
/// Check if a schema has untyped additionalProperties (needs Dynamic import).
pub fn schema_has_untyped_additional_properties(
schema_ref: SchemaRef,
ctx: Context,
) -> Bool {
case schema_ref {
Inline(ObjectSchema(additional_properties: Untyped, ..)) -> True
Inline(AllOfSchema(schemas:, ..)) ->
list.any(schemas, fn(s) {
schema_has_untyped_additional_properties(s, ctx)
})
Reference(..) ->
case resolver.resolve_schema_ref(schema_ref, context.spec(ctx)) {
Ok(schema_obj) ->
schema_has_untyped_additional_properties(Inline(schema_obj), ctx)
// nolint: thrown_away_error -- unresolved refs are treated as not having untyped additionalProperties; the resolver reports the ref error separately
Error(_) -> False
}
_ -> False
}
}
/// Check if a schema has any optional or nullable fields that would need Option.
pub fn schema_has_optional_fields(schema_ref: SchemaRef, ctx: Context) -> Bool {
case schema_ref {
Inline(ObjectSchema(properties:, required:, ..)) -> {
dict.to_list(properties)
|> list.any(fn(entry) {
let #(prop_name, prop_ref) = entry
!list.contains(required, prop_name)
|| schema_ref_is_nullable(prop_ref, ctx)
})
}
Inline(AllOfSchema(schemas:, ..)) ->
list.any(schemas, fn(s) { schema_has_optional_fields(s, ctx) })
Reference(..) ->
case resolver.resolve_schema_ref(schema_ref, context.spec(ctx)) {
Ok(schema_obj) -> schema_has_optional_fields(Inline(schema_obj), ctx)
// nolint: thrown_away_error -- unresolved refs are treated as not having optional fields; the resolver reports the ref error separately
Error(_) -> False
}
_ -> False
}
}
/// Check if a SchemaRef is nullable, resolving $ref if needed.
pub fn schema_ref_is_nullable(ref: SchemaRef, ctx: Context) -> Bool {
case ref {
Inline(inline_schema) -> schema.is_nullable(inline_schema)
Reference(..) ->
case resolver.resolve_schema_ref(ref, context.spec(ctx)) {
Ok(resolved) -> schema.is_nullable(resolved)
// nolint: thrown_away_error -- unresolved refs are treated as non-nullable; the resolver reports the ref error separately
Error(_) -> False
}
}
}
/// Check if a SchemaRef has readOnly metadata, resolving $ref if needed.
pub fn schema_ref_is_read_only(ref: SchemaRef, ctx: Context) -> Bool {
case ref {
Inline(inline_schema) -> schema.get_metadata(inline_schema).read_only
Reference(..) ->
case resolver.resolve_schema_ref(ref, context.spec(ctx)) {
Ok(resolved) -> schema.get_metadata(resolved).read_only
// nolint: thrown_away_error -- unresolved refs are treated as not read-only; the resolver reports the ref error separately
Error(_) -> False
}
}
}
/// Check if a SchemaRef has writeOnly metadata, resolving $ref if needed.
pub fn schema_ref_is_write_only(ref: SchemaRef, ctx: Context) -> Bool {
case ref {
Inline(inline_schema) -> schema.get_metadata(inline_schema).write_only
Reference(..) ->
case resolver.resolve_schema_ref(ref, context.spec(ctx)) {
Ok(resolved) -> schema.get_metadata(resolved).write_only
// nolint: thrown_away_error -- unresolved refs are treated as not write-only; the resolver reports the ref error separately
Error(_) -> False
}
}
}
/// Filter readOnly properties from an ObjectSchema for request body context.
/// Returns a new schema with readOnly properties removed.
pub fn filter_read_only_properties(
schema_obj: SchemaObject,
ctx: Context,
) -> SchemaObject {
case schema_obj {
ObjectSchema(
metadata:,
properties:,
required:,
additional_properties:,
min_properties:,
max_properties:,
) -> {
let filtered_props =
dict.filter(properties, fn(_name, prop_ref) {
!schema_ref_is_read_only(prop_ref, ctx)
})
let filtered_required =
list.filter(required, fn(name) {
case dict.get(filtered_props, name) {
Ok(_) -> True
// nolint: thrown_away_error -- dict miss means the required field was filtered out; no error value to preserve
Error(_) -> False
}
})
ObjectSchema(
metadata:,
properties: filtered_props,
required: filtered_required,
additional_properties:,
min_properties:,
max_properties:,
)
}
_ -> schema_obj
}
}
/// Filter writeOnly properties from an ObjectSchema for response body context.
/// Returns a new schema with writeOnly properties removed.
pub fn filter_write_only_properties(
schema_obj: SchemaObject,
ctx: Context,
) -> SchemaObject {
case schema_obj {
ObjectSchema(
metadata:,
properties:,
required:,
additional_properties:,
min_properties:,
max_properties:,
) -> {
let filtered_props =
dict.filter(properties, fn(_name, prop_ref) {
!schema_ref_is_write_only(prop_ref, ctx)
})
let filtered_required =
list.filter(required, fn(name) {
case dict.get(filtered_props, name) {
Ok(_) -> True
// nolint: thrown_away_error -- dict miss means the required field was filtered out; no error value to preserve
Error(_) -> False
}
})
ObjectSchema(
metadata:,
properties: filtered_props,
required: filtered_required,
additional_properties:,
min_properties:,
max_properties:,
)
}
_ -> schema_obj
}
}