Current section

Files

Jump to
caffeine_lang src caffeine_lang@frontend@validator.erl
Raw

src/caffeine_lang@frontend@validator.erl

-module(caffeine_lang@frontend@validator).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/frontend/validator.gleam").
-export([validate_expects_file/1, validate_blueprints_file/1]).
-export_type([validator_error/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 validator_error() :: {duplicate_extendable, binary()} |
{undefined_extendable, binary(), binary()} |
{duplicate_extends_reference, binary(), binary()} |
{invalid_extendable_kind, binary(), binary(), binary()} |
{undefined_type_alias, binary(), binary()} |
{duplicate_type_alias, binary()} |
{circular_type_alias, binary(), list(binary())} |
{invalid_dict_key_type_alias, binary(), binary(), binary()}.
-file("src/caffeine_lang/frontend/validator.gleam", 105).
-spec validate_no_duplicate_extendables_loop(
list(caffeine_lang@frontend@ast:extendable()),
gleam@set:set(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_no_duplicate_extendables_loop(Extendables, Seen) ->
case Extendables of
[] ->
{ok, nil};
[First | Rest] ->
case gleam@set:contains(Seen, erlang:element(2, First)) of
true ->
{error, {duplicate_extendable, erlang:element(2, First)}};
false ->
validate_no_duplicate_extendables_loop(
Rest,
gleam@set:insert(Seen, erlang:element(2, First))
)
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 99).
?DOC(" Validates that no two extendables have the same name.\n").
-spec validate_no_duplicate_extendables(
list(caffeine_lang@frontend@ast:extendable())
) -> {ok, nil} | {error, validator_error()}.
validate_no_duplicate_extendables(Extendables) ->
validate_no_duplicate_extendables_loop(Extendables, gleam@set:new()).
-file("src/caffeine_lang/frontend/validator.gleam", 125).
?DOC(" Validates that all extendables in an expects file are Provides kind.\n").
-spec validate_extendables_are_provides(
list(caffeine_lang@frontend@ast:extendable())
) -> {ok, nil} | {error, validator_error()}.
validate_extendables_are_provides(Extendables) ->
case Extendables of
[] ->
{ok, nil};
[First | Rest] ->
case erlang:element(3, First) of
extendable_provides ->
validate_extendables_are_provides(Rest);
extendable_requires ->
{error,
{invalid_extendable_kind,
erlang:element(2, First),
<<"Provides"/utf8>>,
<<"Requires"/utf8>>}}
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 185).
?DOC(" Validates that all names in extends list exist as extendables.\n").
-spec validate_extends_exist(list(binary()), binary(), gleam@set:set(binary())) -> {ok,
nil} |
{error, validator_error()}.
validate_extends_exist(Extends, Item_name, Extendable_names) ->
case Extends of
[] ->
{ok, nil};
[First | Rest] ->
case gleam@set:contains(Extendable_names, First) of
true ->
validate_extends_exist(Rest, Item_name, Extendable_names);
false ->
{error, {undefined_extendable, First, Item_name}}
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 210).
-spec validate_no_duplicate_extends_loop(
list(binary()),
binary(),
gleam@set:set(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_no_duplicate_extends_loop(Extends, Item_name, Seen) ->
case Extends of
[] ->
{ok, nil};
[First | Rest] ->
case gleam@set:contains(Seen, First) of
true ->
{error, {duplicate_extends_reference, First, Item_name}};
false ->
validate_no_duplicate_extends_loop(
Rest,
Item_name,
gleam@set:insert(Seen, First)
)
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 203).
?DOC(" Validates that no extendable is referenced twice in the same extends list.\n").
-spec validate_no_duplicate_extends(list(binary()), binary()) -> {ok, nil} |
{error, validator_error()}.
validate_no_duplicate_extends(Extends, Item_name) ->
validate_no_duplicate_extends_loop(Extends, Item_name, gleam@set:new()).
-file("src/caffeine_lang/frontend/validator.gleam", 145).
?DOC(" Validates extends references for blueprint items.\n").
-spec validate_blueprint_items_extends(
list(caffeine_lang@frontend@ast:blueprint_item()),
list(caffeine_lang@frontend@ast:extendable())
) -> {ok, nil} | {error, validator_error()}.
validate_blueprint_items_extends(Items, Extendables) ->
Extendable_names = begin
_pipe = Extendables,
_pipe@1 = gleam@list:map(_pipe, fun(E) -> erlang:element(2, E) end),
gleam@set:from_list(_pipe@1)
end,
gleam@list:try_each(
Items,
fun(Item) ->
gleam@result:'try'(
validate_extends_exist(
erlang:element(3, Item),
erlang:element(2, Item),
Extendable_names
),
fun(_) ->
validate_no_duplicate_extends(
erlang:element(3, Item),
erlang:element(2, Item)
)
end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 165).
?DOC(" Validates extends references for expect items.\n").
-spec validate_expect_items_extends(
list(caffeine_lang@frontend@ast:expect_item()),
list(caffeine_lang@frontend@ast:extendable())
) -> {ok, nil} | {error, validator_error()}.
validate_expect_items_extends(Items, Extendables) ->
Extendable_names = begin
_pipe = Extendables,
_pipe@1 = gleam@list:map(_pipe, fun(E) -> erlang:element(2, E) end),
gleam@set:from_list(_pipe@1)
end,
gleam@list:try_each(
Items,
fun(Item) ->
gleam@result:'try'(
validate_extends_exist(
erlang:element(3, Item),
erlang:element(2, Item),
Extendable_names
),
fun(_) ->
validate_no_duplicate_extends(
erlang:element(3, Item),
erlang:element(2, Item)
)
end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 83).
?DOC(false).
-spec validate_expects_file(caffeine_lang@frontend@ast:expects_file()) -> {ok,
caffeine_lang@frontend@ast:expects_file()} |
{error, validator_error()}.
validate_expects_file(File) ->
Extendables = erlang:element(2, File),
Items = begin
_pipe = erlang:element(3, File),
gleam@list:flat_map(_pipe, fun(Block) -> erlang:element(3, Block) end)
end,
gleam@result:'try'(
validate_no_duplicate_extendables(Extendables),
fun(_) ->
gleam@result:'try'(
validate_extendables_are_provides(Extendables),
fun(_) ->
gleam@result:'try'(
validate_expect_items_extends(Items, Extendables),
fun(_) -> {ok, File} end
)
end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 237).
?DOC(" Builds a map of type alias name to its type for validation.\n").
-spec build_type_alias_map(list(caffeine_lang@frontend@ast:type_alias())) -> list({binary(),
caffeine_lang@common@accepted_types:accepted_types()}).
build_type_alias_map(Type_aliases) ->
gleam@list:map(
Type_aliases,
fun(Ta) -> {erlang:element(2, Ta), erlang:element(3, Ta)} end
).
-file("src/caffeine_lang/frontend/validator.gleam", 244).
?DOC(" Looks up a type alias by name in the map.\n").
-spec lookup_type_alias(
binary(),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()})
) -> {ok, caffeine_lang@common@accepted_types:accepted_types()} | {error, nil}.
lookup_type_alias(Name, Type_alias_map) ->
case Type_alias_map of
[] ->
{error, nil};
[{N, T} | Rest] ->
case N =:= Name of
true ->
{ok, T};
false ->
lookup_type_alias(Name, Rest)
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 265).
-spec validate_no_duplicate_type_aliases_loop(
list(caffeine_lang@frontend@ast:type_alias()),
gleam@set:set(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_no_duplicate_type_aliases_loop(Type_aliases, Seen) ->
case Type_aliases of
[] ->
{ok, nil};
[First | Rest] ->
case gleam@set:contains(Seen, erlang:element(2, First)) of
true ->
{error, {duplicate_type_alias, erlang:element(2, First)}};
false ->
validate_no_duplicate_type_aliases_loop(
Rest,
gleam@set:insert(Seen, erlang:element(2, First))
)
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 259).
?DOC(" Validates that no two type aliases have the same name.\n").
-spec validate_no_duplicate_type_aliases(
list(caffeine_lang@frontend@ast:type_alias())
) -> {ok, nil} | {error, validator_error()}.
validate_no_duplicate_type_aliases(Type_aliases) ->
validate_no_duplicate_type_aliases_loop(Type_aliases, gleam@set:new()).
-file("src/caffeine_lang/frontend/validator.gleam", 628).
?DOC(" Checks if a type is String-based (String primitive or String refinement).\n").
-spec is_string_based_type(caffeine_lang@common@accepted_types:accepted_types()) -> boolean().
is_string_based_type(Typ) ->
case Typ of
{primitive_type, string} ->
true;
{refinement_type, Refinement} ->
case Refinement of
{one_of, Inner, _} ->
case Inner of
{primitive_type, string} ->
true;
_ ->
false
end;
{inclusive_range, _, _, _} ->
false
end;
_ ->
false
end.
-file("src/caffeine_lang/frontend/validator.gleam", 572).
?DOC(" Validates that a Dict key type resolves to a String-based type.\n").
-spec validate_dict_key_type(
caffeine_lang@common@accepted_types:accepted_types(),
binary(),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()})
) -> {ok, nil} | {error, validator_error()}.
validate_dict_key_type(Key_type, Context_name, Type_alias_map) ->
case Key_type of
{primitive_type, string} ->
{ok, nil};
{type_alias_ref, Alias_name} ->
case lookup_type_alias(Alias_name, Type_alias_map) of
{ok, Resolved} ->
case is_string_based_type(Resolved) of
true ->
{ok, nil};
false ->
{error,
{invalid_dict_key_type_alias,
Alias_name,
caffeine_lang@common@accepted_types:accepted_type_to_string(
Resolved
),
Context_name}}
end;
{error, _} ->
{ok, nil}
end;
{refinement_type, Refinement} ->
case Refinement of
{one_of, Inner, _} ->
case Inner of
{primitive_type, string} ->
{ok, nil};
_ ->
{error,
{invalid_dict_key_type_alias,
<<"inline"/utf8>>,
caffeine_lang@common@accepted_types:accepted_type_to_string(
Key_type
),
Context_name}}
end;
{inclusive_range, _, _, _} ->
{error,
{invalid_dict_key_type_alias,
<<"inline"/utf8>>,
caffeine_lang@common@accepted_types:accepted_type_to_string(
Key_type
),
Context_name}}
end;
_ ->
{error,
{invalid_dict_key_type_alias,
<<"inline"/utf8>>,
caffeine_lang@common@accepted_types:accepted_type_to_string(
Key_type
),
Context_name}}
end.
-file("src/caffeine_lang/frontend/validator.gleam", 514).
-spec validate_collection_type_refs(
caffeine_lang@common@collection_types:collection_types(caffeine_lang@common@accepted_types:accepted_types()),
binary(),
gleam@set:set(binary()),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()})
) -> {ok, nil} | {error, validator_error()}.
validate_collection_type_refs(
Collection,
Context_name,
Type_alias_names,
Type_alias_map
) ->
case Collection of
{list, Inner} ->
validate_type_refs(
Inner,
Context_name,
Type_alias_names,
Type_alias_map
);
{dict, Key, Value} ->
gleam@result:'try'(
validate_type_refs(
Key,
Context_name,
Type_alias_names,
Type_alias_map
),
fun(_) ->
gleam@result:'try'(
validate_dict_key_type(
Key,
Context_name,
Type_alias_map
),
fun(_) ->
validate_type_refs(
Value,
Context_name,
Type_alias_names,
Type_alias_map
)
end
)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 475).
?DOC(" Validates that all TypeAliasRef in a type are defined.\n").
-spec validate_type_refs(
caffeine_lang@common@accepted_types:accepted_types(),
binary(),
gleam@set:set(binary()),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()})
) -> {ok, nil} | {error, validator_error()}.
validate_type_refs(Typ, Context_name, Type_alias_names, Type_alias_map) ->
case Typ of
{primitive_type, _} ->
{ok, nil};
{type_alias_ref, Name} ->
case gleam@set:contains(Type_alias_names, Name) of
true ->
{ok, nil};
false ->
{error, {undefined_type_alias, Name, Context_name}}
end;
{collection_type, Collection} ->
validate_collection_type_refs(
Collection,
Context_name,
Type_alias_names,
Type_alias_map
);
{modifier_type, Modifier} ->
validate_modifier_type_refs(
Modifier,
Context_name,
Type_alias_names,
Type_alias_map
);
{refinement_type, Refinement} ->
validate_refinement_type_refs(
Refinement,
Context_name,
Type_alias_names,
Type_alias_map
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 459).
?DOC(" Validates type alias references in a list of fields.\n").
-spec validate_fields_type_refs(
list(caffeine_lang@frontend@ast:field()),
binary(),
gleam@set:set(binary()),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()})
) -> {ok, nil} | {error, validator_error()}.
validate_fields_type_refs(
Fields,
Context_name,
Type_alias_names,
Type_alias_map
) ->
gleam@list:try_each(Fields, fun(Field) -> case erlang:element(3, Field) of
{type_value, Typ} ->
validate_type_refs(
Typ,
Context_name,
Type_alias_names,
Type_alias_map
);
{literal_value, _} ->
{ok, nil}
end end).
-file("src/caffeine_lang/frontend/validator.gleam", 427).
?DOC(" Validates type alias references in extendables.\n").
-spec validate_extendables_type_refs(
list(caffeine_lang@frontend@ast:extendable()),
gleam@set:set(binary()),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()})
) -> {ok, nil} | {error, validator_error()}.
validate_extendables_type_refs(Extendables, Type_alias_names, Type_alias_map) ->
gleam@list:try_each(
Extendables,
fun(Ext) ->
validate_fields_type_refs(
erlang:element(2, erlang:element(4, Ext)),
erlang:element(2, Ext),
Type_alias_names,
Type_alias_map
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 443).
?DOC(" Validates type alias references in blueprint items.\n").
-spec validate_blueprint_items_type_refs(
list(caffeine_lang@frontend@ast:blueprint_item()),
gleam@set:set(binary()),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()})
) -> {ok, nil} | {error, validator_error()}.
validate_blueprint_items_type_refs(Items, Type_alias_names, Type_alias_map) ->
gleam@list:try_each(
Items,
fun(Item) ->
validate_fields_type_refs(
erlang:element(2, erlang:element(4, Item)),
erlang:element(2, Item),
Type_alias_names,
Type_alias_map
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 543).
-spec validate_modifier_type_refs(
caffeine_lang@common@modifier_types:modifier_types(caffeine_lang@common@accepted_types:accepted_types()),
binary(),
gleam@set:set(binary()),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()})
) -> {ok, nil} | {error, validator_error()}.
validate_modifier_type_refs(
Modifier,
Context_name,
Type_alias_names,
Type_alias_map
) ->
case Modifier of
{optional, Inner} ->
validate_type_refs(
Inner,
Context_name,
Type_alias_names,
Type_alias_map
);
{defaulted, Inner@1, _} ->
validate_type_refs(
Inner@1,
Context_name,
Type_alias_names,
Type_alias_map
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 557).
-spec validate_refinement_type_refs(
caffeine_lang@common@refinement_types:refinement_types(caffeine_lang@common@accepted_types:accepted_types()),
binary(),
gleam@set:set(binary()),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()})
) -> {ok, nil} | {error, validator_error()}.
validate_refinement_type_refs(
Refinement,
Context_name,
Type_alias_names,
Type_alias_map
) ->
case Refinement of
{one_of, Inner, _} ->
validate_type_refs(
Inner,
Context_name,
Type_alias_names,
Type_alias_map
);
{inclusive_range, Inner@1, _, _} ->
validate_type_refs(
Inner@1,
Context_name,
Type_alias_names,
Type_alias_map
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 347).
-spec validate_collection_not_circular(
binary(),
caffeine_lang@common@collection_types:collection_types(caffeine_lang@common@accepted_types:accepted_types()),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()}),
list(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_collection_not_circular(
Original_name,
Collection,
Type_alias_map,
Visited
) ->
case Collection of
{list, Inner} ->
validate_type_alias_not_circular(
Original_name,
Inner,
Type_alias_map,
Visited
);
{dict, Key, Value} ->
gleam@result:'try'(
validate_type_alias_not_circular(
Original_name,
Key,
Type_alias_map,
Visited
),
fun(_) ->
validate_type_alias_not_circular(
Original_name,
Value,
Type_alias_map,
Visited
)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 297).
?DOC(" Checks if a type contains a circular reference.\n").
-spec validate_type_alias_not_circular(
binary(),
caffeine_lang@common@accepted_types:accepted_types(),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()}),
list(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_type_alias_not_circular(Original_name, Typ, Type_alias_map, Visited) ->
case Typ of
{primitive_type, _} ->
{ok, nil};
{type_alias_ref, Name} ->
case gleam@list:contains(Visited, Name) of
true ->
{error, {circular_type_alias, Original_name, Visited}};
false ->
case lookup_type_alias(Name, Type_alias_map) of
{ok, Resolved} ->
validate_type_alias_not_circular(
Original_name,
Resolved,
Type_alias_map,
[Name | Visited]
);
{error, _} ->
{ok, nil}
end
end;
{collection_type, Collection} ->
validate_collection_not_circular(
Original_name,
Collection,
Type_alias_map,
Visited
);
{modifier_type, Modifier} ->
validate_modifier_not_circular(
Original_name,
Modifier,
Type_alias_map,
Visited
);
{refinement_type, Refinement} ->
validate_refinement_not_circular(
Original_name,
Refinement,
Type_alias_map,
Visited
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 285).
?DOC(" Validates that no type alias has circular references.\n").
-spec validate_no_circular_type_aliases(
list(caffeine_lang@frontend@ast:type_alias())
) -> {ok, nil} | {error, validator_error()}.
validate_no_circular_type_aliases(Type_aliases) ->
Type_alias_map = build_type_alias_map(Type_aliases),
gleam@list:try_each(
Type_aliases,
fun(Ta) ->
validate_type_alias_not_circular(
erlang:element(2, Ta),
erlang:element(3, Ta),
Type_alias_map,
[erlang:element(2, Ta)]
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 37).
?DOC(false).
-spec validate_blueprints_file(caffeine_lang@frontend@ast:blueprints_file()) -> {ok,
caffeine_lang@frontend@ast:blueprints_file()} |
{error, validator_error()}.
validate_blueprints_file(File) ->
Type_aliases = erlang:element(2, File),
Extendables = erlang:element(3, File),
Items = begin
_pipe = erlang:element(4, File),
gleam@list:flat_map(_pipe, fun(Block) -> erlang:element(3, Block) end)
end,
gleam@result:'try'(
validate_no_duplicate_type_aliases(Type_aliases),
fun(_) ->
gleam@result:'try'(
validate_no_circular_type_aliases(Type_aliases),
fun(_) ->
Type_alias_names = begin
_pipe@1 = Type_aliases,
_pipe@2 = gleam@list:map(
_pipe@1,
fun(Ta) -> erlang:element(2, Ta) end
),
gleam@set:from_list(_pipe@2)
end,
Type_alias_map = build_type_alias_map(Type_aliases),
gleam@result:'try'(
validate_extendables_type_refs(
Extendables,
Type_alias_names,
Type_alias_map
),
fun(_) ->
gleam@result:'try'(
validate_blueprint_items_type_refs(
Items,
Type_alias_names,
Type_alias_map
),
fun(_) ->
gleam@result:'try'(
validate_no_duplicate_extendables(
Extendables
),
fun(_) ->
gleam@result:'try'(
validate_blueprint_items_extends(
Items,
Extendables
),
fun(_) -> {ok, File} end
)
end
)
end
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 378).
-spec validate_modifier_not_circular(
binary(),
caffeine_lang@common@modifier_types:modifier_types(caffeine_lang@common@accepted_types:accepted_types()),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()}),
list(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_modifier_not_circular(Original_name, Modifier, Type_alias_map, Visited) ->
case Modifier of
{optional, Inner} ->
validate_type_alias_not_circular(
Original_name,
Inner,
Type_alias_map,
Visited
);
{defaulted, Inner@1, _} ->
validate_type_alias_not_circular(
Original_name,
Inner@1,
Type_alias_map,
Visited
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 402).
-spec validate_refinement_not_circular(
binary(),
caffeine_lang@common@refinement_types:refinement_types(caffeine_lang@common@accepted_types:accepted_types()),
list({binary(), caffeine_lang@common@accepted_types:accepted_types()}),
list(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_refinement_not_circular(
Original_name,
Refinement,
Type_alias_map,
Visited
) ->
case Refinement of
{one_of, Inner, _} ->
validate_type_alias_not_circular(
Original_name,
Inner,
Type_alias_map,
Visited
);
{inclusive_range, Inner@1, _, _} ->
validate_type_alias_not_circular(
Original_name,
Inner@1,
Type_alias_map,
Visited
)
end.