Skip to main content

code_moniker_workspace/changes/
analyzer.rs

1// code-moniker: ignore-file[smell-clone-reflex]
2// Change analysis materializes owned summaries from borrowed git diff records.
3use std::path::PathBuf;
4
5use code_moniker_core::core::moniker::Moniker;
6use code_moniker_core::lang::Lang;
7
8use crate::code::{CodeIndexSymbolProvider, NormalizedSource, NormalizedSymbol};
9use crate::snapshot::{
10	ChangeId, ChangeRecord, ChangeRecordCoreFields, ChangeStatus, SymbolLocation,
11};
12
13use super::diff;
14
15pub struct ChangeAnalyzer<'a, 'm> {
16	symbols: &'a CodeIndexSymbolProvider<'m>,
17}
18
19impl<'a, 'm> ChangeAnalyzer<'a, 'm> {
20	pub fn new(symbols: &'a CodeIndexSymbolProvider<'m>) -> Self {
21		Self { symbols }
22	}
23
24	pub fn analyze(&self, entries: &[diff::ChangeEntry]) -> Vec<ChangeRecord> {
25		entries
26			.iter()
27			.enumerate()
28			.map(|(idx, entry)| PendingChange::from(entry).record(idx, self.symbols))
29			.collect()
30	}
31}
32
33struct PendingChange {
34	loc: Option<SymbolLocation>,
35	status: ChangeStatus,
36	lang: Lang,
37	file_path: PathBuf,
38	kind: String,
39	name: String,
40	moniker: Moniker,
41	hunk_count: usize,
42	line_range: Option<(u32, u32)>,
43}
44
45impl PendingChange {
46	fn record(&self, idx: usize, symbols: &CodeIndexSymbolProvider<'_>) -> ChangeRecord {
47		let symbol = self.symbol(symbols);
48		let source = self.source(symbols, symbol.as_ref());
49		let mut record = ChangeRecord::new(ChangeRecordCoreFields {
50			id: ChangeId::new(format!("change:{idx}")),
51			status: self.status,
52			identity: self.identity(symbols, symbol.as_ref()),
53			language: self.language(source.as_ref()),
54			file_path: self.display_path(source.as_ref()),
55			name: self.name.clone(),
56			kind: self.kind.clone(),
57			line_range: self.line_range,
58			hunk_count: self.hunk_count,
59		});
60		if let Some(source) = source {
61			record = record.with_source(source.id, source.uri);
62		}
63		if let Some(symbol) = symbol {
64			record = record.with_symbol(symbol.id);
65		}
66		record
67	}
68
69	fn symbol(&self, symbols: &CodeIndexSymbolProvider<'_>) -> Option<NormalizedSymbol> {
70		self.loc
71			.and_then(|loc| symbols.symbol_at(loc))
72			.or_else(|| symbols.symbol_for_moniker(&self.moniker))
73	}
74
75	fn source(
76		&self,
77		symbols: &CodeIndexSymbolProvider<'_>,
78		symbol: Option<&NormalizedSymbol>,
79	) -> Option<NormalizedSource> {
80		self.loc
81			.and_then(|loc| symbols.source_at(loc.file))
82			.or_else(|| symbol.map(|symbol| symbol.source.clone()))
83	}
84
85	fn identity(
86		&self,
87		symbols: &CodeIndexSymbolProvider<'_>,
88		symbol: Option<&NormalizedSymbol>,
89	) -> String {
90		symbol
91			.map(|symbol| symbol.identity.clone())
92			.unwrap_or_else(|| symbols.identity_for_moniker(&self.moniker))
93	}
94
95	fn language(&self, source: Option<&NormalizedSource>) -> String {
96		source
97			.map(NormalizedSource::language_tag)
98			.unwrap_or_else(|| self.lang.tag().to_string())
99	}
100
101	fn display_path(&self, source: Option<&NormalizedSource>) -> String {
102		source
103			.map(NormalizedSource::display_path)
104			.unwrap_or_else(|| self.file_path.display().to_string())
105	}
106}
107
108impl NormalizedSource {
109	fn language_tag(&self) -> String {
110		self.language.tag().to_string()
111	}
112
113	fn display_path(&self) -> String {
114		self.rel_path.display().to_string()
115	}
116}
117
118fn change_status(status: diff::ChangeStatus) -> ChangeStatus {
119	match status {
120		diff::ChangeStatus::Added => ChangeStatus::Added,
121		diff::ChangeStatus::Modified => ChangeStatus::Modified,
122		diff::ChangeStatus::Removed => ChangeStatus::Removed,
123	}
124}
125
126impl From<&diff::ChangeEntry> for PendingChange {
127	fn from(entry: &diff::ChangeEntry) -> Self {
128		Self {
129			loc: entry.loc,
130			status: change_status(entry.status),
131			lang: entry.lang,
132			file_path: entry.file_path.clone(),
133			kind: entry.kind.clone(),
134			name: entry.name.clone(),
135			moniker: entry.moniker.clone(),
136			hunk_count: entry.hunk_count,
137			line_range: entry.line_range,
138		}
139	}
140}