1use seesaw_tgg::graph::{Graph, ValueStore};
17use seesaw_tgg::ident::{GhostId, Status};
18use std::collections::BTreeMap;
19use zerodds_ast::{AnyId, Ast, NodeKind, TypeKind};
20use zerodds_ast_core::NodeId;
21
22pub struct ModelGraph {
24 pub graph: Graph,
26 pub values: ValueStore,
28 by_ghost: BTreeMap<GhostId, NodeId>,
30 by_node: BTreeMap<NodeId, GhostId>,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub struct NodeRef {
37 pub ghost: GhostId,
39 pub node: NodeId,
41}
42
43impl ModelGraph {
44 #[must_use]
46 pub fn of(ast: &Ast) -> Self {
47 let mut this = Self {
48 graph: Graph::new(),
49 values: ValueStore::default(),
50 by_ghost: BTreeMap::new(),
51 by_node: BTreeMap::new(),
52 };
53 this.add_nodes(ast);
54 this.add_links(ast);
55 this
56 }
57
58 #[must_use]
60 pub fn model_node(&self, ghost: &GhostId) -> Option<NodeId> {
61 self.by_ghost.get(ghost).copied()
62 }
63
64 #[must_use]
66 pub fn ghost(&self, node: NodeId) -> Option<GhostId> {
67 self.by_node.get(&node).copied()
68 }
69
70 pub fn pairs(&self) -> impl Iterator<Item = (GhostId, NodeId)> + '_ {
72 self.by_ghost.iter().map(|(ghost, node)| (*ghost, *node))
73 }
74
75 #[must_use]
77 pub fn node_count(&self) -> usize {
78 self.graph.node_count()
79 }
80
81 fn add_nodes(&mut self, ast: &Ast) {
82 for (id, node) in ast.modules() {
85 self.add(AnyId::Module(id, node.node_id), type_name_of_module());
86 }
87 for (id, node) in ast.types() {
88 self.add(AnyId::Type(id, node.node_id), type_name_of_type(&node.kind));
89 self.add_facets(ast, AnyId::Type(id, node.node_id));
90 }
91 for (id, node) in ast.members() {
92 self.add(AnyId::Member(id, node.node_id), "Member");
93 self.add_facets(ast, AnyId::Member(id, node.node_id));
94 }
95 for (id, node) in ast.enumerators() {
96 self.add(AnyId::Enumerator(id, node.node_id), "Enumerator");
97 self.add_facets(ast, AnyId::Enumerator(id, node.node_id));
98 }
99 for (id, node) in ast.interfaces() {
100 self.add(AnyId::Interface(id, node.node_id), "Interface");
101 self.add_facets(ast, AnyId::Interface(id, node.node_id));
102 for attribute in &node.attributes {
106 let Ok(attribute_node) = ast.attribute(*attribute) else {
107 continue;
108 };
109 let any = AnyId::Attribute(*attribute, attribute_node.node_id);
110 let ghost = self.add(any, "Attribute");
111 self.add_facets(ast, any);
112 if attribute_node.readonly {
118 self.add_flag(ghost, "Readonly");
119 }
120 }
121 }
122 for (id, node) in ast.operations() {
126 let any = AnyId::Operation(id, node.node_id);
127 let ghost = self.add(any, "Operation");
128 self.add_facets(ast, any);
129 if node.oneway {
130 self.add_flag(ghost, "Oneway");
131 }
132 for param in &node.params {
133 let Ok(param_node) = ast.param(*param) else {
134 continue;
135 };
136 let any = AnyId::Param(*param, param_node.node_id);
137 let param_ghost = self.add(any, "Param");
138 self.add_facets(ast, any);
139 self.add_flag(param_ghost, direction_name(param_node.direction));
143 }
144 }
145 for (id, node) in ast.cases() {
146 self.add(AnyId::Case(id, node.node_id), "Case");
147 }
148 for (id, node) in ast.bit_values() {
149 self.add(AnyId::BitValue(id, node.node_id), "BitValue");
150 }
151 for (id, node) in ast.constants() {
152 self.add(AnyId::Const(id, node.node_id), "Const");
153 }
154 for (id, node) in ast.exceptions() {
155 self.add(AnyId::Exception(id, node.node_id), "Exception");
156 }
157 }
158
159 fn add(&mut self, any: AnyId, type_name: &str) -> GhostId {
161 let node = any.node_id();
162 let external = external_name(node);
163 let ghost = self.graph.add_baseline(&external, type_name);
164 let _ = self.by_ghost.insert(ghost, node);
165 let _ = self.by_node.insert(node, ghost);
166 ghost
167 }
168
169 fn add_flag(&mut self, owner: GhostId, name: &str) {
175 let flag = self.graph.add_ghost(&owner, name);
176 let _ = self.graph.connect(owner, flag, Status::Solid);
177 }
178
179 fn add_facets(&mut self, ast: &Ast, any: AnyId) {
185 let Some(facets) = ast.facets(any) else {
186 return;
187 };
188 let Some(xtypes) = facets.xtypes.as_ref() else {
189 return;
190 };
191 let owner = self.by_node.get(&any.node_id()).copied();
192 let Some(owner) = owner else {
193 return;
194 };
195
196 for (present, name) in [
200 (xtypes.is_key, "Key"),
201 (xtypes.is_optional, "Optional"),
202 (xtypes.is_external, "External"),
203 (xtypes.is_must_understand, "MustUnderstand"),
204 (xtypes.is_non_serialized, "NonSerialized"),
205 (xtypes.is_nested, "Nested"),
206 (xtypes.is_default_literal, "DefaultLiteral"),
207 ] {
208 if !present {
209 continue;
210 }
211 let facet = self.graph.add_ghost(&owner, name);
212 let _ = self.graph.connect(owner, facet, Status::Solid);
213 }
214
215 if let Some(extensibility) = xtypes.extensibility {
216 let name = match extensibility {
217 zerodds_ast::Extensibility::Final => "Final",
218 zerodds_ast::Extensibility::Appendable => "Appendable",
219 zerodds_ast::Extensibility::Mutable => "Mutable",
220 };
221 let facet = self.graph.add_ghost(&owner, name);
222 let _ = self.graph.connect(owner, facet, Status::Solid);
223 }
224 }
225
226 fn add_links(&mut self, ast: &Ast) {
228 for (id, node) in ast.types() {
229 let Some(type_ghost) = self.by_node.get(&node.node_id).copied() else {
230 continue;
231 };
232 if let Ok(module) = ast.module(node.module) {
234 if let Some(module_ghost) = self.by_node.get(&module.node_id).copied() {
235 let _ = self.graph.connect(module_ghost, type_ghost, Status::Solid);
236 }
237 }
238 for member in ast.members_of(id) {
240 let Ok(member_node) = ast.member(*member) else {
241 continue;
242 };
243 if let Some(member_ghost) = self.by_node.get(&member_node.node_id).copied() {
244 let _ = self.graph.connect(type_ghost, member_ghost, Status::Solid);
245 }
246 }
247 for case in ast.cases_of(id) {
249 let Ok(node) = ast.case(*case) else {
250 continue;
251 };
252 if let Some(ghost) = self.by_node.get(&node.node_id).copied() {
253 let _ = self.graph.connect(type_ghost, ghost, Status::Solid);
254 }
255 }
256 for bit in ast.bits_of(id) {
257 let Ok(node) = ast.bit_value(*bit) else {
258 continue;
259 };
260 if let Some(ghost) = self.by_node.get(&node.node_id).copied() {
261 let _ = self.graph.connect(type_ghost, ghost, Status::Solid);
262 }
263 }
264 for enumerator in ast.enumerators_of(id) {
266 let Ok(node) = ast.enumerator(*enumerator) else {
267 continue;
268 };
269 if let Some(ghost) = self.by_node.get(&node.node_id).copied() {
270 let _ = self.graph.connect(type_ghost, ghost, Status::Solid);
271 }
272 }
273 }
274
275 for (id, node) in ast.interfaces() {
281 let Some(interface_ghost) = self.by_node.get(&node.node_id).copied() else {
282 continue;
283 };
284 if let Ok(module) = ast.module(node.module)
285 && let Some(module_ghost) = self.by_node.get(&module.node_id).copied()
286 {
287 let _ = self
288 .graph
289 .connect(module_ghost, interface_ghost, Status::Solid);
290 }
291 for base in &node.bases {
292 let Some(base_id) = base.resolved() else {
293 continue;
294 };
295 let Ok(base_node) = ast.interface(base_id) else {
296 continue;
297 };
298 if let Some(base_ghost) = self.by_node.get(&base_node.node_id).copied() {
299 let _ = self
300 .graph
301 .connect(interface_ghost, base_ghost, Status::Solid);
302 }
303 }
304 for attribute in &node.attributes {
305 let Ok(attribute_node) = ast.attribute(*attribute) else {
306 continue;
307 };
308 if let Some(attribute_ghost) = self.by_node.get(&attribute_node.node_id).copied() {
309 let _ = self
310 .graph
311 .connect(interface_ghost, attribute_ghost, Status::Solid);
312 }
313 if let Some(target) = attribute_node.type_.resolved()
314 && let Ok(target_node) = ast.type_(target)
315 && let Some(target_ghost) = self.by_node.get(&target_node.node_id).copied()
316 && let Some(attribute_ghost) =
317 self.by_node.get(&attribute_node.node_id).copied()
318 {
319 let _ = self
320 .graph
321 .connect(attribute_ghost, target_ghost, Status::Solid);
322 }
323 }
324 let _ = id;
325 }
326
327 for (_, node) in ast.operations() {
331 let Some(operation_ghost) = self.by_node.get(&node.node_id).copied() else {
332 continue;
333 };
334 if let Some(owner_ghost) = self.by_node.get(&node.parent.node_id()).copied() {
335 let _ = self
336 .graph
337 .connect(owner_ghost, operation_ghost, Status::Solid);
338 }
339 if let Some(target) = node.return_type.as_ref().and_then(|r| r.resolved())
340 && let Ok(target_node) = ast.type_(target)
341 && let Some(target_ghost) = self.by_node.get(&target_node.node_id).copied()
342 {
343 let _ = self
344 .graph
345 .connect(operation_ghost, target_ghost, Status::Solid);
346 }
347 for raised in &node.raises {
348 let Some(raised_id) = raised.resolved() else {
349 continue;
350 };
351 let Ok(raised_node) = ast.exception(raised_id) else {
352 continue;
353 };
354 if let Some(raised_ghost) = self.by_node.get(&raised_node.node_id).copied() {
355 let _ = self
356 .graph
357 .connect(operation_ghost, raised_ghost, Status::Solid);
358 }
359 }
360 for param in &node.params {
361 let Ok(param_node) = ast.param(*param) else {
362 continue;
363 };
364 let Some(param_ghost) = self.by_node.get(¶m_node.node_id).copied() else {
365 continue;
366 };
367 let _ = self
368 .graph
369 .connect(operation_ghost, param_ghost, Status::Solid);
370 if let Some(target) = param_node.type_.resolved()
371 && let Ok(target_node) = ast.type_(target)
372 && let Some(target_ghost) = self.by_node.get(&target_node.node_id).copied()
373 {
374 let _ = self.graph.connect(param_ghost, target_ghost, Status::Solid);
375 }
376 }
377 }
378
379 for (_, node) in ast.members() {
381 let Some(member_ghost) = self.by_node.get(&node.node_id).copied() else {
382 continue;
383 };
384 let Some(target) = node.type_.resolved() else {
385 continue;
386 };
387 let Ok(target_node) = ast.type_(target) else {
388 continue;
389 };
390 if let Some(target_ghost) = self.by_node.get(&target_node.node_id).copied() {
391 let _ = self
392 .graph
393 .connect(member_ghost, target_ghost, Status::Solid);
394 }
395 }
396 }
397}
398
399fn external_name(node: NodeId) -> String {
404 format!("{}:{}", node.space().as_u32(), node.counter())
405}
406
407const fn type_name_of_module() -> &'static str {
408 "Module"
409}
410
411const fn direction_name(direction: zerodds_ast::ParamDirection) -> &'static str {
414 match direction {
415 zerodds_ast::ParamDirection::In => "In",
416 zerodds_ast::ParamDirection::Out => "Out",
417 zerodds_ast::ParamDirection::InOut => "InOut",
418 }
419}
420
421const fn type_name_of_type(kind: &TypeKind) -> &'static str {
427 match kind {
428 TypeKind::Primitive(_) => "Primitive",
429 TypeKind::Fixed { .. } => "Fixed",
430 TypeKind::String { .. } => "StringType",
431 TypeKind::Sequence { .. } => "Sequence",
432 TypeKind::Array { .. } => "Array",
433 TypeKind::Map { .. } => "Map",
434 TypeKind::Structure { .. } => "Structure",
435 TypeKind::Union { .. } => "Union",
436 TypeKind::Enumeration { .. } => "Enumeration",
437 TypeKind::Bitmask { .. } => "Bitmask",
438 TypeKind::Bitset { .. } => "Bitset",
439 TypeKind::Alias { .. } => "Alias",
440 TypeKind::Valuetype { .. } => "Valuetype",
441 TypeKind::ValueBox { .. } => "ValueBox",
442 TypeKind::Native => "Native",
443 TypeKind::Any => "Any",
444 TypeKind::ValueBase => "ValueBase",
445 TypeKind::ObjectRef { .. } => "ObjectRef",
446 TypeKind::AnnotationType { .. } => "AnnotationType",
447 }
448}
449
450#[must_use]
455pub const fn type_name_of_kind(kind: NodeKind) -> &'static str {
456 match kind {
457 NodeKind::Module => "Module",
458 NodeKind::Type => "Type",
459 NodeKind::Member => "Member",
460 NodeKind::Case => "Case",
461 NodeKind::Enumerator => "Enumerator",
462 NodeKind::BitValue => "BitValue",
463 NodeKind::Bitfield => "Bitfield",
464 NodeKind::Const => "Const",
465 NodeKind::ConstExpr => "ConstExpr",
466 NodeKind::Interface => "Interface",
467 NodeKind::Operation => "Operation",
468 NodeKind::Param => "Param",
469 NodeKind::Attribute => "Attribute",
470 NodeKind::Exception => "Exception",
471 NodeKind::AnnotationDef => "AnnotationDef",
472 NodeKind::AnnotationMember => "AnnotationMember",
473 NodeKind::Domain => "Domain",
474 NodeKind::Topic => "Topic",
475 NodeKind::QosProfile => "QosProfile",
476 NodeKind::Participant => "Participant",
477 NodeKind::Endpoint => "Endpoint",
478 NodeKind::Library => "Library",
479 }
480}