use crate::search::{
Embedder, SemanticParams, SemanticScope, SemanticUnit, Thresholds, UnitSource,
find_semantic_clones,
};
use crate::units::{extract_units, supports_units};
use cpd_core::models::CpdClone;
use cpd_finder::pass::{ClonePass, PassContext, PassSource};
use std::sync::{Arc, Mutex, MutexGuard};
pub struct SemanticPass {
embedder: Arc<dyn Embedder>,
thresholds: Thresholds,
scope: SemanticScope,
sources: Mutex<Vec<UnitSource>>,
}
impl SemanticPass {
pub fn new(embedder: Arc<dyn Embedder>, thresholds: Thresholds, scope: SemanticScope) -> Self {
Self {
embedder,
thresholds,
scope,
sources: Mutex::new(Vec::new()),
}
}
pub fn take_sources(&self) -> Vec<UnitSource> {
let mut sources = std::mem::take(&mut *self.lock());
sources.sort_by(|a, b| a.id.cmp(&b.id));
sources
}
fn lock(&self) -> MutexGuard<'_, Vec<UnitSource>> {
self.sources
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
}
impl ClonePass for SemanticPass {
fn name(&self) -> &'static str {
"--semantic"
}
fn reads(&self, format: &str) -> bool {
supports_units(format)
}
fn read(&self, format: &str, content: &str, sources: &[PassSource<'_>]) {
let maps = extract_units(content, format);
let found: Vec<UnitSource> = sources
.iter()
.filter_map(|source| {
let map = maps.iter().find(|m| m.format == source.format)?;
let units: Vec<SemanticUnit> = map
.units
.iter()
.filter_map(|u| {
SemanticUnit::build(
u.grammar,
u.name.clone(),
u.start.clone(),
u.end.clone(),
u.text.clone(),
source.spans,
)
})
.collect();
(!units.is_empty()).then(|| UnitSource {
id: source.id.to_string(),
format: source.format.to_string(),
units,
path_label: Default::default(),
})
})
.collect();
if !found.is_empty() {
self.lock().extend(found);
}
}
fn find(&self, context: &PassContext<'_>) -> Result<Vec<CpdClone>, String> {
let mut sources = self.take_sources();
for source in &mut sources {
source.path_label = (context.label)(&source.id);
}
let params = SemanticParams {
thresholds: self.thresholds,
min_tokens: context.min_tokens,
min_lines: context.min_lines,
scope: self.scope,
};
find_semantic_clones(&sources, self.embedder.as_ref(), ¶ms, context.existing)
}
}