Packages

A Simple JSON Schema library for Gleam

Current section

Files

Jump to
jscheam src jscheam.erl
Raw

src/jscheam.erl

-module(jscheam).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/jscheam.gleam").
-export([prop/2, optional/1, description/2, string/0, integer/0, boolean/0, float/0, null/0, array/1, union/1, enum/2, pattern/2, object/1, allow_additional_props/1, disallow_additional_props/1, constrain_additional_props/2, to_json/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.
-file("src/jscheam.gleam", 13).
?DOC(
" Creates a property with the specified name and type\n"
" Properties are required by default\n"
).
-spec prop(binary(), jscheam@property:type()) -> jscheam@property:property().
prop(Name, Property_type) ->
{property, Name, Property_type, true, none, []}.
-file("src/jscheam.gleam", 24).
?DOC(" Makes a property optional (not required in the schema)\n").
-spec optional(jscheam@property:property()) -> jscheam@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.gleam", 29).
?DOC(" Adds a description to a property for documentation purposes\n").
-spec description(jscheam@property:property(), binary()) -> jscheam@property: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.gleam", 35).
?DOC(" Creates a string type for JSON Schema\n").
-spec string() -> jscheam@property:type().
string() ->
string.
-file("src/jscheam.gleam", 40).
?DOC(" Creates an integer/number type for JSON Schema\n").
-spec integer() -> jscheam@property:type().
integer() ->
integer.
-file("src/jscheam.gleam", 45).
?DOC(" Creates a boolean type for JSON Schema\n").
-spec boolean() -> jscheam@property:type().
boolean() ->
boolean.
-file("src/jscheam.gleam", 50).
?DOC(" Creates a float/number type for JSON Schema\n").
-spec float() -> jscheam@property:type().
float() ->
float.
-file("src/jscheam.gleam", 55).
?DOC(" Creates a null type for JSON Schema\n").
-spec null() -> jscheam@property:type().
null() ->
null.
-file("src/jscheam.gleam", 60).
?DOC(" Creates an array type with the specified item type\n").
-spec array(jscheam@property:type()) -> jscheam@property:type().
array(Item_type) ->
{array, Item_type}.
-file("src/jscheam.gleam", 66).
?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(jscheam@property:type())) -> jscheam@property:type().
union(Types) ->
{union, Types}.
-file("src/jscheam.gleam", 72).
?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(jscheam@property:property(), list(gleam@json:json())) -> jscheam@property: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.gleam", 79).
?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(jscheam@property:property(), binary()) -> jscheam@property: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.gleam", 86).
?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(jscheam@property:property())) -> jscheam@property:type().
object(Properties) ->
{object, Properties, allow_any}.
-file("src/jscheam.gleam", 91).
?DOC(" Explicitly allows any additional properties (outputs \"additionalProperties\": true)\n").
-spec allow_additional_props(jscheam@property:type()) -> jscheam@property:type().
allow_additional_props(Object_type) ->
case Object_type of
{object, Props, _} ->
{object, Props, allow_explicit};
_ ->
Object_type
end.
-file("src/jscheam.gleam", 100).
?DOC(" Disallows additional properties (outputs \"additionalProperties\": false)\n").
-spec disallow_additional_props(jscheam@property:type()) -> jscheam@property:type().
disallow_additional_props(Object_type) ->
case Object_type of
{object, Props, _} ->
{object, Props, disallow};
_ ->
Object_type
end.
-file("src/jscheam.gleam", 109).
?DOC(" Constrains additional properties to conform to the specified schema\n").
-spec constrain_additional_props(
jscheam@property:type(),
jscheam@property:type()
) -> jscheam@property:type().
constrain_additional_props(Object_type, Schema) ->
case Object_type of
{object, Props, _} ->
{object, Props, {schema, Schema}};
_ ->
Object_type
end.
-file("src/jscheam.gleam", 131).
-spec type_to_type_string(jscheam@property: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"/utf8>>,
function => <<"type_to_type_string"/utf8>>,
line => 141})
end.
-file("src/jscheam.gleam", 234).
-spec add_single_constraint_field(
list({binary(), gleam@json:json()}),
jscheam@property: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.gleam", 220).
-spec add_constraint_fields(
list({binary(), gleam@json:json()}),
list(jscheam@property: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.gleam", 247).
-spec fields_to_required(list(jscheam@property: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.gleam", 145).
-spec type_to_json_value(jscheam@property: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.gleam", 117).
-spec additional_properties_to_json(jscheam@property: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.gleam", 276).
?DOC(
" Converts a Type to a JSON Schema document\n"
" This is the main function to generate JSON Schema from your type definitions\n"
).
-spec to_json(jscheam@property:type()) -> gleam@json:json().
to_json(Object_type) ->
type_to_json_value(Object_type).
-file("src/jscheam.gleam", 191).
-spec get_base_type_fields(jscheam@property: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.gleam", 174).
-spec property_to_field(jscheam@property: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)}.