-module(caffeine_lang@analysis@dependency_validator). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/caffeine_lang/analysis/dependency_validator.gleam"). -export([build_expectation_index/1, parse_dependency_path/1, validate_dependency_relations/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/caffeine_lang/analysis/dependency_validator.gleam", 59). ?DOC(false). -spec build_expectation_index( list(caffeine_lang@linker@ir:intermediate_representation()) ) -> gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation()). build_expectation_index(Irs) -> _pipe = Irs, _pipe@1 = gleam@list:map( _pipe, fun(Ir) -> Path = caffeine_lang@linker@ir:ir_to_identifier(Ir), {Path, Ir} end ), maps:from_list(_pipe@1). -file("src/caffeine_lang/analysis/dependency_validator.gleam", 105). -spec get_all_dependency_targets( gleam@dict:dict(caffeine_lang@linker@artifacts:dependency_relation_type(), list(binary())) ) -> list(binary()). get_all_dependency_targets(Relations) -> _pipe = Relations, _pipe@1 = maps:values(_pipe), lists:append(_pipe@1). -file("src/caffeine_lang/analysis/dependency_validator.gleam", 125). -spec do_check_for_duplicates(list(binary()), gleam@set:set(binary()), binary()) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}. do_check_for_duplicates(Targets, Seen, Self_path) -> case Targets of [] -> {ok, nil}; [Target | Rest] -> gleam@bool:guard( gleam@set:contains(Seen, Target), {error, {semantic_analysis_dependency_validation_error, <<<<<<<<"Duplicate dependency reference '"/utf8, Target/binary>>/binary, "' in '"/utf8>>/binary, Self_path/binary>>/binary, "'"/utf8>>, caffeine_lang@errors:empty_context()}}, fun() -> do_check_for_duplicates( Rest, gleam@set:insert(Seen, Target), Self_path ) end ) end. -file("src/caffeine_lang/analysis/dependency_validator.gleam", 113). -spec check_for_duplicates_per_relation( gleam@dict:dict(caffeine_lang@linker@artifacts:dependency_relation_type(), list(binary())), binary() ) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}. check_for_duplicates_per_relation(Relations, Self_path) -> _pipe = Relations, _pipe@1 = maps:to_list(_pipe), gleam@list:try_each( _pipe@1, fun(Pair) -> {_, Targets} = Pair, do_check_for_duplicates(Targets, gleam@set:new(), Self_path) end ). -file("src/caffeine_lang/analysis/dependency_validator.gleam", 198). ?DOC(false). -spec parse_dependency_path(binary()) -> {ok, {binary(), binary(), binary(), binary()}} | {error, nil}. parse_dependency_path(Path) -> case gleam@string:split(Path, <<"."/utf8>>) of [Org, Team, Service, Name] when (((Org =/= <<""/utf8>>) andalso (Team =/= <<""/utf8>>)) andalso (Service =/= <<""/utf8>>)) andalso (Name =/= <<""/utf8>>) -> {ok, {Org, Team, Service, Name}}; _ -> {error, nil} end. -file("src/caffeine_lang/analysis/dependency_validator.gleam", 149). -spec validate_dependency_target( binary(), binary(), gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation()) ) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}. validate_dependency_target(Target, Self_path, Expectation_index) -> case parse_dependency_path(Target) of {error, nil} -> {error, {semantic_analysis_dependency_validation_error, <<<<<<<<"Invalid dependency reference '"/utf8, Target/binary>>/binary, "' in '"/utf8>>/binary, Self_path/binary>>/binary, "': expected format 'org.team.service.name'"/utf8>>, caffeine_lang@errors:empty_context()}}; {ok, _} -> gleam@bool:guard( Target =:= Self_path, {error, {semantic_analysis_dependency_validation_error, <<<<<<<<"Invalid dependency reference '"/utf8, Target/binary>>/binary, "' in '"/utf8>>/binary, Self_path/binary>>/binary, "': self-reference not allowed"/utf8>>, caffeine_lang@errors:empty_context()}}, fun() -> case gleam_stdlib:map_get(Expectation_index, Target) of {ok, _} -> {ok, nil}; {error, nil} -> {error, {semantic_analysis_dependency_validation_error, <<<<<<<<"Invalid dependency reference '"/utf8, Target/binary>>/binary, "' in '"/utf8>>/binary, Self_path/binary>>/binary, "': target does not exist"/utf8>>, caffeine_lang@errors:empty_context()}} end end ) end. -file("src/caffeine_lang/analysis/dependency_validator.gleam", 70). -spec validate_ir_dependencies( caffeine_lang@linker@ir:intermediate_representation(), gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation()) ) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}. validate_ir_dependencies(Ir, Expectation_index) -> gleam@bool:guard( not gleam@list:contains(erlang:element(4, Ir), dependency_relations), {ok, nil}, fun() -> Self_path = caffeine_lang@linker@ir:ir_to_identifier(Ir), gleam@result:'try'( begin _pipe = caffeine_lang@linker@ir:get_dependency_fields( erlang:element(6, Ir) ), gleam@option:to_result( _pipe, {semantic_analysis_dependency_validation_error, <>, caffeine_lang@errors:empty_context()} ) end, fun(Dep) -> Relations = erlang:element(2, Dep), gleam@result:'try'( check_for_duplicates_per_relation(Relations, Self_path), fun(_) -> All_targets = get_all_dependency_targets(Relations), _pipe@1 = All_targets, gleam@list:try_each( _pipe@1, fun(Target) -> validate_dependency_target( Target, Self_path, Expectation_index ) end ) end ) end ) end ). -file("src/caffeine_lang/analysis/dependency_validator.gleam", 212). ?DOC(" Builds a directed adjacency list from all IRs with DependencyRelations.\n"). -spec build_adjacency_list( list(caffeine_lang@linker@ir:intermediate_representation()) ) -> gleam@dict:dict(binary(), list(binary())). build_adjacency_list(Irs) -> _pipe = Irs, _pipe@1 = gleam@list:filter( _pipe, fun(Ir) -> gleam@list:contains(erlang:element(4, Ir), dependency_relations) end ), _pipe@2 = gleam@list:filter_map( _pipe@1, fun(Ir@1) -> case caffeine_lang@linker@ir:get_dependency_fields( erlang:element(6, Ir@1) ) of {some, Dep} -> Path = caffeine_lang@linker@ir:ir_to_identifier(Ir@1), Targets = get_all_dependency_targets(erlang:element(2, Dep)), {ok, {Path, Targets}}; none -> {error, nil} end end ), maps:from_list(_pipe@2). -file("src/caffeine_lang/analysis/dependency_validator.gleam", 368). -spec validate_single_hard_threshold( binary(), float(), binary(), gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation()) ) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}. validate_single_hard_threshold( Source_path, Source_threshold, Target_path, Expectation_index ) -> case gleam_stdlib:map_get(Expectation_index, Target_path) of {error, nil} -> {ok, nil}; {ok, Target_ir} -> gleam@bool:guard( not gleam@list:contains(erlang:element(4, Target_ir), s_l_o), {ok, nil}, fun() -> gleam@result:'try'( begin _pipe = caffeine_lang@linker@ir:get_slo_fields( erlang:element(6, Target_ir) ), gleam@option:to_result( _pipe, {semantic_analysis_dependency_validation_error, <>, caffeine_lang@errors:empty_context()} ) end, fun(Target_slo) -> Target_threshold = erlang:element(2, Target_slo), case gleam@float:compare( Source_threshold, Target_threshold ) of gt -> {error, {semantic_analysis_dependency_validation_error, <<<<<<<<<<<<<<<<"Hard dependency threshold violation: '"/utf8, Source_path/binary>>/binary, "' (threshold: "/utf8>>/binary, (gleam_stdlib:float_to_string( Source_threshold ))/binary>>/binary, ") cannot exceed its hard dependency '"/utf8>>/binary, Target_path/binary>>/binary, "' (threshold: "/utf8>>/binary, (gleam_stdlib:float_to_string( Target_threshold ))/binary>>/binary, ")"/utf8>>, caffeine_lang@errors:empty_context()}}; _ -> {ok, nil} end end ) end ) end. -file("src/caffeine_lang/analysis/dependency_validator.gleam", 336). ?DOC( " Validates hard dependency thresholds for a single IR.\n" " A source's threshold must not exceed its hard dependency's threshold.\n" ). -spec validate_single_ir_hard_thresholds( caffeine_lang@linker@ir:intermediate_representation(), gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation()) ) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}. validate_single_ir_hard_thresholds(Ir, Expectation_index) -> Self_path = caffeine_lang@linker@ir:ir_to_identifier(Ir), gleam@result:'try'( case {caffeine_lang@linker@ir:get_slo_fields(erlang:element(6, Ir)), caffeine_lang@linker@ir:get_dependency_fields(erlang:element(6, Ir))} of {{some, Slo}, {some, Dep}} -> {ok, {Slo, Dep}}; {_, _} -> {error, {semantic_analysis_dependency_validation_error, <>, caffeine_lang@errors:empty_context()}} end, fun(_use0) -> {Slo@1, Dep@1} = _use0, Source_threshold = erlang:element(2, Slo@1), Hard_targets = begin _pipe = gleam_stdlib:map_get(erlang:element(2, Dep@1), hard), gleam@result:unwrap(_pipe, []) end, _pipe@1 = Hard_targets, gleam@list:try_each( _pipe@1, fun(Target) -> validate_single_hard_threshold( Self_path, Source_threshold, Target, Expectation_index ) end ) end ). -file("src/caffeine_lang/analysis/dependency_validator.gleam", 293). -spec explore_neighbors( list(binary()), gleam@dict:dict(binary(), list(binary())), gleam@set:set(binary()), gleam@set:set(binary()), list(binary()) ) -> {ok, {gleam@set:set(binary()), gleam@set:set(binary())}} | {error, caffeine_lang@errors:compilation_error()}. explore_neighbors(Neighbors, Adjacency, Visited, In_progress, Path) -> case Neighbors of [] -> {ok, {Visited, In_progress}}; [Neighbor | Rest] -> gleam@bool:guard( gleam@set:contains(In_progress, Neighbor), {error, {semantic_analysis_dependency_validation_error, <<<<<<"Circular dependency detected: "/utf8, (gleam@string:join( lists:reverse(Path), <<" -> "/utf8>> ))/binary>>/binary, " -> "/utf8>>/binary, Neighbor/binary>>, caffeine_lang@errors:empty_context()}}, fun() -> gleam@bool:guard( gleam@set:contains(Visited, Neighbor), explore_neighbors( Rest, Adjacency, Visited, In_progress, Path ), fun() -> gleam@result:'try'( explore_node( Neighbor, Adjacency, Visited, In_progress, [Neighbor | Path] ), fun(_use0) -> {Visited@1, In_progress@1} = _use0, explore_neighbors( Rest, Adjacency, Visited@1, In_progress@1, Path ) end ) end ) end ) end. -file("src/caffeine_lang/analysis/dependency_validator.gleam", 269). -spec explore_node( binary(), gleam@dict:dict(binary(), list(binary())), gleam@set:set(binary()), gleam@set:set(binary()), list(binary()) ) -> {ok, {gleam@set:set(binary()), gleam@set:set(binary())}} | {error, caffeine_lang@errors:compilation_error()}. explore_node(Node, Adjacency, Visited, In_progress, Path) -> In_progress@1 = gleam@set:insert(In_progress, Node), Neighbors = begin _pipe = gleam_stdlib:map_get(Adjacency, Node), gleam@result:unwrap(_pipe, []) end, gleam@result:'try'( explore_neighbors(Neighbors, Adjacency, Visited, In_progress@1, Path), fun(_use0) -> {Visited@1, In_progress@2} = _use0, Visited@2 = gleam@set:insert(Visited@1, Node), In_progress@3 = gleam@set:delete(In_progress@2, Node), {ok, {Visited@2, In_progress@3}} end ). -file("src/caffeine_lang/analysis/dependency_validator.gleam", 244). -spec detect_cycles_loop( list(binary()), gleam@dict:dict(binary(), list(binary())), gleam@set:set(binary()), gleam@set:set(binary()) ) -> {ok, gleam@set:set(binary())} | {error, caffeine_lang@errors:compilation_error()}. detect_cycles_loop(Nodes, Adjacency, Visited, In_progress) -> case Nodes of [] -> {ok, Visited}; [Node | Rest] -> gleam@bool:guard( gleam@set:contains(Visited, Node), detect_cycles_loop(Rest, Adjacency, Visited, In_progress), fun() -> gleam@result:'try'( explore_node( Node, Adjacency, Visited, In_progress, [Node] ), fun(_use0) -> {Visited@1, In_progress@1} = _use0, detect_cycles_loop( Rest, Adjacency, Visited@1, In_progress@1 ) end ) end ) end. -file("src/caffeine_lang/analysis/dependency_validator.gleam", 231). ?DOC(" Detects circular dependencies in the dependency graph.\n"). -spec detect_cycles(list(caffeine_lang@linker@ir:intermediate_representation())) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}. detect_cycles(Irs) -> Adjacency = build_adjacency_list(Irs), Nodes = begin _pipe = Adjacency, _pipe@1 = maps:keys(_pipe), gleam@list:sort(_pipe@1, fun gleam@string:compare/2) end, _pipe@2 = detect_cycles_loop( Nodes, Adjacency, gleam@set:new(), gleam@set:new() ), gleam@result:map(_pipe@2, fun(_) -> nil end). -file("src/caffeine_lang/analysis/dependency_validator.gleam", 22). ?DOC(false). -spec validate_dependency_relations( list(caffeine_lang@linker@ir:intermediate_representation()) ) -> {ok, list(caffeine_lang@linker@ir:intermediate_representation())} | {error, caffeine_lang@errors:compilation_error()}. validate_dependency_relations(Irs) -> Expectation_index = build_expectation_index(Irs), gleam@result:'try'( begin _pipe = Irs, _pipe@1 = gleam@list:map( _pipe, fun(Ir) -> validate_ir_dependencies(Ir, Expectation_index) end ), _pipe@2 = caffeine_lang@errors:from_results(_pipe@1), gleam@result:map(_pipe@2, fun(_) -> nil end) end, fun(_) -> gleam@result:'try'( detect_cycles(Irs), fun(_) -> gleam@result:'try'( begin _pipe@3 = Irs, _pipe@4 = gleam@list:filter( _pipe@3, fun(Ir@1) -> gleam@list:contains( erlang:element(4, Ir@1), dependency_relations ) andalso gleam@list:contains( erlang:element(4, Ir@1), s_l_o ) end ), _pipe@5 = gleam@list:map( _pipe@4, fun(Ir@2) -> validate_single_ir_hard_thresholds( Ir@2, Expectation_index ) end ), _pipe@6 = caffeine_lang@errors:from_results(_pipe@5), gleam@result:map(_pipe@6, fun(_) -> nil end) end, fun(_) -> {ok, Irs} end ) end ) end ).