1use std::collections::{BTreeMap, BTreeSet, VecDeque};
2
3use sim_lib_mutation::{
4 ArenaError, EdgeId, EdgeLimits, EdgeSnapshot, EdgeVisitor, ManagedArena, ManagedHandle,
5 ManagedId, ManagedNode, ManagedObject, StrongEdgeMutationError,
6};
7
8#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct ManagedException<P, R> {
15 node: ManagedNode<P>,
16 relation_roles: BTreeMap<EdgeId, R>,
17}
18
19impl<P, R> ManagedException<P, R> {
20 pub const fn new(payload: P) -> Self {
22 Self::with_edge_limits(payload, EdgeLimits::DEFAULT)
23 }
24
25 pub const fn with_edge_limits(payload: P, limits: EdgeLimits) -> Self {
27 Self {
28 node: ManagedNode::with_edge_limits(payload, limits),
29 relation_roles: BTreeMap::new(),
30 }
31 }
32
33 pub const fn payload(&self) -> &P {
35 self.node.role()
36 }
37
38 pub fn replace_payload(&mut self, payload: P) -> P {
40 self.node.replace_role(payload)
41 }
42
43 pub fn insert_relation(
45 &mut self,
46 role: R,
47 target: ManagedId,
48 ) -> Result<EdgeId, StrongEdgeMutationError> {
49 let edge = self.node.insert_strong(target)?;
50 let previous = self.relation_roles.insert(edge, role);
51 debug_assert!(
52 previous.is_none(),
53 "fresh managed edge must not have a role"
54 );
55 Ok(edge)
56 }
57
58 pub fn remove_relation(
60 &mut self,
61 edge: EdgeId,
62 expected: ManagedId,
63 ) -> Result<(R, ManagedId), StrongEdgeMutationError> {
64 let target = self.node.remove_strong(edge, expected)?;
65 let role = self
66 .relation_roles
67 .remove(&edge)
68 .expect("every adapter relation has role evidence");
69 Ok((role, target))
70 }
71
72 pub fn relations(&self) -> impl Iterator<Item = (EdgeId, &R, ManagedId)> {
74 self.node
75 .edge_snapshot()
76 .into_iter()
77 .filter_map(|snapshot| {
78 let EdgeSnapshot::Strong { edge, target } = snapshot else {
79 return None;
80 };
81 Some((edge, &self.relation_roles[&edge], target))
82 })
83 }
84}
85
86impl<P, R> ManagedObject for ManagedException<P, R> {
87 fn trace_edges(&self, visitor: &mut dyn EdgeVisitor) {
88 self.node.trace_edges(visitor);
89 }
90
91 fn clear_weak_edge(&mut self, edge: EdgeId, expected: ManagedId) -> bool {
92 self.node.clear_weak_edge(edge, expected)
93 }
94
95 fn clear_ephemeron_edge(
96 &mut self,
97 edge: EdgeId,
98 expected_key: ManagedId,
99 expected_value: ManagedId,
100 ) -> bool {
101 self.node
102 .clear_ephemeron_edge(edge, expected_key, expected_value)
103 }
104}
105
106#[derive(Clone, Copy, Debug, Eq, PartialEq)]
108pub struct ExceptionGraphBudget {
109 max_edges: usize,
110}
111
112impl ExceptionGraphBudget {
113 pub const fn new(max_edges: usize) -> Self {
115 Self { max_edges }
116 }
117
118 pub const fn max_edges(self) -> usize {
120 self.max_edges
121 }
122}
123
124#[derive(Clone, Debug, Eq, PartialEq)]
126pub struct ExceptionGraphEdge<R> {
127 pub parent: ManagedId,
129 pub edge: EdgeId,
131 pub role: R,
133 pub target: ManagedId,
135}
136
137#[derive(Clone, Debug, Eq, PartialEq)]
139pub struct ExceptionGraphView<R> {
140 pub edges: Vec<ExceptionGraphEdge<R>>,
142 pub truncated: bool,
144}
145
146impl<R: Clone> ExceptionGraphView<R> {
147 pub fn project<P>(
152 arena: &ManagedArena<ManagedException<P, R>>,
153 root: ManagedHandle,
154 budget: ExceptionGraphBudget,
155 ) -> Result<Self, ArenaError> {
156 arena.get(root)?;
157 let mut queue = VecDeque::from([root.id()]);
158 let mut expanded = BTreeSet::new();
159 let mut edges = Vec::new();
160
161 while let Some(parent) = queue.pop_front() {
162 if !expanded.insert(parent) {
163 continue;
164 }
165 let handle = arena.handle(parent)?;
166 for (edge, role, target) in arena.get(handle)?.relations() {
167 if edges.len() == budget.max_edges {
168 return Ok(Self {
169 edges,
170 truncated: true,
171 });
172 }
173 edges.push(ExceptionGraphEdge {
174 parent,
175 edge,
176 role: role.clone(),
177 target,
178 });
179 if !expanded.contains(&target) {
180 queue.push_back(target);
181 }
182 }
183 }
184 Ok(Self {
185 edges,
186 truncated: false,
187 })
188 }
189}