heddle_object_model/object/
semantic_edges.rs1use serde::{Deserialize, Serialize};
5
6use super::{ContentHash, SemanticIndexError};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum SemanticEdgeKind {
12 RefersTo,
14 Calls,
16 TypeRef,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
22pub struct ResolvedSemanticEdge {
23 pub source_occurrence: u32,
25 pub target_path: String,
27 pub target_file_node: ContentHash,
29 pub target_definition: u32,
31 pub kind: SemanticEdgeKind,
33}
34
35#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
37pub struct FileBindingDelta {
38 pub path: String,
40 pub file_node: Option<ContentHash>,
42 pub replace_edges: Vec<ResolvedSemanticEdge>,
44}
45
46impl FileBindingDelta {
47 pub fn new(
49 path: impl Into<String>,
50 file_node: Option<ContentHash>,
51 mut replace_edges: Vec<ResolvedSemanticEdge>,
52 ) -> Self {
53 replace_edges.sort();
54 replace_edges.dedup();
55 Self {
56 path: path.into(),
57 file_node,
58 replace_edges,
59 }
60 }
61}
62
63#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
65pub struct BindingDelta {
66 pub format_version: u8,
67 pub parent: Option<ContentHash>,
69 pub files: Vec<FileBindingDelta>,
71}
72
73impl BindingDelta {
74 pub const FORMAT_VERSION: u8 = 1;
75
76 pub fn new(parent: Option<ContentHash>, mut files: Vec<FileBindingDelta>) -> Self {
78 files.sort_by(|a, b| a.path.cmp(&b.path));
79 Self {
80 format_version: Self::FORMAT_VERSION,
81 parent,
82 files,
83 }
84 }
85
86 pub fn encode(&self) -> Result<Vec<u8>, SemanticIndexError> {
88 rmp_serde::to_vec_named(self).map_err(|err| SemanticIndexError::Encoding(err.to_string()))
89 }
90
91 pub fn decode(bytes: &[u8]) -> Result<Self, SemanticIndexError> {
93 let delta: Self = rmp_serde::from_slice(bytes)
94 .map_err(|err| SemanticIndexError::Encoding(err.to_string()))?;
95 if delta.format_version != Self::FORMAT_VERSION {
96 return Err(SemanticIndexError::UnsupportedVersion(delta.format_version));
97 }
98 Ok(delta)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 fn hash(seed: u8) -> ContentHash {
107 ContentHash::from_bytes([seed; 32])
108 }
109
110 #[test]
111 fn binding_delta_roundtrips_and_canonicalizes() {
112 let edge = ResolvedSemanticEdge {
113 source_occurrence: 3,
114 target_path: "api.rs".to_string(),
115 target_file_node: hash(2),
116 target_definition: 1,
117 kind: SemanticEdgeKind::Calls,
118 };
119 let delta = BindingDelta::new(
120 Some(hash(9)),
121 vec![
122 FileBindingDelta::new("z.rs", Some(hash(1)), vec![edge.clone(), edge]),
123 FileBindingDelta::new("a.rs", None, Vec::new()),
124 ],
125 );
126
127 assert_eq!(delta.files[0].path, "a.rs");
128 assert_eq!(delta.files[1].replace_edges.len(), 1);
129 assert_eq!(
130 BindingDelta::decode(&delta.encode().unwrap()).unwrap(),
131 delta
132 );
133 }
134}