Packages
caffeine_lang
6.2.2
6.3.1
6.3.0
6.2.2
6.2.1
6.2.0
6.1.2
6.1.1
6.1.0
6.0.0
5.6.0
5.5.0
5.4.4
5.4.3
5.4.2
5.4.1
5.4.0
5.3.0
5.2.0
5.1.1
5.1.0
5.0.12
5.0.11
5.0.10
5.0.8
5.0.7
5.0.6
5.0.5
5.0.4
5.0.1
5.0.0
4.10.0
4.9.0
4.8.3
4.8.2
4.8.1
4.8.0
4.7.9
4.7.8
4.7.7
4.7.6
4.7.5
4.6.7
4.6.6
4.6.5
4.6.4
4.6.3
4.6.2
4.6.0
4.5.1
4.5.0
4.4.4
4.4.3
4.4.1
4.4.0
4.3.7
4.3.6
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.0.2
1.0.1
0.1.0
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
0.0.5
0.0.4
0.0.2
0.0.1
A compiler for generating reliability artifacts from service expectation definitions.
Current section
Files
Jump to
Current section
Files
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([error_to_string/1, validate_measurements_file/1, validate_expects_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(), list(binary())} |
{duplicate_extends_reference, binary(), binary()} |
{invalid_extendable_kind, binary(), binary(), binary()} |
{undefined_type_alias, binary(), binary(), list(binary())} |
{duplicate_type_alias, binary()} |
{circular_type_alias, binary(), list(binary())} |
{invalid_dict_key_type_alias, binary(), binary(), binary()} |
{extendable_overshadowing, binary(), binary(), binary()} |
{extendable_type_alias_name_collision, binary()} |
{invalid_refinement_value, binary(), binary(), binary()} |
{invalid_percentage_bounds, binary(), binary()}.
-file("src/caffeine_lang/frontend/validator.gleam", 62).
?DOC(false).
-spec error_to_string(validator_error()) -> binary().
error_to_string(Err) ->
case Err of
{duplicate_extendable, Name} ->
<<"Duplicate extendable: "/utf8, Name/binary>>;
{undefined_extendable, Name@1, Referenced_by, _} ->
<<<<<<<<"Undefined extendable '"/utf8, Name@1/binary>>/binary,
"' referenced by '"/utf8>>/binary,
Referenced_by/binary>>/binary,
"'"/utf8>>;
{duplicate_extends_reference, Name@2, Referenced_by@1} ->
<<<<<<<<"Duplicate extends reference '"/utf8, Name@2/binary>>/binary,
"' in '"/utf8>>/binary,
Referenced_by@1/binary>>/binary,
"'"/utf8>>;
{invalid_extendable_kind, Name@3, Expected, Got} ->
<<<<<<<<<<"Invalid extendable kind for '"/utf8, Name@3/binary>>/binary,
"': expected "/utf8>>/binary,
Expected/binary>>/binary,
", got "/utf8>>/binary,
Got/binary>>;
{undefined_type_alias, Name@4, Referenced_by@2, _} ->
<<<<<<<<"Undefined type alias '"/utf8, Name@4/binary>>/binary,
"' referenced by '"/utf8>>/binary,
Referenced_by@2/binary>>/binary,
"'"/utf8>>;
{duplicate_type_alias, Name@5} ->
<<"Duplicate type alias: "/utf8, Name@5/binary>>;
{circular_type_alias, Name@6, _} ->
<<<<"Circular type alias reference detected in '"/utf8,
Name@6/binary>>/binary,
"'"/utf8>>;
{invalid_dict_key_type_alias, Alias_name, Resolved_to, Referenced_by@3} ->
<<<<<<<<<<<<"Type alias '"/utf8, Alias_name/binary>>/binary,
"' used as Dict key resolves to '"/utf8>>/binary,
Resolved_to/binary>>/binary,
"' which is not String-based, in '"/utf8>>/binary,
Referenced_by@3/binary>>/binary,
"'"/utf8>>;
{extendable_overshadowing, Field_name, Item_name, Extendable_name} ->
<<<<<<<<<<<<"Field '"/utf8, Field_name/binary>>/binary,
"' in '"/utf8>>/binary,
Item_name/binary>>/binary,
"' overshadows field from extendable '"/utf8>>/binary,
Extendable_name/binary>>/binary,
"'"/utf8>>;
{extendable_type_alias_name_collision, Name@7} ->
<<<<"Name '"/utf8, Name@7/binary>>/binary,
"' is used as both an extendable and a type alias"/utf8>>;
{invalid_refinement_value, Value, Expected_type, Referenced_by@4} ->
<<<<<<<<<<<<"Refinement value '"/utf8, Value/binary>>/binary,
"' is not a valid "/utf8>>/binary,
Expected_type/binary>>/binary,
" literal, in '"/utf8>>/binary,
Referenced_by@4/binary>>/binary,
"'"/utf8>>;
{invalid_percentage_bounds, Value@1, Referenced_by@5} ->
<<<<<<<<"Percentage value '"/utf8, Value@1/binary>>/binary,
"' must be between 0.0 and 100.0, in '"/utf8>>/binary,
Referenced_by@5/binary>>/binary,
"'"/utf8>>
end.
-file("src/caffeine_lang/frontend/validator.gleam", 834).
?DOC(
" Guards against accumulated errors. If errors is non-empty, returns them;\n"
" otherwise continues with the provided callback.\n"
).
-spec guard_errors(
list(validator_error()),
fun(() -> {ok, NJR} | {error, list(validator_error())})
) -> {ok, NJR} | {error, list(validator_error())}.
guard_errors(Errors, Otherwise) ->
case Errors of
[] ->
Otherwise();
_ ->
{error, Errors}
end.
-file("src/caffeine_lang/frontend/validator.gleam", 818).
?DOC(" Converts a single-error Result to a list of errors (empty on Ok).\n").
-spec errors_to_list({ok, nil} | {error, validator_error()}) -> list(validator_error()).
errors_to_list(Result) ->
case Result of
{ok, _} ->
[];
{error, Err} ->
[Err]
end.
-file("src/caffeine_lang/frontend/validator.gleam", 432).
?DOC(" Validates that an item's field sets don't overshadow fields from its extended extendables.\n").
-spec validate_no_overshadowing(
binary(),
list(binary()),
list(gleam@set:set(binary())),
gleam@dict:dict(binary(), gleam@set:set(binary()))
) -> {ok, nil} | {error, validator_error()}.
validate_no_overshadowing(Item_name, Extends, Field_sets, Extendable_map) ->
gleam@list:try_each(
Extends,
fun(Ext_name) -> case gleam_stdlib:map_get(Extendable_map, Ext_name) of
{error, _} ->
{ok, nil};
{ok, Ext_fields} ->
gleam@list:try_each(
Field_sets,
fun(Item_fields) ->
case begin
_pipe = gleam@set:intersection(
Item_fields,
Ext_fields
),
gleam@set:to_list(_pipe)
end of
[] ->
{ok, nil};
[Field | _] ->
{error,
{extendable_overshadowing,
Field,
Item_name,
Ext_name}}
end
end
)
end end
).
-file("src/caffeine_lang/frontend/validator.gleam", 392).
-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] ->
gleam@bool:guard(
gleam@set:contains(Seen, First),
{error, {duplicate_extends_reference, First, Item_name}},
fun() ->
validate_no_duplicate_extends_loop(
Rest,
Item_name,
gleam@set:insert(Seen, First)
)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 385).
?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", 363).
?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] ->
gleam@bool:guard(
not gleam@set:contains(Extendable_names, First),
{error,
{undefined_extendable,
First,
Item_name,
gleam@set:to_list(Extendable_names)}},
fun() ->
validate_extends_exist(Rest, Item_name, Extendable_names)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 417).
?DOC(" Builds a map of extendable name to its field names for overshadowing checks.\n").
-spec build_extendable_field_map(list(caffeine_lang@frontend@ast:extendable())) -> gleam@dict:dict(binary(), gleam@set:set(binary())).
build_extendable_field_map(Extendables) ->
_pipe = Extendables,
_pipe@3 = gleam@list:map(
_pipe,
fun(E) ->
Field_names = begin
_pipe@1 = erlang:element(2, erlang:element(4, E)),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(F) -> erlang:element(2, F) end
),
gleam@set:from_list(_pipe@2)
end,
{erlang:element(2, E), Field_names}
end
),
maps:from_list(_pipe@3).
-file("src/caffeine_lang/frontend/validator.gleam", 295).
?DOC(" Validates extends references for measurement items.\n").
-spec validate_measurement_items_extends(
list(caffeine_lang@frontend@ast:measurement_item()),
list(caffeine_lang@frontend@ast:extendable())
) -> {ok, nil} | {error, validator_error()}.
validate_measurement_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,
Extendable_map = build_extendable_field_map(Extendables),
gleam@list:try_each(
Items,
fun(Item) ->
gleam@result:'try'(
validate_extends_exist(
erlang:element(4, Item),
erlang:element(2, Item),
Extendable_names
),
fun(_) ->
gleam@result:'try'(
validate_no_duplicate_extends(
erlang:element(4, Item),
erlang:element(2, Item)
),
fun(_) ->
Requires_fields = begin
_pipe@2 = erlang:element(
2,
erlang:element(5, Item)
),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(F) -> erlang:element(2, F) end
),
gleam@set:from_list(_pipe@3)
end,
Provides_fields = begin
_pipe@4 = erlang:element(
2,
erlang:element(6, Item)
),
_pipe@5 = gleam@list:map(
_pipe@4,
fun(F@1) -> erlang:element(2, F@1) end
),
gleam@set:from_list(_pipe@5)
end,
validate_no_overshadowing(
erlang:element(2, Item),
erlang:element(4, Item),
[Requires_fields, Provides_fields],
Extendable_map
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 750).
?DOC(" Validates that a string value is valid for the given primitive type.\n").
-spec validate_string_matches_primitive(
binary(),
caffeine_lang@types:primitive_types(),
binary()
) -> {ok, nil} | {error, validator_error()}.
validate_string_matches_primitive(Value, Primitive, Context_name) ->
Is_valid = case Primitive of
string ->
true;
{semantic_type, _} ->
true;
boolean ->
(Value =:= <<"true"/utf8>>) orelse (Value =:= <<"false"/utf8>>);
{numeric_type, integer} ->
gleam@result:is_ok(gleam_stdlib:parse_int(Value));
{numeric_type, float} ->
gleam@result:is_ok(gleam_stdlib:parse_float(Value));
{numeric_type, percentage} ->
Raw = case gleam_stdlib:string_ends_with(Value, <<"%"/utf8>>) of
true ->
gleam@string:drop_end(Value, 1);
false ->
Value
end,
case gleam_stdlib:parse_float(Raw) of
{ok, F} ->
gleam@bool:guard(
(F < +0.0) orelse (F > 100.0),
(false),
fun() -> true end
);
{error, _} ->
false
end
end,
case Is_valid of
true ->
{ok, nil};
false ->
case Primitive of
{numeric_type, percentage} ->
Raw@1 = case gleam_stdlib:string_ends_with(
Value,
<<"%"/utf8>>
) of
true ->
gleam@string:drop_end(Value, 1);
false ->
Value
end,
case gleam_stdlib:parse_float(Raw@1) of
{ok, _} ->
{error,
{invalid_percentage_bounds, Value, Context_name}};
{error, _} ->
{error,
{invalid_refinement_value,
Value,
caffeine_lang@types:primitive_type_to_string(
Primitive
),
Context_name}}
end;
_ ->
{error,
{invalid_refinement_value,
Value,
caffeine_lang@types:primitive_type_to_string(
Primitive
),
Context_name}}
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 739).
?DOC(" Extracts the primitive type from a ParsedType, unwrapping Defaulted modifiers.\n").
-spec extract_primitive_from_parsed(caffeine_lang@types:parsed_type()) -> {ok,
caffeine_lang@types:primitive_types()} |
{error, nil}.
extract_primitive_from_parsed(Typ) ->
case Typ of
{parsed_primitive, Primitive} ->
{ok, Primitive};
{parsed_modifier, {defaulted, Inner, _}} ->
extract_primitive_from_parsed(Inner);
_ ->
{error, nil}
end.
-file("src/caffeine_lang/frontend/validator.gleam", 706).
?DOC(" Validates that refinement string values match the declared primitive type.\n").
-spec validate_refinement_values(
caffeine_lang@types:refinement_types(caffeine_lang@types:parsed_type()),
binary()
) -> {ok, nil} | {error, validator_error()}.
validate_refinement_values(Refinement, Context_name) ->
case Refinement of
{one_of, Inner, Values} ->
case extract_primitive_from_parsed(Inner) of
{ok, Primitive} ->
_pipe = Values,
_pipe@1 = gleam@set:to_list(_pipe),
gleam@list:try_each(
_pipe@1,
fun(Value) ->
validate_string_matches_primitive(
Value,
Primitive,
Context_name
)
end
);
{error, _} ->
{ok, nil}
end;
{inclusive_range, Inner@1, Low, High} ->
case extract_primitive_from_parsed(Inner@1) of
{ok, Primitive@1} ->
gleam@result:'try'(
validate_string_matches_primitive(
Low,
Primitive@1,
Context_name
),
fun(_) ->
validate_string_matches_primitive(
High,
Primitive@1,
Context_name
)
end
);
{error, _} ->
{ok, nil}
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 693).
?DOC(" Checks if a parsed type is String-based (String primitive or String refinement).\n").
-spec is_string_based_parsed_type(caffeine_lang@types:parsed_type()) -> boolean().
is_string_based_parsed_type(Typ) ->
case Typ of
{parsed_primitive, string} ->
true;
{parsed_refinement, {one_of, {parsed_primitive, string}, _}} ->
true;
_ ->
false
end.
-file("src/caffeine_lang/frontend/validator.gleam", 654).
?DOC(" Validates that a Dict key type resolves to a String-based type.\n").
-spec validate_dict_key_type(
caffeine_lang@types:parsed_type(),
binary(),
list({binary(), caffeine_lang@types:parsed_type()})
) -> {ok, nil} | {error, validator_error()}.
validate_dict_key_type(Key_type, Context_name, Type_alias_map) ->
case Key_type of
{parsed_primitive, string} ->
{ok, nil};
{parsed_type_alias_ref, Alias_name} ->
case gleam@list:key_find(Type_alias_map, Alias_name) of
{ok, Resolved} ->
gleam@bool:guard(
is_string_based_parsed_type(Resolved),
{ok, nil},
fun() ->
{error,
{invalid_dict_key_type_alias,
Alias_name,
caffeine_lang@types:parsed_type_to_string(
Resolved
),
Context_name}}
end
);
{error, _} ->
{ok, nil}
end;
{parsed_refinement, {one_of, {parsed_primitive, string}, _}} ->
{ok, nil};
_ ->
{error,
{invalid_dict_key_type_alias,
<<"inline"/utf8>>,
caffeine_lang@types:parsed_type_to_string(Key_type),
Context_name}}
end.
-file("src/caffeine_lang/frontend/validator.gleam", 593).
?DOC(
" Validates that all ParsedTypeAliasRef in a type are defined.\n"
" Uses try_each_inner_parsed to handle the structural decomposition of compound types.\n"
" Adds special Dict key validation and refinement value validation.\n"
).
-spec validate_type_refs(
caffeine_lang@types:parsed_type(),
binary(),
gleam@set:set(binary()),
list({binary(), caffeine_lang@types:parsed_type()})
) -> {ok, nil} | {error, validator_error()}.
validate_type_refs(Typ, Context_name, Type_alias_names, Type_alias_map) ->
case Typ of
{parsed_primitive, _} ->
{ok, nil};
{parsed_type_alias_ref, Name} ->
gleam@bool:guard(
gleam@set:contains(Type_alias_names, Name),
{ok, nil},
fun() ->
{error,
{undefined_type_alias,
Name,
Context_name,
gleam@set:to_list(Type_alias_names)}}
end
);
{parsed_collection, {dict, Key, _}} ->
gleam@result:'try'(
validate_dict_key_type(Key, Context_name, Type_alias_map),
fun(_) ->
caffeine_lang@types:try_each_inner_parsed(
Typ,
fun(Inner) ->
validate_type_refs(
Inner,
Context_name,
Type_alias_names,
Type_alias_map
)
end
)
end
);
{parsed_refinement, Refinement} ->
gleam@result:'try'(
validate_refinement_values(Refinement, Context_name),
fun(_) ->
caffeine_lang@types:try_each_inner_parsed(
Typ,
fun(Inner@1) ->
validate_type_refs(
Inner@1,
Context_name,
Type_alias_names,
Type_alias_map
)
end
)
end
);
_ ->
caffeine_lang@types:try_each_inner_parsed(
Typ,
fun(Inner@2) ->
validate_type_refs(
Inner@2,
Context_name,
Type_alias_names,
Type_alias_map
)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 575).
?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@types:parsed_type()})
) -> {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", 559).
?DOC(" Validates type alias references in measurement items.\n").
-spec validate_measurement_items_type_refs(
list(caffeine_lang@frontend@ast:measurement_item()),
gleam@set:set(binary()),
list({binary(), caffeine_lang@types:parsed_type()})
) -> {ok, nil} | {error, validator_error()}.
validate_measurement_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(5, Item)),
erlang:element(2, Item),
Type_alias_names,
Type_alias_map
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 543).
?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@types:parsed_type()})
) -> {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", 532).
?DOC(" Validates type alias definitions for type refs and refinement values.\n").
-spec validate_type_aliases_type_refs(
list(caffeine_lang@frontend@ast:type_alias()),
gleam@set:set(binary()),
list({binary(), caffeine_lang@types:parsed_type()})
) -> {ok, nil} | {error, validator_error()}.
validate_type_aliases_type_refs(Type_aliases, Type_alias_names, Type_alias_map) ->
gleam@list:try_each(
Type_aliases,
fun(Ta) ->
validate_type_refs(
erlang:element(3, Ta),
erlang:element(2, Ta),
Type_alias_names,
Type_alias_map
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 826).
?DOC(" Collects errors from a list of independent validation results.\n").
-spec collect_errors(list({ok, nil} | {error, validator_error()})) -> list(validator_error()).
collect_errors(Results) ->
_pipe = Results,
gleam@list:flat_map(_pipe, fun errors_to_list/1).
-file("src/caffeine_lang/frontend/validator.gleam", 462).
?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@types:parsed_type()}).
build_type_alias_map(Type_aliases) ->
caffeine_lang@frontend@ast:build_type_alias_pairs(Type_aliases).
-file("src/caffeine_lang/frontend/validator.gleam", 256).
?DOC(" Validates that no extendable shares a name with a type alias.\n").
-spec validate_no_extendable_type_alias_collision(
list(caffeine_lang@frontend@ast:extendable()),
list(caffeine_lang@frontend@ast:type_alias())
) -> {ok, nil} | {error, validator_error()}.
validate_no_extendable_type_alias_collision(Extendables, Type_aliases) ->
Type_alias_names = begin
_pipe = Type_aliases,
_pipe@1 = gleam@list:map(_pipe, fun(Ta) -> erlang:element(2, Ta) end),
gleam@set:from_list(_pipe@1)
end,
gleam@list:try_each(
Extendables,
fun(Ext) ->
gleam@bool:guard(
gleam@set:contains(Type_alias_names, erlang:element(2, Ext)),
{error,
{extendable_type_alias_name_collision,
erlang:element(2, Ext)}},
fun() -> {ok, nil} end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 231).
-spec validate_no_duplicates_loop(
list(NGA),
fun((NGA) -> binary()),
fun((binary()) -> validator_error()),
gleam@set:set(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_no_duplicates_loop(Items, Get_name, Make_error, Seen) ->
case Items of
[] ->
{ok, nil};
[First | Rest] ->
Name = Get_name(First),
gleam@bool:guard(
gleam@set:contains(Seen, Name),
{error, Make_error(Name)},
fun() ->
validate_no_duplicates_loop(
Rest,
Get_name,
Make_error,
gleam@set:insert(Seen, Name)
)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 223).
?DOC(" Validates that no two items in a list share the same name.\n").
-spec validate_no_duplicates(
list(NFW),
fun((NFW) -> binary()),
fun((binary()) -> validator_error())
) -> {ok, nil} | {error, validator_error()}.
validate_no_duplicates(Items, Get_name, Make_error) ->
validate_no_duplicates_loop(Items, Get_name, Make_error, gleam@set:new()).
-file("src/caffeine_lang/frontend/validator.gleam", 487).
?DOC(
" Checks if a type contains a circular reference.\n"
" Uses try_each_inner_parsed to handle the structural decomposition of compound types,\n"
" with leaf-specific logic for ParsedPrimitive and ParsedTypeAliasRef.\n"
).
-spec validate_type_alias_not_circular(
binary(),
caffeine_lang@types:parsed_type(),
list({binary(), caffeine_lang@types:parsed_type()}),
list(binary()),
gleam@set:set(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_type_alias_not_circular(
Original_name,
Typ,
Type_alias_map,
Visited_path,
Visited_set
) ->
case Typ of
{parsed_primitive, _} ->
{ok, nil};
{parsed_type_alias_ref, Name} ->
gleam@bool:guard(
gleam@set:contains(Visited_set, Name),
{error, {circular_type_alias, Original_name, Visited_path}},
fun() -> case gleam@list:key_find(Type_alias_map, Name) of
{ok, Resolved} ->
validate_type_alias_not_circular(
Original_name,
Resolved,
Type_alias_map,
[Name | Visited_path],
gleam@set:insert(Visited_set, Name)
);
{error, _} ->
{ok, nil}
end end
);
_ ->
caffeine_lang@types:try_each_inner_parsed(
Typ,
fun(Inner) ->
validate_type_alias_not_circular(
Original_name,
Inner,
Type_alias_map,
Visited_path,
Visited_set
)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 469).
?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)],
gleam@set:from_list([erlang:element(2, Ta)])
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 134).
?DOC(false).
-spec validate_measurements_file(
caffeine_lang@frontend@ast:measurements_file(caffeine_lang@frontend@ast:parsed())
) -> {ok,
caffeine_lang@frontend@ast:measurements_file(caffeine_lang@frontend@ast:validated())} |
{error, list(validator_error())}.
validate_measurements_file(File) ->
Type_aliases = erlang:element(2, File),
Extendables = erlang:element(3, File),
Items = erlang:element(4, File),
Type_alias_errors = begin
_pipe = validate_no_duplicates(
Type_aliases,
fun(Ta) -> erlang:element(2, Ta) end,
fun(Field@0) -> {duplicate_type_alias, Field@0} end
),
_pipe@1 = gleam@result:'try'(
_pipe,
fun(_) -> validate_no_circular_type_aliases(Type_aliases) end
),
errors_to_list(_pipe@1)
end,
Extendable_errors = collect_errors(
[validate_no_duplicates(
Extendables,
fun(E) -> erlang:element(2, E) end,
fun(Field@0) -> {duplicate_extendable, Field@0} end
),
validate_no_extendable_type_alias_collision(
Extendables,
Type_aliases
)]
),
Structural_errors = lists:append(Type_alias_errors, Extendable_errors),
guard_errors(
Structural_errors,
fun() ->
Type_alias_names = begin
_pipe@2 = Type_aliases,
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Ta@1) -> erlang:element(2, Ta@1) end
),
gleam@set:from_list(_pipe@3)
end,
Type_alias_map = build_type_alias_map(Type_aliases),
Type_ref_errors = collect_errors(
[validate_type_aliases_type_refs(
Type_aliases,
Type_alias_names,
Type_alias_map
),
validate_extendables_type_refs(
Extendables,
Type_alias_names,
Type_alias_map
),
validate_measurement_items_type_refs(
Items,
Type_alias_names,
Type_alias_map
)]
),
Extends_errors = begin
_pipe@4 = validate_measurement_items_extends(Items, Extendables),
errors_to_list(_pipe@4)
end,
Dependent_errors = lists:append(Type_ref_errors, Extends_errors),
guard_errors(
Dependent_errors,
fun() ->
{ok,
caffeine_lang@frontend@ast:promote_measurements_file(
File
)}
end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 328).
?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,
Extendable_map = build_extendable_field_map(Extendables),
gleam@list:try_each(
Items,
fun(Item) ->
gleam@result:'try'(
validate_extends_exist(
erlang:element(3, Item),
erlang:element(2, Item),
Extendable_names
),
fun(_) ->
gleam@result:'try'(
validate_no_duplicate_extends(
erlang:element(3, Item),
erlang:element(2, Item)
),
fun(_) ->
With_fields = case erlang:element(
5,
erlang:element(5, Item)
) of
{some, Mb} ->
_pipe@2 = erlang:element(
2,
erlang:element(3, Mb)
),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(F) -> erlang:element(2, F) end
),
gleam@set:from_list(_pipe@3);
none ->
gleam@set:new()
end,
validate_no_overshadowing(
erlang:element(2, Item),
erlang:element(3, Item),
[With_fields],
Extendable_map
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 275).
?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", 200).
?DOC(false).
-spec validate_expects_file(
caffeine_lang@frontend@ast:expects_file(caffeine_lang@frontend@ast:parsed())
) -> {ok,
caffeine_lang@frontend@ast:expects_file(caffeine_lang@frontend@ast:validated())} |
{error, list(validator_error())}.
validate_expects_file(File) ->
Extendables = erlang:element(2, File),
Items = erlang:element(3, File),
Structural_errors = collect_errors(
[validate_no_duplicates(
Extendables,
fun(E) -> erlang:element(2, E) end,
fun(Field@0) -> {duplicate_extendable, Field@0} end
),
validate_extendables_are_provides(Extendables)]
),
guard_errors(
Structural_errors,
fun() ->
Extends_errors = begin
_pipe = validate_expect_items_extends(Items, Extendables),
errors_to_list(_pipe)
end,
guard_errors(
Extends_errors,
fun() ->
{ok, caffeine_lang@frontend@ast:promote_expects_file(File)}
end
)
end
).