1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#![allow(unused)]
#![cfg_attr(coverage_nightly, coverage(off))]
//! Deterministic Mermaid Generation Engine
//!
//! This module implements PageRank-based layout and deterministic Mermaid
//! diagram generation as specified in deterministic-graphs-mmd-spec.md
//!
//! Uses a local SimpleStableGraph implementation (no petgraph dependency)
use crate::models::dag::EdgeType;
use crate::services::unified_ast_engine::{ModuleNode, ProjectMetrics};
use std::collections::BTreeMap;
use std::fmt::Write;
// ============================================================================
// Local SimpleStableGraph implementation (replaces petgraph::StableGraph)
// ============================================================================
/// Node index for the stable graph
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct NodeIndex(usize);
/// Edge representation
struct Edge<E> {
source: NodeIndex,
target: NodeIndex,
weight: E,
}
/// Edge reference for iteration
struct EdgeRef<'a, E> {
source: NodeIndex,
target: NodeIndex,
weight: &'a E,
}
impl<'a, E> EdgeRef<'a, E> {
fn source(&self) -> NodeIndex {
self.source
}
fn target(&self) -> NodeIndex {
self.target
}
fn weight(&self) -> &E {
self.weight
}
}
/// A simple stable graph implementation
/// Nodes maintain their indices even when other nodes are removed
pub struct SimpleStableGraph<N, E> {
nodes: Vec<Option<N>>,
edges: Vec<Edge<E>>,
}
impl<N: Clone, E: Clone> Default for SimpleStableGraph<N, E> {
fn default() -> Self {
Self::new()
}
}
impl<N, E> std::ops::Index<NodeIndex> for SimpleStableGraph<N, E> {
type Output = N;
fn index(&self, idx: NodeIndex) -> &Self::Output {
self.nodes[idx.0]
.as_ref()
.expect("node exists at index (stable graph invariant)")
}
}
// ============================================================================
// Public API
// ============================================================================
/// Deterministic Mermaid engine with PageRank-based layout
pub struct DeterministicMermaidEngine {
/// Number of `PageRank` iterations for stable results
pagerank_iterations: usize,
/// Quantization factor to avoid floating-point drift
quantization_factor: u32,
}
impl Default for DeterministicMermaidEngine {
fn default() -> Self {
Self::new()
}
}
impl DeterministicMermaidEngine {
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
/// Create a new instance.
pub fn new() -> Self {
Self {
pagerank_iterations: 100,
quantization_factor: 10000,
}
}
}
/// Complexity buckets for styling
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ComplexityBucket {
Low,
Medium,
High,
}
// ============================================================================
// Implementation split across include files
// ============================================================================
include!("deterministic_mermaid_engine_graph.rs");
include!("deterministic_mermaid_engine_rendering.rs");
include!("deterministic_mermaid_engine_algorithms.rs");
include!("deterministic_mermaid_engine_utils.rs");
include!("deterministic_mermaid_engine_tests.rs");