Skip to content

Commit b15aa22

Browse files
authored
Merge pull request #35 from Quad9DNS/feature/exit-on-match
Add `exit_on_match` flag for all rules
2 parents a170e6e + da1b36c commit b15aa22

File tree

8 files changed

+204
-105
lines changed

8 files changed

+204
-105
lines changed

crates/stringsimile-config/src/rules.rs

Lines changed: 108 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,34 @@ use stringsimile_matcher::{
2222
},
2323
};
2424

25+
/// Configuration for rules
26+
#[derive(Debug, Clone, Serialize, Deserialize)]
27+
pub struct RuleConfig {
28+
#[serde(flatten, default)]
29+
pub(crate) common: CommonRuleConfig,
30+
#[serde(flatten)]
31+
pub(crate) rule_type: RuleTypeConfig,
32+
}
33+
34+
/// Common configuration for rules
35+
#[derive(Debug, Clone, Serialize, Deserialize)]
36+
pub struct CommonRuleConfig {
37+
#[serde(default)]
38+
pub(crate) exit_on_match: bool,
39+
}
40+
41+
impl From<&CommonRuleConfig> for stringsimile_matcher::ruleset::CommonRuleConfig {
42+
fn from(value: &CommonRuleConfig) -> Self {
43+
Self {
44+
exit_on_match: value.exit_on_match,
45+
}
46+
}
47+
}
48+
2549
#[derive(Debug, Clone, Serialize, Deserialize)]
2650
#[serde(tag = "rule_type", rename_all = "snake_case", content = "values")]
27-
/// Configuration for rules
28-
pub enum RuleConfig {
51+
/// Configuration for specific rule types
52+
pub enum RuleTypeConfig {
2953
/// Configuration for Levenshtein rule
3054
Levenshtein(LevenshteinConfig),
3155
/// Configuration for Hamming rule
@@ -130,48 +154,63 @@ impl RuleConfig {
130154
&self,
131155
target_str: &str,
132156
ignore_mismatch_metadata: bool,
133-
) -> Result<Box<dyn GenericMatcherRule>, Error> {
134-
Ok(match self {
135-
RuleConfig::Levenshtein(levenshtein_config) => Box::new(
136-
levenshtein_config
137-
.build(ignore_mismatch_metadata)?
138-
.into_generic_matcher(),
139-
),
140-
RuleConfig::Hamming(hamming_config) => {
141-
Box::new(hamming_config.build()?.into_generic_matcher())
142-
}
143-
RuleConfig::Confusables => Box::new(ConfusablesConfig.build()?.into_generic_matcher()),
144-
RuleConfig::Jaro(jaro_config) => Box::new(jaro_config.build()?.into_generic_matcher()),
145-
RuleConfig::JaroWinkler(jaro_winkler_config) => {
146-
Box::new(jaro_winkler_config.build()?.into_generic_matcher())
147-
}
148-
RuleConfig::DamerauLevenshtein(damerau_levenshtein_config) => {
149-
Box::new(damerau_levenshtein_config.build(ignore_mismatch_metadata)?)
150-
}
151-
RuleConfig::Soundex(soundex_config) => {
152-
Box::new(soundex_config.build(target_str)?.into_generic_matcher())
153-
}
154-
RuleConfig::Metaphone(metaphone_config) => {
155-
Box::new(metaphone_config.build(target_str)?.into_generic_matcher())
156-
}
157-
RuleConfig::Nysiis(nysiis_config) => {
158-
Box::new(nysiis_config.build(target_str)?.into_generic_matcher())
159-
}
160-
RuleConfig::MatchRating => {
161-
Box::new(MatchRatingConfig.build(target_str)?.into_generic_matcher())
162-
}
163-
RuleConfig::Bitflip(bitflip_config) => Box::new(
164-
bitflip_config
165-
.clone()
166-
.unwrap_or_default()
167-
.build(target_str)?
168-
.into_generic_matcher(),
169-
),
170-
RuleConfig::Regex(regex_config) => {
171-
Box::new(regex_config.build()?.into_generic_matcher())
172-
}
173-
RuleConfig::Cidr(cidr_config) => Box::new(cidr_config.build()?.into_generic_matcher()),
174-
})
157+
) -> Result<
158+
(
159+
stringsimile_matcher::ruleset::CommonRuleConfig,
160+
Box<dyn GenericMatcherRule>,
161+
),
162+
Error,
163+
> {
164+
Ok((
165+
(&self.common).into(),
166+
match &self.rule_type {
167+
RuleTypeConfig::Levenshtein(levenshtein_config) => Box::new(
168+
levenshtein_config
169+
.build(ignore_mismatch_metadata)?
170+
.into_generic_matcher(),
171+
),
172+
RuleTypeConfig::Hamming(hamming_config) => {
173+
Box::new(hamming_config.build()?.into_generic_matcher())
174+
}
175+
RuleTypeConfig::Confusables => {
176+
Box::new(ConfusablesConfig.build()?.into_generic_matcher())
177+
}
178+
RuleTypeConfig::Jaro(jaro_config) => {
179+
Box::new(jaro_config.build()?.into_generic_matcher())
180+
}
181+
RuleTypeConfig::JaroWinkler(jaro_winkler_config) => {
182+
Box::new(jaro_winkler_config.build()?.into_generic_matcher())
183+
}
184+
RuleTypeConfig::DamerauLevenshtein(damerau_levenshtein_config) => {
185+
Box::new(damerau_levenshtein_config.build(ignore_mismatch_metadata)?)
186+
}
187+
RuleTypeConfig::Soundex(soundex_config) => {
188+
Box::new(soundex_config.build(target_str)?.into_generic_matcher())
189+
}
190+
RuleTypeConfig::Metaphone(metaphone_config) => {
191+
Box::new(metaphone_config.build(target_str)?.into_generic_matcher())
192+
}
193+
RuleTypeConfig::Nysiis(nysiis_config) => {
194+
Box::new(nysiis_config.build(target_str)?.into_generic_matcher())
195+
}
196+
RuleTypeConfig::MatchRating => {
197+
Box::new(MatchRatingConfig.build(target_str)?.into_generic_matcher())
198+
}
199+
RuleTypeConfig::Bitflip(bitflip_config) => Box::new(
200+
bitflip_config
201+
.clone()
202+
.unwrap_or_default()
203+
.build(target_str)?
204+
.into_generic_matcher(),
205+
),
206+
RuleTypeConfig::Regex(regex_config) => {
207+
Box::new(regex_config.build()?.into_generic_matcher())
208+
}
209+
RuleTypeConfig::Cidr(cidr_config) => {
210+
Box::new(cidr_config.build()?.into_generic_matcher())
211+
}
212+
},
213+
))
175214
}
176215
}
177216

@@ -491,7 +530,7 @@ mod tests {
491530
}
492531
"#;
493532

494-
let RuleConfig::Levenshtein(config) = serde_json::from_str(json).unwrap() else {
533+
let RuleTypeConfig::Levenshtein(config) = serde_json::from_str(json).unwrap() else {
495534
panic!("Expected Levenshtein config");
496535
};
497536
assert_eq!(3, config.maximum_distance);
@@ -508,7 +547,7 @@ mod tests {
508547
}
509548
"#;
510549

511-
let RuleConfig::Jaro(config) = serde_json::from_str(json).unwrap() else {
550+
let RuleTypeConfig::Jaro(config) = serde_json::from_str(json).unwrap() else {
512551
panic!("Expected Jaro config");
513552
};
514553
assert_eq!(0.4, config.match_percent_threshold);
@@ -522,7 +561,7 @@ mod tests {
522561
}
523562
"#;
524563

525-
let RuleConfig::Confusables = serde_json::from_str(json).unwrap() else {
564+
let RuleTypeConfig::Confusables = serde_json::from_str(json).unwrap() else {
526565
panic!("Expected Confusables config");
527566
};
528567
}
@@ -538,7 +577,7 @@ mod tests {
538577
}
539578
"#;
540579

541-
let RuleConfig::DamerauLevenshtein(config) = serde_json::from_str(json).unwrap() else {
580+
let RuleTypeConfig::DamerauLevenshtein(config) = serde_json::from_str(json).unwrap() else {
542581
panic!("Expected Damera Levenshtein config");
543582
};
544583
assert_eq!(3, config.maximum_distance);
@@ -555,7 +594,7 @@ mod tests {
555594
}
556595
"#;
557596

558-
let RuleConfig::JaroWinkler(config) = serde_json::from_str(json).unwrap() else {
597+
let RuleTypeConfig::JaroWinkler(config) = serde_json::from_str(json).unwrap() else {
559598
panic!("Expected Jaro-Winkler config");
560599
};
561600
assert_eq!(0.4, config.match_percent_threshold);
@@ -572,7 +611,7 @@ mod tests {
572611
}
573612
"#;
574613

575-
let RuleConfig::Hamming(config) = serde_json::from_str(json).unwrap() else {
614+
let RuleTypeConfig::Hamming(config) = serde_json::from_str(json).unwrap() else {
576615
panic!("Expected Hamming config");
577616
};
578617
assert_eq!(3, config.maximum_distance);
@@ -590,7 +629,7 @@ mod tests {
590629
}
591630
"#;
592631

593-
let RuleConfig::Soundex(config) = serde_json::from_str(json).unwrap() else {
632+
let RuleTypeConfig::Soundex(config) = serde_json::from_str(json).unwrap() else {
594633
panic!("Expected Soundex config");
595634
};
596635
assert_eq!(3, config.minimum_similarity);
@@ -608,7 +647,7 @@ mod tests {
608647
}
609648
"#;
610649

611-
let RuleConfig::Soundex(config) = serde_json::from_str(json).unwrap() else {
650+
let RuleTypeConfig::Soundex(config) = serde_json::from_str(json).unwrap() else {
612651
panic!("Expected Soundex config");
613652
};
614653
assert_eq!(3, config.minimum_similarity);
@@ -627,7 +666,7 @@ mod tests {
627666
}
628667
"#;
629668

630-
let RuleConfig::Soundex(config) = serde_json::from_str(json).unwrap() else {
669+
let RuleTypeConfig::Soundex(config) = serde_json::from_str(json).unwrap() else {
631670
panic!("Expected Soundex config");
632671
};
633672
assert_eq!(3, config.minimum_similarity);
@@ -646,7 +685,7 @@ mod tests {
646685
}
647686
"#;
648687

649-
let RuleConfig::Metaphone(config) = serde_json::from_str(json).unwrap() else {
688+
let RuleTypeConfig::Metaphone(config) = serde_json::from_str(json).unwrap() else {
650689
panic!("Expected Metaphone config");
651690
};
652691
assert_eq!(Some(3), config.max_code_length);
@@ -662,7 +701,7 @@ mod tests {
662701
}
663702
"#;
664703

665-
let RuleConfig::Metaphone(config) = serde_json::from_str(json).unwrap() else {
704+
let RuleTypeConfig::Metaphone(config) = serde_json::from_str(json).unwrap() else {
666705
panic!("Expected Metaphone config");
667706
};
668707
assert_eq!(Some(4), config.max_code_length);
@@ -680,7 +719,7 @@ mod tests {
680719
}
681720
"#;
682721

683-
let RuleConfig::Metaphone(config) = serde_json::from_str(json).unwrap() else {
722+
let RuleTypeConfig::Metaphone(config) = serde_json::from_str(json).unwrap() else {
684723
panic!("Expected Metaphone config");
685724
};
686725
assert_eq!(None, config.max_code_length);
@@ -698,7 +737,7 @@ mod tests {
698737
}
699738
"#;
700739

701-
let RuleConfig::Metaphone(config) = serde_json::from_str(json).unwrap() else {
740+
let RuleTypeConfig::Metaphone(config) = serde_json::from_str(json).unwrap() else {
702741
panic!("Expected Metaphone config");
703742
};
704743
assert_eq!(default_metaphone_max_code_length(), config.max_code_length);
@@ -717,7 +756,7 @@ mod tests {
717756
}
718757
"#;
719758

720-
let RuleConfig::Metaphone(config) = serde_json::from_str(json).unwrap() else {
759+
let RuleTypeConfig::Metaphone(config) = serde_json::from_str(json).unwrap() else {
721760
panic!("Expected Metaphone config");
722761
};
723762
assert_eq!(Some(3), config.max_code_length);
@@ -735,7 +774,7 @@ mod tests {
735774
}
736775
"#;
737776

738-
let RuleConfig::Nysiis(config) = serde_json::from_str(json).unwrap() else {
777+
let RuleTypeConfig::Nysiis(config) = serde_json::from_str(json).unwrap() else {
739778
panic!("Expected Nysiis config");
740779
};
741780
assert!(!config.strict);
@@ -750,7 +789,7 @@ mod tests {
750789
}
751790
"#;
752791

753-
let RuleConfig::Nysiis(config) = serde_json::from_str(json).unwrap() else {
792+
let RuleTypeConfig::Nysiis(config) = serde_json::from_str(json).unwrap() else {
754793
panic!("Expected Nysiis config");
755794
};
756795
assert!(config.strict);
@@ -764,7 +803,7 @@ mod tests {
764803
}
765804
"#;
766805

767-
let RuleConfig::MatchRating = serde_json::from_str(json).unwrap() else {
806+
let RuleTypeConfig::MatchRating = serde_json::from_str(json).unwrap() else {
768807
panic!("Expected Match Rating config");
769808
};
770809
}
@@ -777,7 +816,7 @@ mod tests {
777816
}
778817
"#;
779818

780-
let RuleConfig::Bitflip(config) = serde_json::from_str(json).unwrap() else {
819+
let RuleTypeConfig::Bitflip(config) = serde_json::from_str(json).unwrap() else {
781820
panic!("Expected Biflip config");
782821
};
783822
let config = config.unwrap_or_default();
@@ -798,7 +837,7 @@ mod tests {
798837
}
799838
"#;
800839

801-
let RuleConfig::Bitflip(config) = serde_json::from_str(json).unwrap() else {
840+
let RuleTypeConfig::Bitflip(config) = serde_json::from_str(json).unwrap() else {
802841
panic!("Expected Biflip config");
803842
};
804843
let config = config.unwrap_or_default();
@@ -820,7 +859,7 @@ mod tests {
820859
}
821860
"#;
822861

823-
let RuleConfig::Bitflip(config) = serde_json::from_str(json).unwrap() else {
862+
let RuleTypeConfig::Bitflip(config) = serde_json::from_str(json).unwrap() else {
824863
panic!("Expected Biflip config");
825864
};
826865
let config = config.unwrap_or_default();
@@ -842,7 +881,7 @@ mod tests {
842881
}
843882
"#;
844883

845-
let RuleConfig::Bitflip(config) = serde_json::from_str(json).unwrap() else {
884+
let RuleTypeConfig::Bitflip(config) = serde_json::from_str(json).unwrap() else {
846885
panic!("Expected Biflip config");
847886
};
848887
let config = config.unwrap_or_default();
@@ -869,7 +908,7 @@ mod tests {
869908
}
870909
"#;
871910

872-
let RuleConfig::Regex(config) = serde_json::from_str(json).unwrap() else {
911+
let RuleTypeConfig::Regex(config) = serde_json::from_str(json).unwrap() else {
873912
panic!("Expected Regex config");
874913
};
875914
assert_eq!(config.pattern, "test");
@@ -889,7 +928,7 @@ mod tests {
889928
}
890929
"#;
891930

892-
let RuleConfig::Regex(config) = serde_json::from_str(json).unwrap() else {
931+
let RuleTypeConfig::Regex(config) = serde_json::from_str(json).unwrap() else {
893932
panic!("Expected Regex config");
894933
};
895934
assert_eq!(config.pattern, "[");
@@ -909,7 +948,7 @@ mod tests {
909948
}
910949
"#;
911950

912-
let RuleConfig::Cidr(config) = serde_json::from_str(json).unwrap() else {
951+
let RuleTypeConfig::Cidr(config) = serde_json::from_str(json).unwrap() else {
913952
panic!("Expected CIDR config");
914953
};
915954
assert_eq!(config.address, "192.168.0.0/24");
@@ -929,7 +968,7 @@ mod tests {
929968
}
930969
"#;
931970

932-
let RuleConfig::Cidr(config) = serde_json::from_str(json).unwrap() else {
971+
let RuleTypeConfig::Cidr(config) = serde_json::from_str(json).unwrap() else {
933972
panic!("Expected CIDR config");
934973
};
935974
assert_eq!(config.address, "test");

0 commit comments

Comments
 (0)