suffix_rs/
lib.rs

1#![allow(dead_code)]
2
3
4#[macro_use]
5pub extern crate serde_derive;
6
7
8use std::collections::HashMap;
9use std::cmp::Ordering;
10
11#[cfg(test)]
12mod tests {
13
14    use MockKDTree;
15    use KDTree;
16    use MockEntity;
17
18    #[test]
19    fn it_works() {
20        assert_eq!(2 + 2, 4);
21    }
22
23
24
25    #[test]
26    fn mock_works() {
27        let tree = MockKDTree::build(
28            &vec![
29                MockEntity {
30                    id: 1,
31                    txt: "Apple".to_string(),
32                },
33                MockEntity {
34                    id: 2,
35                    txt: "Banana".to_string(),
36                },
37                MockEntity {
38                    id: 3,
39                    txt: "Lettuce".to_string(),
40                },
41            ],
42            true,
43        );
44
45        let first_result = tree.search("an");
46        assert_eq!(first_result.len(), 1);
47        assert_eq!(first_result[0].id, 2);
48        assert_eq!(first_result[0].index, 1);
49
50        let second_result = tree.search("e");
51        assert_eq!(second_result.len(), 2);
52        assert_eq!(second_result[0].id, 1);
53        assert_eq!(second_result[0].index, 4);
54        assert_eq!(second_result[1].id, 3);
55        assert_eq!(second_result[1].index, 1);
56    }
57
58
59    #[test]
60    fn mock_case_insensitive_works() {
61        let tree = MockKDTree::build(
62            &vec![
63                MockEntity {
64                    id: 1,
65                    txt: "Apple".to_string(),
66                },
67                MockEntity {
68                    id: 2,
69                    txt: "Banana".to_string(),
70                },
71                MockEntity {
72                    id: 3,
73                    txt: "Lettuce".to_string(),
74                },
75            ],
76            false,
77        );
78
79        let first_result = tree.search("A");
80        assert_eq!(first_result.len(), 2);
81        println!("{:?}", first_result);
82        assert_eq!(first_result[0].id, 1);
83        assert_eq!(first_result[0].index, 0);
84        assert_eq!(first_result[1].id, 2);
85        assert_eq!(first_result[1].index, 1);
86
87        let second_result = tree.search("a");
88        assert_eq!(second_result.len(), 2);
89        println!("{:?}", first_result);
90        assert_eq!(second_result[0].id, 1);
91        assert_eq!(second_result[0].index, 0);
92        assert_eq!(second_result[1].id, 2);
93        assert_eq!(second_result[1].index, 1);
94    }
95}
96
97
98#[derive(Debug, Serialize, Deserialize)]
99pub struct MockEntity {
100    pub id: u32,
101    pub txt: String,
102}
103
104impl SearchableElement for MockEntity {
105    fn as_searchable_text(&self) -> String {
106        return self.txt.to_string();
107    }
108
109    fn get_id(&self) -> u32 {
110        return self.id;
111    }
112}
113
114
115
116pub trait SearchableElement {
117    fn as_searchable_text(&self) -> String;
118    fn get_id(&self) -> u32;
119}
120
121#[derive(Debug, Eq, PartialEq, PartialOrd)]
122pub struct SearchResult {
123    pub id: u32,
124    pub index: usize,
125}
126
127
128impl std::cmp::Ord for SearchResult {
129    fn cmp(&self, other: &Self) -> Ordering {
130        let idcmp = self.id.cmp(&other.id);
131        if idcmp == Ordering::Equal {
132            return self.index.cmp(&other.index);
133        } else {
134            return idcmp;
135        }
136    }
137}
138
139
140
141pub trait KDTree {
142    fn build<T: SearchableElement>(elements: &Vec<T>, case_sensitive: bool) -> Self;
143    fn search(&self, query: &str) -> Vec<SearchResult>;
144    fn is_case_sensitive(&self) -> bool;
145}
146
147#[derive(Debug, Serialize, Deserialize)]
148pub struct MockKDTree {
149    elements: HashMap<u32, String>,
150    case_sensitive: bool,
151}
152
153impl KDTree for MockKDTree {
154    fn build<T: SearchableElement>(elements: &Vec<T>, case_sensitive: bool) -> Self {
155        let mut tree = MockKDTree {
156            elements: HashMap::new(),
157            case_sensitive: case_sensitive,
158        };
159        for element in elements {
160            tree.elements.insert(
161                element.get_id(),
162                if case_sensitive {
163                    element.as_searchable_text()
164                } else {
165                    element.as_searchable_text().to_lowercase()
166                },
167            );
168        }
169        return tree;
170    }
171
172    fn search(&self, query: &str) -> Vec<SearchResult> {
173        let mut results: Vec<SearchResult> = Vec::new();
174
175        let query = if self.is_case_sensitive() {
176            query.to_string()
177        } else {
178            query.to_lowercase()
179        };
180
181        for (id, string) in &self.elements {
182            let index_opt = string.find(&query);
183
184            if let Some(idx) = index_opt {
185                results.push(SearchResult {
186                    id: *id,
187                    index: idx,
188                });
189            }
190        }
191        results.sort();
192        return results;
193    }
194    fn is_case_sensitive(&self) -> bool {
195        return self.case_sensitive;
196    }
197}