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}