1pub use objects::object::ContentHash;
9use objects::object::{ByteSpan, SymbolEntry};
10
11mod compare;
12mod extract;
13
14pub const EXTRACTOR_VERSION: u32 = 1;
15pub const BINDING_VERSION: u32 = 1;
16pub const COMPARISON_VERSION: u32 = 1;
17pub const MAX_SOURCE_BYTES: usize = 1024 * 1024;
18pub const MAX_ASSIGNMENTS: usize = 512;
19
20pub fn extraction_key(blob: ContentHash) -> ContentHash {
21 extract::hash(&[
22 b"extraction",
23 blob.as_bytes(),
24 crate::semantic_index::grammar_version(crate::parser::Language::Rust).as_bytes(),
25 &crate::semantic_index::EXTRACTOR_VERSION.to_le_bytes(),
26 &EXTRACTOR_VERSION.to_le_bytes(),
27 ])
28}
29
30pub fn binding_key(extraction: ContentHash) -> ContentHash {
31 extract::hash(&[
33 b"bindings",
34 extraction.as_bytes(),
35 &BINDING_VERSION.to_le_bytes(),
36 ])
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub enum Side {
41 Base,
42 Source,
43}
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub enum Support {
46 Supported,
47 Partial,
48 Unsupported,
49 Unavailable,
50}
51#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub enum Limitation {
53 ParseError,
54 Language,
55 MissingElse,
56 Syntax,
57 MutableBinding,
58 UnresolvedBinding,
59 AmbiguousMatch,
60 Budget,
61 MissingSource,
62}
63#[derive(Clone, Debug, PartialEq, Eq)]
64pub struct Location {
65 pub side: Side,
66 pub span: ByteSpan,
67 pub text: String,
68}
69#[derive(Clone, Debug, PartialEq, Eq)]
70pub struct Operation {
71 pub id: String,
72 pub symbol: SymbolEntry,
73 pub source: Location,
74}
75#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76pub enum AssignmentKind {
77 FieldInitializer,
78 Assignment,
79}
80#[derive(Clone, Debug, PartialEq, Eq)]
81pub struct Assignment {
82 pub id: String,
83 pub operation_id: String,
84 pub target: String,
85 pub kind: AssignmentKind,
86 pub value_id: String,
87 pub source: Location,
88}
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90pub enum BranchLabel {
91 True,
92 False,
93}
94#[derive(Clone, Debug, PartialEq, Eq)]
95pub struct Branch {
96 pub id: String,
97 pub label: BranchLabel,
98 pub result_id: String,
99 pub source: Location,
100}
101#[derive(Clone, Debug, PartialEq, Eq)]
102pub struct Conditional {
103 pub predicate_id: String,
104 pub branches: Vec<Branch>,
105}
106#[derive(Clone, Debug, PartialEq, Eq)]
107pub struct Expression {
108 pub id: String,
109 pub source: Location,
110 pub normalized_hash: ContentHash,
111 pub conditional: Option<Conditional>,
112}
113#[derive(Clone, Debug, PartialEq, Eq)]
114pub struct Binding {
115 pub id: String,
116 pub occurrence_id: String,
117 pub name: String,
118 pub value_id: Option<String>,
119 pub declaration: Option<Location>,
120 pub lexical_scope: Option<ByteSpan>,
121 pub support: Support,
122 pub limitations: Vec<Limitation>,
123}
124#[derive(Clone, Copy, Debug, PartialEq, Eq)]
125pub enum CorrespondenceKind {
126 Retained,
127 Replaced,
128 Added,
129 Removed,
130 Unmatched,
131 Ambiguous,
132}
133#[derive(Clone, Copy, Debug, PartialEq, Eq)]
134pub enum MatchReason {
135 SameTargetInMatchedSymbol,
136 ExactNormalizedExpression,
137 ResolvedBinding,
138 SameBranchLabel,
139}
140#[derive(Clone, Debug, PartialEq, Eq)]
141pub struct Correspondence {
142 pub id: String,
143 pub base_ids: Vec<String>,
144 pub source_ids: Vec<String>,
145 pub kind: CorrespondenceKind,
146 pub reasons: Vec<MatchReason>,
147}
148#[derive(Clone, Debug, PartialEq, Eq)]
149pub struct Change {
150 pub id: String,
151 pub symbol_address: String,
152 pub support: Support,
153 pub limitations: Vec<Limitation>,
154 pub operations: Vec<Operation>,
155 pub assignments: Vec<Assignment>,
156 pub expressions: Vec<Expression>,
157 pub bindings: Vec<Binding>,
158 pub correspondences: Vec<Correspondence>,
159}
160#[derive(Clone, Debug, PartialEq, Eq)]
161pub struct Scope {
162 pub symbol_address: String,
163 pub target: String,
164 pub support: Support,
165 pub limitations: Vec<Limitation>,
166 pub sources: Vec<Location>,
167}
168#[derive(Clone, Debug, PartialEq, Eq)]
169pub struct Comparison {
170 pub base_blob: Option<ContentHash>,
171 pub source_blob: Option<ContentHash>,
172 pub changes: Vec<Change>,
173 pub analyzed: Vec<Scope>,
174 pub omitted: Vec<Scope>,
175 pub exhausted: bool,
176}
177
178pub fn compare_file(
182 path: &str,
183 base: Option<&str>,
184 source: Option<&str>,
185 symbols: &[String],
186) -> Comparison {
187 let mut comparison = Comparison {
188 base_blob: base.map(|s| ContentHash::compute_typed("blob", s.as_bytes())),
189 source_blob: source.map(|s| ContentHash::compute_typed("blob", s.as_bytes())),
190 changes: vec![],
191 analyzed: vec![],
192 omitted: vec![],
193 exhausted: true,
194 };
195 compare::run(path, base, source, symbols, &mut comparison);
196 comparison
197}
198
199#[cfg(test)]
200mod tests;