-module(glon). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/glon.gleam"). -export([describe/2, decode/2, string/0, integer/0, number/0, boolean/0, array/1, nullable/1, enum/1, enum_map/1, constant/1, constant_map/2, map/2, one_of/1, any_of/1, success/1, tagged_union/2, field/3, optional/3, optional_or_null/3, field_with_default/5, to_json/1, to_string/1]). -export_type([schema_node/0, combiner_keyword/0, object_field/0, json_schema/1]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type schema_node() :: string_node | integer_node | number_node | boolean_node | {array_node, schema_node()} | {nullable_node, schema_node()} | {object_node, list(object_field())} | {description_node, schema_node(), binary()} | {enum_node, list(binary())} | {const_node, binary()} | {default_node, schema_node(), gleam@json:json()} | {combiner_node, combiner_keyword(), list(schema_node())}. -type combiner_keyword() :: one_of | any_of. -type object_field() :: {object_field, binary(), schema_node(), boolean()}. -opaque json_schema(DTC) :: {json_schema, schema_node(), gleam@dynamic@decode:decoder(DTC)}. -file("src/glon.gleam", 306). ?DOC( " Attach a `\"description\"` annotation to any schema.\n" "\n" " The description appears in the JSON Schema output but does not affect\n" " decoding. Can be composed with other annotations and combinators.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema =\n" " glon.string()\n" " |> glon.describe(\"A person's full name\")\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"string\\\",\\\"description\\\":\\\"A person's full name\\\"}\"\n" " ```\n" ). -spec describe(json_schema(DUB), binary()) -> json_schema(DUB). describe(Schema, Description) -> {json_schema, {description_node, erlang:element(2, Schema), Description}, erlang:element(3, Schema)}. -file("src/glon.gleam", 711). ?DOC( " Decode a JSON string using the schema's decoder.\n" "\n" " Parses the given JSON string and decodes it according to the schema.\n" " Returns `Ok(value)` on success or `Error(DecodeError)` on failure.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.integer()\n" " glon.decode(schema, from: \"42\")\n" " // -> Ok(42)\n" "\n" " glon.decode(schema, from: \"\\\"not a number\\\"\")\n" " // -> Error(...)\n" " ```\n" ). -spec decode(json_schema(DVW), binary()) -> {ok, DVW} | {error, gleam@json:decode_error()}. decode(Schema, Json_string) -> gleam@json:parse(Json_string, erlang:element(3, Schema)). -file("src/glon.gleam", 70). ?DOC( " A schema for JSON strings.\n" "\n" " Produces `{\"type\": \"string\"}` and decodes JSON strings into `String`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.string()\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"string\\\"}\"\n" "\n" " glon.decode(schema, from: \"\\\"hello\\\"\")\n" " // -> Ok(\"hello\")\n" " ```\n" ). -spec string() -> json_schema(binary()). string() -> {json_schema, string_node, {decoder, fun gleam@dynamic@decode:decode_string/1}}. -file("src/glon.gleam", 88). ?DOC( " A schema for JSON integers.\n" "\n" " Produces `{\"type\": \"integer\"}` and decodes JSON integers into `Int`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.integer()\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"integer\\\"}\"\n" "\n" " glon.decode(schema, from: \"42\")\n" " // -> Ok(42)\n" " ```\n" ). -spec integer() -> json_schema(integer()). integer() -> {json_schema, integer_node, {decoder, fun gleam@dynamic@decode:decode_int/1}}. -file("src/glon.gleam", 106). ?DOC( " A schema for JSON numbers (floating point).\n" "\n" " Produces `{\"type\": \"number\"}` and decodes JSON numbers into `Float`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.number()\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"number\\\"}\"\n" "\n" " glon.decode(schema, from: \"3.14\")\n" " // -> Ok(3.14)\n" " ```\n" ). -spec number() -> json_schema(float()). number() -> {json_schema, number_node, {decoder, fun gleam@dynamic@decode:decode_float/1}}. -file("src/glon.gleam", 124). ?DOC( " A schema for JSON booleans.\n" "\n" " Produces `{\"type\": \"boolean\"}` and decodes JSON booleans into `Bool`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.boolean()\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"boolean\\\"}\"\n" "\n" " glon.decode(schema, from: \"true\")\n" " // -> Ok(True)\n" " ```\n" ). -spec boolean() -> json_schema(boolean()). boolean() -> {json_schema, boolean_node, {decoder, fun gleam@dynamic@decode:decode_bool/1}}. -file("src/glon.gleam", 145). ?DOC( " A schema for JSON arrays where every element matches the given inner schema.\n" "\n" " Produces `{\"type\": \"array\", \"items\": }` and decodes JSON arrays\n" " into `List(t)`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.array(of: glon.string())\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"array\\\",\\\"items\\\":{\\\"type\\\":\\\"string\\\"}}\"\n" "\n" " glon.decode(schema, from: \"[\\\"a\\\",\\\"b\\\"]\")\n" " // -> Ok([\"a\", \"b\"])\n" " ```\n" ). -spec array(json_schema(DTI)) -> json_schema(list(DTI)). array(Inner) -> {json_schema, {array_node, erlang:element(2, Inner)}, gleam@dynamic@decode:list(erlang:element(3, Inner))}. -file("src/glon.gleam", 171). ?DOC( " A schema for values that may be `null`.\n" "\n" " Wraps an inner schema to also accept JSON `null`. Produces a schema with\n" " `\"type\": [\"\", \"null\"]` for simple inner types, or an `anyOf`\n" " for complex ones. Decodes into `Option(t)`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.nullable(glon.string())\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":[\\\"string\\\",\\\"null\\\"]}\"\n" "\n" " glon.decode(schema, from: \"\\\"hello\\\"\")\n" " // -> Ok(Some(\"hello\"))\n" "\n" " glon.decode(schema, from: \"null\")\n" " // -> Ok(None)\n" " ```\n" ). -spec nullable(json_schema(DTM)) -> json_schema(gleam@option:option(DTM)). nullable(Inner) -> {json_schema, {nullable_node, erlang:element(2, Inner)}, gleam@dynamic@decode:optional(erlang:element(3, Inner))}. -file("src/glon.gleam", 272). -spec enum_decoder(list(binary()), fun((binary()) -> DTZ)) -> gleam@dynamic@decode:decoder(DTZ). enum_decoder(Values, Mapper) -> {First@1, Rest@1} = case gleam@list:map( Values, fun(V) -> _pipe = {decoder, fun gleam@dynamic@decode:decode_string/1}, gleam@dynamic@decode:then(_pipe, fun(S) -> case S =:= V of true -> gleam@dynamic@decode:success(Mapper(V)); false -> gleam@dynamic@decode:failure( Mapper(V), <<"one of: "/utf8, (gleam@string:join(Values, <<", "/utf8>>))/binary>> ) end end) end ) of [First | Rest] -> {First, Rest}; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"glon"/utf8>>, function => <<"enum_decoder"/utf8>>, line => 276, value => _assert_fail, start => 7282, 'end' => 7578, pattern_start => 7293, pattern_end => 7308}) end, gleam@dynamic@decode:one_of(First@1, Rest@1). -file("src/glon.gleam", 198). ?DOC( " A schema that restricts values to a fixed set of strings.\n" "\n" " Produces `{\"type\": \"string\", \"enum\": [...]}` and decodes only strings\n" " that appear in the given list. Decoding rejects any value not in the list.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.enum([\"red\", \"green\", \"blue\"])\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"string\\\",\\\"enum\\\":[\\\"red\\\",\\\"green\\\",\\\"blue\\\"]}\"\n" "\n" " glon.decode(schema, from: \"\\\"red\\\"\")\n" " // -> Ok(\"red\")\n" "\n" " glon.decode(schema, from: \"\\\"yellow\\\"\")\n" " // -> Error(...)\n" " ```\n" ). -spec enum(list(binary())) -> json_schema(binary()). enum(Values) -> Decoder = enum_decoder(Values, fun(S) -> S end), {json_schema, {enum_node, Values}, Decoder}. -file("src/glon.gleam", 220). ?DOC( " Like `enum`, but maps each string value to an arbitrary Gleam type.\n" "\n" " Takes a list of `#(json_string, gleam_value)` pairs. The schema output\n" " uses the JSON strings, while the decoder maps each to its paired value.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.enum_map([\n" " #(\"red\", Red), #(\"green\", Green), #(\"blue\", Blue),\n" " ])\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"string\\\",\\\"enum\\\":[\\\"red\\\",\\\"green\\\",\\\"blue\\\"]}\"\n" "\n" " glon.decode(schema, from: \"\\\"red\\\"\")\n" " // -> Ok(Red)\n" " ```\n" ). -spec enum_map(list({binary(), DTS})) -> json_schema(DTS). enum_map(Variants) -> Decoder = enum_decoder( gleam@list:map(Variants, fun(V) -> erlang:element(1, V) end), fun(S) -> Mapped@1 = case gleam@list:find( Variants, fun(V@1) -> erlang:element(1, V@1) =:= S end ) of {ok, {_, Mapped}} -> Mapped; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"glon"/utf8>>, function => <<"enum_map"/utf8>>, line => 223, value => _assert_fail, start => 5870, 'end' => 5939, pattern_start => 5881, pattern_end => 5897}) end, Mapped@1 end ), {json_schema, {enum_node, gleam@list:map(Variants, fun(V@2) -> erlang:element(1, V@2) end)}, Decoder}. -file("src/glon.gleam", 250). ?DOC( " A schema that accepts only a single specific string value.\n" "\n" " Produces `{\"type\": \"string\", \"const\": \"\"}` and decodes only\n" " that exact string.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.constant(\"active\")\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"string\\\",\\\"const\\\":\\\"active\\\"}\"\n" "\n" " glon.decode(schema, from: \"\\\"active\\\"\")\n" " // -> Ok(\"active\")\n" "\n" " glon.decode(schema, from: \"\\\"inactive\\\"\")\n" " // -> Error(...)\n" " ```\n" ). -spec constant(binary()) -> json_schema(binary()). constant(Value) -> Decoder = enum_decoder([Value], fun(S) -> S end), {json_schema, {const_node, Value}, Decoder}. -file("src/glon.gleam", 267). ?DOC( " Like `constant`, but maps the matched string to an arbitrary Gleam value.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.constant_map(\"yes\", mapped: True)\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"string\\\",\\\"const\\\":\\\"yes\\\"}\"\n" "\n" " glon.decode(schema, from: \"\\\"yes\\\"\")\n" " // -> Ok(True)\n" " ```\n" ). -spec constant_map(binary(), DTW) -> json_schema(DTW). constant_map(Value, Mapped) -> Decoder = enum_decoder([Value], fun(_) -> Mapped end), {json_schema, {const_node, Value}, Decoder}. -file("src/glon.gleam", 336). ?DOC( " Transform the decoded type of a schema without changing its JSON Schema output.\n" "\n" " The schema definition stays the same, but the decoder maps decoded values\n" " through the given function. Useful for wrapping primitives in custom types\n" " or making different schemas produce the same type for use with `one_of`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " type Email { Email(String) }\n" "\n" " let schema = glon.string() |> glon.map(Email)\n" " glon.to_string(schema)\n" " // -> \"{\\\"type\\\":\\\"string\\\"}\"\n" "\n" " glon.decode(schema, from: \"\\\"a@b.com\\\"\")\n" " // -> Ok(Email(\"a@b.com\"))\n" " ```\n" ). -spec map(json_schema(DUE), fun((DUE) -> DUG)) -> json_schema(DUG). map(Schema, Transform) -> {json_schema, erlang:element(2, Schema), gleam@dynamic@decode:then( erlang:element(3, Schema), fun(A) -> gleam@dynamic@decode:success(Transform(A)) end )}. -file("src/glon.gleam", 361). ?DOC( " A schema where the value must match exactly one of the given sub-schemas.\n" "\n" " Produces `{\"oneOf\": [...]}`. The decoder tries each variant in order and\n" " returns the first successful match. All variants must decode to the same\n" " Gleam type `t` — use `map` to align types if needed.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " type Value { TextVal(String) NumVal(Int) }\n" "\n" " let schema = glon.one_of([\n" " glon.string() |> glon.map(TextVal),\n" " glon.integer() |> glon.map(NumVal),\n" " ])\n" " glon.to_string(schema)\n" " // -> \"{\\\"oneOf\\\":[{\\\"type\\\":\\\"string\\\"},{\\\"type\\\":\\\"integer\\\"}]}\"\n" " ```\n" ). -spec one_of(list(json_schema(DUI))) -> json_schema(DUI). one_of(Variants) -> {First@1, Rest@1} = case Variants of [First | Rest] -> {First, Rest}; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"glon"/utf8>>, function => <<"one_of"/utf8>>, line => 362, value => _assert_fail, start => 9780, 'end' => 9817, pattern_start => 9791, pattern_end => 9806}) end, {json_schema, {combiner_node, one_of, gleam@list:map(Variants, fun(V) -> erlang:element(2, V) end)}, gleam@dynamic@decode:one_of( erlang:element(3, First@1), gleam@list:map(Rest@1, fun(V@1) -> erlang:element(3, V@1) end) )}. -file("src/glon.gleam", 389). ?DOC( " A schema where the value must match at least one of the given sub-schemas.\n" "\n" " Produces `{\"anyOf\": [...]}`. Behaves identically to `one_of` for decoding\n" " (first match wins), but generates `anyOf` instead of `oneOf` in the schema.\n" " The distinction matters for JSON Schema validation: `oneOf` requires exactly\n" " one match, `anyOf` allows multiple.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = glon.any_of([\n" " glon.string() |> glon.map(TextVal),\n" " glon.integer() |> glon.map(NumVal),\n" " ])\n" " glon.to_string(schema)\n" " // -> \"{\\\"anyOf\\\":[{\\\"type\\\":\\\"string\\\"},{\\\"type\\\":\\\"integer\\\"}]}\"\n" " ```\n" ). -spec any_of(list(json_schema(DUM))) -> json_schema(DUM). any_of(Variants) -> {First@1, Rest@1} = case Variants of [First | Rest] -> {First, Rest}; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"glon"/utf8>>, function => <<"any_of"/utf8>>, line => 390, value => _assert_fail, start => 10719, 'end' => 10756, pattern_start => 10730, pattern_end => 10745}) end, {json_schema, {combiner_node, any_of, gleam@list:map(Variants, fun(V) -> erlang:element(2, V) end)}, gleam@dynamic@decode:one_of( erlang:element(3, First@1), gleam@list:map(Rest@1, fun(V@1) -> erlang:element(3, V@1) end) )}. -file("src/glon.gleam", 471). ?DOC( " Finish building an object schema by providing the final value.\n" "\n" " This is used as the last step in a chain of `field`, `optional`, or\n" " `field_with_default` calls via `use` syntax. It produces an empty object\n" " node that gets merged with the fields collected from the chain.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = {\n" " use name <- glon.field(\"name\", glon.string())\n" " use age <- glon.field(\"age\", glon.integer())\n" " glon.success(User(name:, age:))\n" " }\n" " ```\n" ). -spec success(DUU) -> json_schema(DUU). success(Value) -> {json_schema, {object_node, []}, gleam@dynamic@decode:success(Value)}. -file("src/glon.gleam", 720). -spec get_object_fields(schema_node()) -> list(object_field()). get_object_fields(Node) -> case Node of {object_node, Fields} -> Fields; _ -> [] end. -file("src/glon.gleam", 422). ?DOC( " A schema for discriminated unions (tagged unions).\n" "\n" " Produces a `oneOf` schema where each variant is an object with a\n" " discriminator field set to a constant tag value. The decoder checks\n" " the discriminator field to select the correct variant.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " type Shape { Circle(Float) Square(Float) }\n" "\n" " let schema = glon.tagged_union(\"type\", [\n" " #(\"circle\", {\n" " use radius <- glon.field(\"radius\", glon.number())\n" " glon.success(Circle(radius))\n" " }),\n" " #(\"square\", {\n" " use side <- glon.field(\"side\", glon.number())\n" " glon.success(Square(side))\n" " }),\n" " ])\n" " ```\n" ). -spec tagged_union(binary(), list({binary(), json_schema(DUQ)})) -> json_schema(DUQ). tagged_union(Discriminator, Variants) -> Schema_variants = gleam@list:map( Variants, fun(Variant) -> {Tag, Schema} = Variant, Fields = get_object_fields(erlang:element(2, Schema)), {object_node, [{object_field, Discriminator, {const_node, Tag}, true} | Fields]} end ), {First_decoder@1, Rest_decoders@1} = case gleam@list:map( Variants, fun(Variant@1) -> {Tag@1, Schema@1} = Variant@1, gleam@dynamic@decode:field( Discriminator, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Decoded_tag) -> case Decoded_tag =:= Tag@1 of true -> erlang:element(3, Schema@1); false -> gleam@dynamic@decode:failure( glon_ffi:coerce_nil(), Tag@1 ) end end ) end ) of [First_decoder | Rest_decoders] -> {First_decoder, Rest_decoders}; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"glon"/utf8>>, function => <<"tagged_union"/utf8>>, line => 438, value => _assert_fail, start => 12189, 'end' => 12503, pattern_start => 12200, pattern_end => 12232}) end, {json_schema, {combiner_node, one_of, Schema_variants}, gleam@dynamic@decode:one_of(First_decoder@1, Rest_decoders@1)}. -file("src/glon.gleam", 493). ?DOC( " Declare a required object field.\n" "\n" " The field appears in the schema's `\"properties\"` and `\"required\"` array.\n" " Decoding fails if the field is missing from the JSON input.\n" "\n" " Used with Gleam's `use` syntax to chain multiple fields together.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = {\n" " use name <- glon.field(\"name\", glon.string())\n" " use age <- glon.field(\"age\", glon.integer())\n" " glon.success(User(name:, age:))\n" " }\n" " glon.decode(schema, from: \"{\\\"name\\\":\\\"Alice\\\",\\\"age\\\":30}\")\n" " // -> Ok(User(name: \"Alice\", age: 30))\n" " ```\n" ). -spec field(binary(), json_schema(DUW), fun((DUW) -> json_schema(DUY))) -> json_schema(DUY). field(Name, Schema, Next) -> Rest = Next(glon_ffi:coerce_nil()), Rest_fields = get_object_fields(erlang:element(2, Rest)), Node = {object_node, [{object_field, Name, erlang:element(2, Schema), true} | Rest_fields]}, Decoder = begin gleam@dynamic@decode:field( Name, erlang:element(3, Schema), fun(Value) -> Result = Next(Value), erlang:element(3, Result) end ) end, {json_schema, Node, Decoder}. -file("src/glon.gleam", 534). ?DOC( " Declare an optional object field.\n" "\n" " The field appears in the schema's `\"properties\"` but not in `\"required\"`.\n" " The continuation receives `Option(a)`: `Some(value)` when the field is\n" " present, `None` when absent.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = {\n" " use name <- glon.field(\"name\", glon.string())\n" " use email <- glon.optional(\"email\", glon.string())\n" " glon.success(User(name:, email:))\n" " }\n" " glon.decode(schema, from: \"{\\\"name\\\":\\\"Alice\\\"}\")\n" " // -> Ok(User(name: \"Alice\", email: None))\n" " ```\n" ). -spec optional( binary(), json_schema(DVB), fun((gleam@option:option(DVB)) -> json_schema(DVE)) ) -> json_schema(DVE). optional(Name, Schema, Next) -> Rest = Next(glon_ffi:coerce_nil()), Rest_fields = get_object_fields(erlang:element(2, Rest)), Node = {object_node, [{object_field, Name, erlang:element(2, Schema), false} | Rest_fields]}, Decoder = begin gleam@dynamic@decode:optional_field( Name, none, gleam@dynamic@decode:optional(erlang:element(3, Schema)), fun(Value) -> Result = Next(Value), erlang:element(3, Result) end ) end, {json_schema, Node, Decoder}. -file("src/glon.gleam", 577). ?DOC( " Declare an optional object field that may also be `null`.\n" "\n" " Like `optional`, but the schema type is wrapped with `nullable` and the\n" " decoder treats both an absent field and a `null` value as `None`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = {\n" " use name <- glon.field(\"name\", glon.string())\n" " use nick <- glon.optional_or_null(\"nickname\", glon.string())\n" " glon.success(User(name:, nickname: nick))\n" " }\n" " // Field absent -> None, field null -> None, field present -> Some(value)\n" " ```\n" ). -spec optional_or_null( binary(), json_schema(DVH), fun((gleam@option:option(DVH)) -> json_schema(DVK)) ) -> json_schema(DVK). optional_or_null(Name, Schema, Next) -> Rest = Next(glon_ffi:coerce_nil()), Rest_fields = get_object_fields(erlang:element(2, Rest)), Node = {object_node, [{object_field, Name, {nullable_node, erlang:element(2, Schema)}, false} | Rest_fields]}, Decoder = begin gleam@dynamic@decode:optional_field( Name, none, gleam@dynamic@decode:optional(erlang:element(3, Schema)), fun(Value) -> Result = Next(Value), erlang:element(3, Result) end ) end, {json_schema, Node, Decoder}. -file("src/glon.gleam", 640). ?DOC( " Declare an optional object field with a default value.\n" "\n" " The field appears in the schema's `\"properties\"` with a `\"default\"`\n" " annotation, but not in `\"required\"`. When the field is absent from the\n" " JSON input, the decoder uses the provided default value. Unlike `optional`,\n" " the continuation receives the unwrapped type `a` directly, not `Option(a)`.\n" "\n" " The `encode` parameter converts the default value to `json.Json` for the\n" " schema output. For primitives, use `json.int`, `json.string`, `json.float`,\n" " or `json.bool`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let schema = {\n" " use host <- glon.field(\"host\", glon.string())\n" " use port <- glon.field_with_default(\n" " \"port\", glon.integer(),\n" " default: 8080, encode: json.int,\n" " )\n" " glon.success(Config(host:, port:))\n" " }\n" " glon.to_string(schema)\n" " // -> \"{...\\\"port\\\":{\\\"type\\\":\\\"integer\\\",\\\"default\\\":8080}...}\"\n" "\n" " glon.decode(schema, from: \"{\\\"host\\\":\\\"localhost\\\"}\")\n" " // -> Ok(Config(host: \"localhost\", port: 8080))\n" "\n" " glon.decode(schema, from: \"{\\\"host\\\":\\\"localhost\\\",\\\"port\\\":3000}\")\n" " // -> Ok(Config(host: \"localhost\", port: 3000))\n" " ```\n" ). -spec field_with_default( binary(), json_schema(DVN), DVN, fun((DVN) -> gleam@json:json()), fun((DVN) -> json_schema(DVP)) ) -> json_schema(DVP). field_with_default(Name, Schema, Default_value, Encode, Next) -> Rest = Next(glon_ffi:coerce_nil()), Rest_fields = get_object_fields(erlang:element(2, Rest)), Node = {object_node, [{object_field, Name, {default_node, erlang:element(2, Schema), Encode(Default_value)}, false} | Rest_fields]}, Decoder = begin gleam@dynamic@decode:optional_field( Name, Default_value, erlang:element(3, Schema), fun(Value) -> Result = Next(Value), erlang:element(3, Result) end ) end, {json_schema, Node, Decoder}. -file("src/glon.gleam", 825). -spec get_type_name(schema_node()) -> {ok, binary()} | {error, nil}. get_type_name(Node) -> case Node of string_node -> {ok, <<"string"/utf8>>}; integer_node -> {ok, <<"integer"/utf8>>}; number_node -> {ok, <<"number"/utf8>>}; boolean_node -> {ok, <<"boolean"/utf8>>}; {array_node, _} -> {ok, <<"array"/utf8>>}; {object_node, _} -> {ok, <<"object"/utf8>>}; {enum_node, _} -> {ok, <<"string"/utf8>>}; {const_node, _} -> {ok, <<"string"/utf8>>}; {description_node, Inner, _} -> get_type_name(Inner); {default_node, Inner@1, _} -> get_type_name(Inner@1); {nullable_node, _} -> {error, nil}; {combiner_node, _, _} -> {error, nil} end. -file("src/glon.gleam", 727). -spec node_to_pairs(schema_node()) -> list({binary(), gleam@json:json()}). node_to_pairs(Node) -> case Node of string_node -> [{<<"type"/utf8>>, gleam@json:string(<<"string"/utf8>>)}]; integer_node -> [{<<"type"/utf8>>, gleam@json:string(<<"integer"/utf8>>)}]; number_node -> [{<<"type"/utf8>>, gleam@json:string(<<"number"/utf8>>)}]; boolean_node -> [{<<"type"/utf8>>, gleam@json:string(<<"boolean"/utf8>>)}]; {array_node, Items} -> [{<<"type"/utf8>>, gleam@json:string(<<"array"/utf8>>)}, {<<"items"/utf8>>, node_to_json(Items)}]; {nullable_node, Inner} -> case get_type_name(Inner) of {ok, Type_name} -> Null_type = gleam@json:preprocessed_array( [gleam@json:string(Type_name), gleam@json:string(<<"null"/utf8>>)] ), Inner_pairs = node_to_pairs(Inner), _pipe = Inner_pairs, _pipe@1 = gleam@list:filter( _pipe, fun(Pair) -> erlang:element(1, Pair) /= <<"type"/utf8>> end ), gleam@list:prepend(_pipe@1, {<<"type"/utf8>>, Null_type}); {error, nil} -> [{<<"anyOf"/utf8>>, gleam@json:preprocessed_array( [node_to_json(Inner), gleam@json:object( [{<<"type"/utf8>>, gleam@json:string( <<"null"/utf8>> )}] )] )}] end; {object_node, Fields} -> Properties = gleam@list:map( Fields, fun(F) -> {erlang:element(2, F), node_to_json(erlang:element(3, F))} end ), Required = begin _pipe@2 = Fields, _pipe@3 = gleam@list:filter( _pipe@2, fun(F@1) -> erlang:element(4, F@1) end ), gleam@list:map( _pipe@3, fun(F@2) -> gleam@json:string(erlang:element(2, F@2)) end ) end, case Required of [] -> [{<<"type"/utf8>>, gleam@json:string(<<"object"/utf8>>)}, {<<"properties"/utf8>>, gleam@json:object(Properties)}]; _ -> [{<<"type"/utf8>>, gleam@json:string(<<"object"/utf8>>)}, {<<"properties"/utf8>>, gleam@json:object(Properties)}, {<<"required"/utf8>>, gleam@json:preprocessed_array(Required)}] end; {enum_node, Values} -> [{<<"type"/utf8>>, gleam@json:string(<<"string"/utf8>>)}, {<<"enum"/utf8>>, gleam@json:preprocessed_array( gleam@list:map(Values, fun gleam@json:string/1) )}]; {const_node, Value} -> [{<<"type"/utf8>>, gleam@json:string(<<"string"/utf8>>)}, {<<"const"/utf8>>, gleam@json:string(Value)}]; {description_node, Inner@1, Description} -> lists:append( node_to_pairs(Inner@1), [{<<"description"/utf8>>, gleam@json:string(Description)}] ); {default_node, Inner@2, Default} -> lists:append( node_to_pairs(Inner@2), [{<<"default"/utf8>>, Default}] ); {combiner_node, Keyword, Variants} -> Keyword_str = case Keyword of one_of -> <<"oneOf"/utf8>>; any_of -> <<"anyOf"/utf8>> end, [{Keyword_str, gleam@json:preprocessed_array( gleam@list:map(Variants, fun node_to_json/1) )}] end. -file("src/glon.gleam", 819). -spec node_to_json(schema_node()) -> gleam@json:json(). node_to_json(Node) -> _pipe = Node, _pipe@1 = node_to_pairs(_pipe), gleam@json:object(_pipe@1). -file("src/glon.gleam", 676). ?DOC( " Convert a schema to a `json.Json` value.\n" "\n" " Returns the JSON Schema representation as a `json.Json` value that can\n" " be further processed or serialized.\n" ). -spec to_json(json_schema(any())) -> gleam@json:json(). to_json(Schema) -> node_to_json(erlang:element(2, Schema)). -file("src/glon.gleam", 690). ?DOC( " Convert a schema to a JSON string.\n" "\n" " Returns the JSON Schema as a serialized JSON string.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " glon.string() |> glon.to_string\n" " // -> \"{\\\"type\\\":\\\"string\\\"}\"\n" " ```\n" ). -spec to_string(json_schema(any())) -> binary(). to_string(Schema) -> _pipe = Schema, _pipe@1 = to_json(_pipe), gleam@json:to_string(_pipe@1).