meta_ast/output/shard/
edge.rs1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::graph::{CodeGraph, EdgeKind, NodeData};
8use crate::output::shard::error::ShardError;
9use crate::output::shard::name::stable_node_name;
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct ShardEdge {
14 pub source_name: String,
15 pub target_name: String,
16 pub kind: ShardEdgeKind,
17 pub confidence: f32,
18 pub flow_kind: Option<ShardFlowKind>,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
22#[serde(rename_all = "snake_case")]
23pub enum ShardEdgeKind {
24 Ownership,
25 Import,
26 Reference,
27 Flow,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
31#[serde(rename_all = "snake_case")]
32pub enum ShardFlowKind {
33 DefUse,
34 Argument,
35 Return,
36 FieldAccess,
37}
38
39impl From<EdgeKind> for ShardEdgeKind {
40 fn from(kind: EdgeKind) -> Self {
41 match kind {
42 EdgeKind::Ownership => Self::Ownership,
43 EdgeKind::Import => Self::Import,
44 EdgeKind::Reference => Self::Reference,
45 EdgeKind::Flow => Self::Flow,
46 }
47 }
48}
49
50impl From<ShardEdgeKind> for EdgeKind {
51 fn from(kind: ShardEdgeKind) -> Self {
52 match kind {
53 ShardEdgeKind::Ownership => Self::Ownership,
54 ShardEdgeKind::Import => Self::Import,
55 ShardEdgeKind::Reference => Self::Reference,
56 ShardEdgeKind::Flow => Self::Flow,
57 }
58 }
59}
60
61impl From<crate::model::FlowKind> for ShardFlowKind {
62 fn from(kind: crate::model::FlowKind) -> Self {
63 match kind {
64 crate::model::FlowKind::DefUse => Self::DefUse,
65 crate::model::FlowKind::Argument => Self::Argument,
66 crate::model::FlowKind::Return => Self::Return,
67 crate::model::FlowKind::FieldAccess => Self::FieldAccess,
68 }
69 }
70}
71
72impl From<ShardFlowKind> for crate::model::FlowKind {
73 fn from(kind: ShardFlowKind) -> Self {
74 match kind {
75 ShardFlowKind::DefUse => Self::DefUse,
76 ShardFlowKind::Argument => Self::Argument,
77 ShardFlowKind::Return => Self::Return,
78 ShardFlowKind::FieldAccess => Self::FieldAccess,
79 }
80 }
81}
82
83pub(crate) fn validate_edge(
84 edge: &ShardEdge,
85 line: usize,
86 edge_index: usize,
87) -> Result<(), ShardError> {
88 let valid_confidence = edge.confidence.is_finite() && (0.0..=1.0).contains(&edge.confidence);
89 if !valid_confidence {
90 return Err(ShardError::InvalidEdge {
91 line,
92 edge_index,
93 message: "confidence must be finite and in the range 0.0..=1.0".to_string(),
94 });
95 }
96 if edge.kind == ShardEdgeKind::Flow {
97 return Err(ShardError::InvalidEdge {
98 line,
99 edge_index,
100 message: "schema version 2 does not persist dataflow nodes".to_string(),
101 });
102 }
103 if edge.flow_kind.is_some() {
104 return Err(ShardError::InvalidEdge {
105 line,
106 edge_index,
107 message: "non-flow edges forbid flow_kind".to_string(),
108 });
109 }
110 Ok(())
111}
112
113pub fn restore_shard_edges(graph: &mut CodeGraph, edges: &[ShardEdge]) -> Result<(), ShardError> {
115 let endpoint_index = graph
116 .graph()
117 .node_indices()
118 .filter(|index| !matches!(graph.graph()[*index], NodeData::Data(_)))
119 .map(|index| stable_node_name(graph, index).map(|name| (name, index)))
120 .collect::<Result<HashMap<_, _>, ShardError>>()?;
121
122 for (edge_index, edge) in edges.iter().enumerate() {
123 validate_edge(edge, 0, edge_index)?;
124 let source = endpoint_index
125 .get(&edge.source_name)
126 .copied()
127 .ok_or_else(|| ShardError::MissingEndpoint {
128 name: edge.source_name.clone(),
129 })?;
130 let target = endpoint_index
131 .get(&edge.target_name)
132 .copied()
133 .ok_or_else(|| ShardError::MissingEndpoint {
134 name: edge.target_name.clone(),
135 })?;
136 graph.add_edge_normalized_with_flow(
137 source,
138 target,
139 edge.kind.into(),
140 edge.confidence,
141 edge.flow_kind.map(Into::into),
142 );
143 }
144 Ok(())
145}