zer_blocking/keys/
exact.rs1use zer_core::{record::Record, schema::Schema};
2
3use super::BlockingKey;
4use crate::normalize::normalize_text;
5
6pub struct ExactFieldKey {
7 field: String,
8}
9
10impl ExactFieldKey {
11 pub fn new(field: &str) -> Self {
12 Self {
13 field: field.into(),
14 }
15 }
16}
17
18impl BlockingKey for ExactFieldKey {
19 fn name(&self) -> &str {
20 "exact"
21 }
22
23 fn extract(&self, record: &Record, _schema: &Schema) -> Vec<String> {
24 let cow = record.field_as_str(&self.field);
25 let raw = match cow.as_deref() {
26 Some(s) => s,
27 None => return vec![],
28 };
29
30 let normalized = normalize_text(raw);
31 if normalized.is_empty() {
32 return vec![];
33 }
34
35 vec![normalized]
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use zer_core::{
43 record::FieldValue,
44 schema::{FieldKind, SchemaBuilder},
45 };
46
47 fn schema() -> Schema {
48 SchemaBuilder::new()
49 .field("category", FieldKind::Categorical)
50 .build()
51 .unwrap()
52 }
53
54 #[test]
55 fn exact_normalizes_and_matches() {
56 let k = ExactFieldKey::new("category");
57 let s = schema();
58 let r1 = Record::new(1).insert("category", FieldValue::Text("Eenmanszaak".into()));
59 let r2 = Record::new(2).insert("category", FieldValue::Text("EENMANSZAAK".into()));
60 assert_eq!(k.extract(&r1, &s), k.extract(&r2, &s));
61 }
62
63 #[test]
64 fn exact_empty_field_returns_empty() {
65 let k = ExactFieldKey::new("category");
66 let r = Record::new(1).insert("category", FieldValue::Text("".into()));
67 assert!(k.extract(&r, &schema()).is_empty());
68 }
69
70 #[test]
71 fn exact_missing_field_returns_empty() {
72 let k = ExactFieldKey::new("category");
73 let r = Record::new(1);
74 assert!(k.extract(&r, &schema()).is_empty());
75 }
76}