-module(caffeine_lang@codegen@dependency_graph). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/caffeine_lang/codegen/dependency_graph.gleam"). -export([generate/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/codegen/dependency_graph.gleam", 40). ?DOC(" Builds the service grouping key from IR metadata.\n"). -spec service_key(caffeine_lang@linker@ir:intermediate_representation()) -> binary(). service_key(Ir) -> erlang:element(4, erlang:element(2, Ir)). -file("src/caffeine_lang/codegen/dependency_graph.gleam", 83). ?DOC( " Escapes characters that have special meaning in Mermaid labels.\n" " Uses numeric HTML entity codes (e.g. #91; for [) which are broadly compatible.\n" ). -spec escape_label(binary()) -> binary(). escape_label(Text) -> _pipe = Text, _pipe@1 = gleam@string:replace(_pipe, <<"\""/utf8>>, <<"#34;"/utf8>>), _pipe@2 = gleam@string:replace(_pipe@1, <<"["/utf8>>, <<"#91;"/utf8>>), _pipe@3 = gleam@string:replace(_pipe@2, <<"]"/utf8>>, <<"#93;"/utf8>>), _pipe@4 = gleam@string:replace(_pipe@3, <<"("/utf8>>, <<"#40;"/utf8>>), _pipe@5 = gleam@string:replace(_pipe@4, <<")"/utf8>>, <<"#41;"/utf8>>), _pipe@6 = gleam@string:replace(_pipe@5, <<"{"/utf8>>, <<"#123;"/utf8>>), _pipe@7 = gleam@string:replace(_pipe@6, <<"}"/utf8>>, <<"#125;"/utf8>>), _pipe@8 = gleam@string:replace(_pipe@7, <<"<"/utf8>>, <<"#60;"/utf8>>), gleam@string:replace(_pipe@8, <<">"/utf8>>, <<"#62;"/utf8>>). -file("src/caffeine_lang/codegen/dependency_graph.gleam", 114). -spec is_id_char(binary()) -> boolean(). is_id_char(G) -> case G of <<"_"/utf8>> -> true; _ -> case gleam@string:to_utf_codepoints(G) of [Cp] -> Code = gleam_stdlib:identity(Cp), (((Code >= 65) andalso (Code =< 90)) orelse ((Code >= 97) andalso (Code =< 122))) orelse ((Code >= 48) andalso (Code =< 57)); _ -> false end end. -file("src/caffeine_lang/codegen/dependency_graph.gleam", 97). ?DOC(" Strips all non-alphanumeric characters (except underscores) for Mermaid-safe node IDs.\n"). -spec sanitize_id(binary()) -> binary(). sanitize_id(Path) -> _pipe = Path, _pipe@1 = gleam@string:to_graphemes(_pipe), _pipe@2 = gleam@list:map(_pipe@1, fun(G) -> case G of <<"."/utf8>> -> <<"_"/utf8>>; <<" "/utf8>> -> <<"_"/utf8>>; <<"-"/utf8>> -> <<"_"/utf8>>; _ -> case is_id_char(G) of true -> G; false -> <<""/utf8>> end end end), erlang:list_to_binary(_pipe@2). -file("src/caffeine_lang/codegen/dependency_graph.gleam", 45). ?DOC(" Generates a single Mermaid node declaration with just the expectation name.\n"). -spec build_node(caffeine_lang@linker@ir:intermediate_representation()) -> binary(). build_node(Ir) -> Path = caffeine_lang@linker@ir:ir_to_identifier(Ir), Id = sanitize_id(Path), Safe_name = escape_label(erlang:element(2, erlang:element(2, Ir))), <<<<<<<<" "/utf8, Id/binary>>/binary, "[\""/utf8>>/binary, Safe_name/binary>>/binary, "\"]"/utf8>>. -file("src/caffeine_lang/codegen/dependency_graph.gleam", 21). ?DOC(" Groups IRs by service and generates Mermaid subgraph blocks.\n"). -spec build_subgraphs( list(caffeine_lang@linker@ir:intermediate_representation()) ) -> list(binary()). build_subgraphs(Irs) -> _pipe = Irs, _pipe@1 = gleam@list:group(_pipe, fun(Ir) -> service_key(Ir) end), _pipe@2 = maps:to_list(_pipe@1), _pipe@3 = gleam@list:sort( _pipe@2, fun(A, B) -> gleam@string:compare(erlang:element(1, A), erlang:element(1, B)) end ), gleam@list:flat_map( _pipe@3, fun(Group) -> {Service, Group_irs} = Group, Header = <<<<<<<<" subgraph "/utf8, (sanitize_id(Service))/binary>>/binary, "[\""/utf8>>/binary, (escape_label(Service))/binary>>/binary, "\"]"/utf8>>, Nodes = gleam@list:map(Group_irs, fun build_node/1), lists:append([[Header], Nodes, [<<" end"/utf8>>]]) end ). -file("src/caffeine_lang/codegen/dependency_graph.gleam", 53). ?DOC(" Generates Mermaid edge declarations for hard and soft dependencies.\n"). -spec build_edges(list(caffeine_lang@linker@ir:intermediate_representation())) -> list(binary()). build_edges(Irs) -> _pipe = Irs, _pipe@1 = gleam@list:filter( _pipe, fun(Ir) -> gleam@list:contains(erlang:element(4, Ir), dependency_relations) end ), gleam@list:flat_map( _pipe@1, fun(Ir@1) -> Source_id = sanitize_id( caffeine_lang@linker@ir:ir_to_identifier(Ir@1) ), case caffeine_lang@linker@ir:get_dependency_fields( erlang:element(6, Ir@1) ) of none -> []; {some, Dep} -> Hard_edges = begin _pipe@2 = gleam_stdlib:map_get( erlang:element(2, Dep), hard ), _pipe@3 = gleam@result:unwrap(_pipe@2, []), gleam@list:map( _pipe@3, fun(Target) -> <<<<<<" "/utf8, Source_id/binary>>/binary, " -->|hard| "/utf8>>/binary, (sanitize_id(Target))/binary>> end ) end, Soft_edges = begin _pipe@4 = gleam_stdlib:map_get( erlang:element(2, Dep), soft ), _pipe@5 = gleam@result:unwrap(_pipe@4, []), gleam@list:map( _pipe@5, fun(Target@1) -> <<<<<<" "/utf8, Source_id/binary>>/binary, " -.->|soft| "/utf8>>/binary, (sanitize_id(Target@1))/binary>> end ) end, lists:append(Hard_edges, Soft_edges) end end ). -file("src/caffeine_lang/codegen/dependency_graph.gleam", 11). ?DOC( " Generates a Mermaid flowchart string from dependency relations in IRs.\n" " Nodes are grouped into subgraphs by service.\n" ). -spec generate(list(caffeine_lang@linker@ir:intermediate_representation())) -> binary(). generate(Irs) -> Subgraphs = build_subgraphs(Irs), Edges = build_edges(Irs), _pipe = [[<<"graph TD"/utf8>>], Subgraphs, Edges], _pipe@1 = lists:append(_pipe), gleam@string:join(_pipe@1, <<"\n"/utf8>>).