-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.