Packages

A Simple JSON Schema library for Gleam

Current section

Files

Jump to
jscheam src jscheam@schema.erl
Raw

src/jscheam@schema.erl

-module(jscheam@schema).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/jscheam/schema.gleam").
-export([string/0, integer/0, boolean/0, float/0, null/0, array/1, union/1, object/1, allow_additional_props/1, disallow_additional_props/1, constrain_additional_props/2, prop/2, optional/1, description/2, enum/2, pattern/2, to_json/1]).
-export_type([constraint/0, type/0, property/0, additional_properties/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 constraint() :: {enum, list(gleam@json:json())} | {pattern, binary()}.
-type type() :: integer |
string |
boolean |
float |
null |
{object, list(property()), additional_properties()} |
{array, type()} |
{union, list(type())}.
-type property() :: {property,
binary(),
type(),
boolean(),
gleam@option:option(binary()),
list(constraint())}.
-type additional_properties() :: allow_any |
allow_explicit |
disallow |
{schema, type()}.
-file("src/jscheam/schema.gleam", 30).
?DOC(" Creates a string type for JSON Schema\n").
-spec string() -> type().
string() ->
string.
-file("src/jscheam/schema.gleam", 35).
?DOC(" Creates an integer/number type for JSON Schema\n").
-spec integer() -> type().
integer() ->
integer.
-file("src/jscheam/schema.gleam", 40).
?DOC(" Creates a boolean type for JSON Schema\n").
-spec boolean() -> type().
boolean() ->
boolean.
-file("src/jscheam/schema.gleam", 45).
?DOC(" Creates a float/number type for JSON Schema\n").
-spec float() -> type().
float() ->
float.
-file("src/jscheam/schema.gleam", 50).
?DOC(" Creates a null type for JSON Schema\n").
-spec null() -> type().
null() ->
null.
-file("src/jscheam/schema.gleam", 55).
?DOC(" Creates an array type with the specified item type\n").
-spec array(type()) -> type().
array(Item_type) ->
{array, Item_type}.
-file("src/jscheam/schema.gleam", 61).
?DOC(
" Creates a union type that accepts multiple types (e.g., string or null)\n"
" Example: union([string(), null()]) creates a schema that accepts both strings and null values\n"
).
-spec union(list(type())) -> type().
union(Types) ->
{union, Types}.
-file("src/jscheam/schema.gleam", 67).
?DOC(
" Creates an object type with the specified properties\n"
" By default allows any additional properties (JSON Schema default behavior - omits the field)\n"
).
-spec object(list(property())) -> type().
object(Properties) ->
{object, Properties, allow_any}.
-file("src/jscheam/schema.gleam", 73).
?DOC(
" Update an object type to allow any additional properties\n"
" Explicitly allows any additional properties (outputs \"additionalProperties\": true)\n"
).
-spec allow_additional_props(type()) -> type().
allow_additional_props(Object_type) ->
case Object_type of
{object, Props, _} ->
{object, Props, allow_explicit};
_ ->
Object_type
end.
-file("src/jscheam/schema.gleam", 83).
?DOC(
" Update an object type to disallow additional properties\n"
" Disallows additional properties (outputs \"additionalProperties\": false)\n"
).
-spec disallow_additional_props(type()) -> type().
disallow_additional_props(Object_type) ->
case Object_type of
{object, Props, _} ->
{object, Props, disallow};
_ ->
Object_type
end.
-file("src/jscheam/schema.gleam", 94).
?DOC(
" Update an object type to constrain additional properties to a specific schema\n"
" Example: object([prop(\"name\", string())]) |> constrain_additional_props(string())\n"
" This will set \"additionalProperties\" to the specified schema type\n"
).
-spec constrain_additional_props(type(), type()) -> type().
constrain_additional_props(Object_type, Schema) ->
case Object_type of
{object, Props, _} ->
{object, Props, {schema, Schema}};
_ ->
Object_type
end.
-file("src/jscheam/schema.gleam", 130).
?DOC(
" Creates a property with the specified name and type\n"
" Properties are required by default\n"
).
-spec prop(binary(), type()) -> property().
prop(Name, Property_type) ->
{property, Name, Property_type, true, none, []}.
-file("src/jscheam/schema.gleam", 142).
?DOC(
" Makes a property optional (not required in the schema)\n"
" Example: object([prop(\"name\", string()) |> optional()])\n"
).
-spec optional(property()) -> property().
optional(Property) ->
_record = Property,
{property,
erlang:element(2, _record),
erlang:element(3, _record),
false,
erlang:element(5, _record),
erlang:element(6, _record)}.
-file("src/jscheam/schema.gleam", 148).
?DOC(
" Adds a description to a property for documentation purposes\n"
" Example: prop(\"name\", string()) |> description(\"The name of the person\")\n"
).
-spec description(property(), binary()) -> property().
description(Property, Desc) ->
_record = Property,
{property,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
{some, Desc},
erlang:element(6, _record)}.
-file("src/jscheam/schema.gleam", 154).
?DOC(
" Adds an enum constraint to a property that restricts values to a fixed set\n"
" Example: prop(\"color\", string()) |> enum(enum_strings([\"red\", \"green\", \"blue\"]))\n"
).
-spec enum(property(), list(gleam@json:json())) -> property().
enum(Property, Values) ->
New_constraint = {enum, Values},
_record = Property,
{property,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
[New_constraint | erlang:element(6, Property)]}.
-file("src/jscheam/schema.gleam", 161).
?DOC(
" Adds a pattern constraint to a property that restricts values to match a regex pattern\n"
" Example: prop(\"phone\", string()) |> pattern(\"^(\\\\([0-9]{3}\\\\))?[0-9]{3}-[0-9]{4}$\")\n"
).
-spec pattern(property(), binary()) -> property().
pattern(Property, Regex) ->
New_constraint = {pattern, Regex},
_record = Property,
{property,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
[New_constraint | erlang:element(6, Property)]}.
-file("src/jscheam/schema.gleam", 180).
-spec type_to_type_string(type()) -> binary().
type_to_type_string(Property_type) ->
case Property_type of
string ->
<<"string"/utf8>>;
integer ->
<<"number"/utf8>>;
boolean ->
<<"boolean"/utf8>>;
null ->
<<"null"/utf8>>;
float ->
<<"number"/utf8>>;
{object, _, _} ->
<<"object"/utf8>>;
{array, _} ->
<<"array"/utf8>>;
{union, _} ->
erlang:error(#{gleam_error => panic,
message => <<"Union types should not be converted to single type strings"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"jscheam/schema"/utf8>>,
function => <<"type_to_type_string"/utf8>>,
line => 190})
end.
-file("src/jscheam/schema.gleam", 283).
-spec add_single_constraint_field(
list({binary(), gleam@json:json()}),
constraint()
) -> list({binary(), gleam@json:json()}).
add_single_constraint_field(Fields, Constraint) ->
case Constraint of
{enum, Values} ->
[{<<"enum"/utf8>>, gleam@json:array(Values, fun(X) -> X end)} |
Fields];
{pattern, Regex} ->
[{<<"pattern"/utf8>>, gleam@json:string(Regex)} | Fields]
end.
-file("src/jscheam/schema.gleam", 269).
-spec add_constraint_fields(
list({binary(), gleam@json:json()}),
list(constraint())
) -> list({binary(), gleam@json:json()}).
add_constraint_fields(Base_fields, Constraints) ->
case Constraints of
[] ->
Base_fields;
[Constraint | Rest] ->
Fields_with_constraint = add_single_constraint_field(
Base_fields,
Constraint
),
add_constraint_fields(Fields_with_constraint, Rest)
end.
-file("src/jscheam/schema.gleam", 296).
-spec fields_to_required(list(property())) -> gleam@json:json().
fields_to_required(Fields) ->
Required_fields = gleam@list:filter(
Fields,
fun(Property) ->
{property, _, _, Is_required, _, _} = Property,
Is_required
end
),
Names = gleam@list:map(
Required_fields,
fun(Property@1) ->
{property, Name, _, _, _, _} = Property@1,
gleam@json:string(Name)
end
),
gleam@json:array(Names, fun(X) -> X end).
-file("src/jscheam/schema.gleam", 194).
-spec type_to_json_value(type()) -> gleam@json:json().
type_to_json_value(Property_type) ->
case Property_type of
string ->
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))}]
);
integer ->
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))}]
);
boolean ->
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))}]
);
null ->
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))}]
);
float ->
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))}]
);
{object, Props, Add_props} ->
Properties_json = begin
_pipe = gleam@list:map(Props, fun property_to_field/1),
gleam@json:object(_pipe)
end,
Required_json = fields_to_required(Props),
Additional_props_fields = additional_properties_to_json(Add_props),
Base_fields = [{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))},
{<<"properties"/utf8>>, Properties_json},
{<<"required"/utf8>>, Required_json}],
gleam@json:object(
lists:append(Base_fields, Additional_props_fields)
);
{array, Item_type} ->
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))},
{<<"items"/utf8>>, type_to_json_value(Item_type)}]
);
{union, Types} ->
Type_strings = gleam@list:map(Types, fun type_to_type_string/1),
gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:array(Type_strings, fun gleam@json:string/1)}]
)
end.
-file("src/jscheam/schema.gleam", 166).
-spec additional_properties_to_json(additional_properties()) -> list({binary(),
gleam@json:json()}).
additional_properties_to_json(Add_props) ->
case Add_props of
allow_any ->
[];
allow_explicit ->
[{<<"additionalProperties"/utf8>>, gleam@json:bool(true)}];
disallow ->
[{<<"additionalProperties"/utf8>>, gleam@json:bool(false)}];
{schema, Schema_type} ->
[{<<"additionalProperties"/utf8>>, type_to_json_value(Schema_type)}]
end.
-file("src/jscheam/schema.gleam", 326).
?DOC(
" Converts a Type to a JSON Schema document\n"
" This is the main function to generate JSON Schema from your type definitions\n"
" Example: object([prop(\"name\", string()), prop(\"age\", integer())]) |> to_json()\n"
).
-spec to_json(type()) -> gleam@json:json().
to_json(Object_type) ->
type_to_json_value(Object_type).
-file("src/jscheam/schema.gleam", 240).
-spec get_base_type_fields(type()) -> list({binary(), gleam@json:json()}).
get_base_type_fields(Property_type) ->
case Property_type of
string ->
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))}];
integer ->
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))}];
boolean ->
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))}];
null ->
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))}];
float ->
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))}];
{array, Item_type} ->
[{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))},
{<<"items"/utf8>>, type_to_json_value(Item_type)}];
{object, Props, Add_props} ->
Properties_json = begin
_pipe = gleam@list:map(Props, fun property_to_field/1),
gleam@json:object(_pipe)
end,
Required_json = fields_to_required(Props),
Additional_props_fields = additional_properties_to_json(Add_props),
Base_fields = [{<<"type"/utf8>>,
gleam@json:string(type_to_type_string(Property_type))},
{<<"properties"/utf8>>, Properties_json},
{<<"required"/utf8>>, Required_json}],
lists:append(Base_fields, Additional_props_fields);
{union, Types} ->
Type_strings = gleam@list:map(Types, fun type_to_type_string/1),
[{<<"type"/utf8>>,
gleam@json:array(Type_strings, fun gleam@json:string/1)}]
end.
-file("src/jscheam/schema.gleam", 223).
-spec property_to_field(property()) -> {binary(), gleam@json:json()}.
property_to_field(Property) ->
{property, Name, Property_type, _, Description, Constraints} = Property,
Base_fields = get_base_type_fields(Property_type),
Fields_with_constraints = add_constraint_fields(Base_fields, Constraints),
Final_fields = case Description of
{some, Desc} ->
[{<<"description"/utf8>>, gleam@json:string(Desc)} |
Fields_with_constraints];
none ->
Fields_with_constraints
end,
{Name, gleam@json:object(Final_fields)}.