weavatrix_clone/
config.rs1use crate::{CloneError, DetectionMode, Result, Similarity};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct CloneConfig {
5 pub mode: DetectionMode,
6 pub min_tokens: usize,
7 pub k_gram: usize,
8 pub winnowing_window: usize,
9 pub min_similarity: Similarity,
10 pub candidate_similarity: Similarity,
11 pub min_shared_fingerprints: usize,
12 pub max_bucket_size: usize,
13 pub max_fragments: usize,
14 pub max_tokens_per_fragment: usize,
15 pub max_candidates: usize,
16 pub compare_overlapping_fragments: bool,
17}
18
19impl Default for CloneConfig {
20 fn default() -> Self {
21 Self {
22 mode: DetectionMode::NearMiss,
23 min_tokens: 24,
24 k_gram: 8,
25 winnowing_window: 4,
26 min_similarity: Similarity::from_permille(800),
27 candidate_similarity: Similarity::from_permille(450),
28 min_shared_fingerprints: 2,
29 max_bucket_size: 128,
30 max_fragments: 1_000_000,
31 max_tokens_per_fragment: 100_000,
32 max_candidates: 5_000_000,
33 compare_overlapping_fragments: false,
34 }
35 }
36}
37
38impl CloneConfig {
39 pub fn validate(self) -> Result<Self> {
46 if self.k_gram == 0 {
47 return Err(invalid("k_gram", "must be greater than zero"));
48 }
49 if self.winnowing_window == 0 {
50 return Err(invalid("winnowing_window", "must be greater than zero"));
51 }
52 let guaranteed = self
53 .k_gram
54 .checked_add(self.winnowing_window)
55 .and_then(|value| value.checked_sub(1))
56 .ok_or(CloneError::CapacityExceeded {
57 resource: "winnowing guarantee",
58 limit: usize::MAX,
59 })?;
60 if self.min_tokens < guaranteed {
61 return Err(invalid(
62 "min_tokens",
63 "must cover at least one complete winnowing window",
64 ));
65 }
66 if self.candidate_similarity > self.min_similarity {
67 return Err(invalid(
68 "candidate_similarity",
69 "must not exceed min_similarity",
70 ));
71 }
72 for (field, value) in [
73 ("min_shared_fingerprints", self.min_shared_fingerprints),
74 ("max_bucket_size", self.max_bucket_size),
75 ("max_fragments", self.max_fragments),
76 ("max_tokens_per_fragment", self.max_tokens_per_fragment),
77 ("max_candidates", self.max_candidates),
78 ] {
79 if value == 0 {
80 return Err(invalid(field, "must be greater than zero"));
81 }
82 }
83 Ok(self)
84 }
85}
86
87const fn invalid(field: &'static str, reason: &'static str) -> CloneError {
88 CloneError::InvalidConfig { field, reason }
89}