libzettels/backstage/querying/
mod.rs1use std::path::{PathBuf};
23use std::collections::{HashSet, BTreeMap};
24use backstage::index::Index;
25use backstage::zettel::Zettel;
26pub mod sequences;
27
28pub fn get_keywords(index: &Index) -> HashSet<String> {
37 let mut keywords = HashSet::new();
38 for (_, zettel) in &index.files {
39 for keyword in &zettel.keywords {
40 keywords.insert(keyword.to_string());
41 }
42 }
43 keywords
44}
45
46pub fn count_keywords(index: &Index) -> BTreeMap<String, u32> {
50 let mut counted_keywords = BTreeMap::new();
51 for (_, zettel) in &index.files {
52 for keyword in &zettel.keywords {
53 if let Some(count) = counted_keywords.get(keyword) {
54 let new_count = count + 1;
55 counted_keywords.insert(keyword.to_string(), new_count);
56 } else {
57 counted_keywords.insert(keyword.to_string(), 1);
58 }
59 }
60 }
61 counted_keywords
62}
63
64pub fn search_index_for_keywords(index: &Index,
69 searched_keywords: Vec<String>,
70 all: bool) -> HashSet<PathBuf> {
71 let mut matching_zettels = HashSet::new();
72 for (key, zettel) in &index.files {
73 if search_zettel_for_keywords(&zettel, &searched_keywords, all) {
74 matching_zettels.insert(key.to_path_buf());
75 }
76 }
77 matching_zettels
78}
79
80fn search_zettel_for_keywords(zettel: &Zettel,
85 searched_keywords: &Vec<String>,
86 all: bool) -> bool {
87 if all {
88 for keyword in searched_keywords {
90 if !zettel.keywords.contains(&keyword) {
92 return false;
93 }
94 }
95 return true;
97 } else {
98 for keyword in searched_keywords {
100 if zettel.keywords.contains(&keyword) {
102 return true;
103 }
104 }
105 return false;
107 }
108}
109
110pub fn search_index_for_title<T: AsRef<str>>(index: &Index,
115 search_term: T,
116 exact: bool) -> HashSet<PathBuf> {
117 let mut matching_zettels = HashSet::new();
118 for (key, zettel) in &index.files {
119 if search_zettel_title(&zettel, &search_term, exact) {
120 matching_zettels.insert(key.to_path_buf());
121 }
122 }
123 matching_zettels
124}
125
126fn search_zettel_title<T: AsRef<str>>(zettel: &Zettel,
130 search_term: T,
131 exact: bool) -> bool {
132 let search_term = search_term.as_ref();
133 let qs = search_term;
134 let zt = zettel.title.as_str();
135
136 if exact {
137 trace!("exact. Returning {:?}", zt==qs);
138 zt == qs
139 } else {
140 let qs = qs.to_ascii_lowercase();
141 let zt = zt.to_ascii_lowercase();
142 zt.contains(&qs)
143 }
144}
145
146
147pub fn combi_search<T: AsRef<str>>(index: &Index,
150 searched_keywords: Vec<String>,
151 all: bool,
152 search_term: T,
153 exact: bool)
154 -> HashSet<PathBuf> {
155 let mut k_result = search_index_for_keywords(&index,
156 searched_keywords,
157 all);
158 let t_result = search_index_for_title(&index, search_term, exact);
159
160 if all {
162 let mut temp = HashSet::new();
164 for r in k_result {
165 if t_result.contains(&r) {
166 temp.insert(r);
167 }
168 }
169 k_result = temp;
170 } else {
171 for r in t_result {
173 k_result.insert(r);
174 }
175 }
176 k_result
177}
178pub fn inspect_incoming(index: &Index,
201 inspected_zettels: &HashSet<PathBuf>,
202 all: bool)
203 -> HashSet<PathBuf> {
204 let mut incoming = HashSet::new();
205 for (other_key, other_zettel) in &index.files {
207 if other_zettel.links_to(&inspected_zettels, all) {
208 incoming.insert(other_key.to_path_buf());
209 }
210 }
211 incoming
212}
213
214#[cfg(test)]
216mod tests {
217 use super::*;
218 extern crate tempfile;
219 use self::tempfile::tempdir;
220 use examples::*;
221 use std::path::Path;
222
223 fn setup() -> Index {
224 let tmp_dir = tempdir().expect("Failed setting up temp directory.");
225 let dir = tmp_dir.path();
226 generate_examples_with_index(dir)
227 .expect("Failed to generate examples");
228 let file_path = dir.join("examples/config/index.yaml");
229 let index = Index::from_file(&file_path).unwrap();
230 index
231 }
232
233 #[test]
234 fn test_search_zettel_for_keywords() {
235 let index = setup();
237 let z1 = index.get_zettel(Path::new("file1.md"));
238 assert!(z1.is_some());
239 let z1 = z1.unwrap();
240 let searched_keywords = vec!["example".to_string()];
245 assert!(search_zettel_for_keywords(&z1, &searched_keywords, false));
246
247 let searched_keywords = vec!["foo".to_string()];
249 assert!(!search_zettel_for_keywords(&z1, &searched_keywords, false));
250
251 let searched_keywords = vec!["example".to_string(),
253 "foo".to_string()];
254 assert!(search_zettel_for_keywords(&z1, &searched_keywords, false));
255
256 let searched_keywords = vec!["foo".to_string(),
258 "bar".to_string()];
259 assert!(!search_zettel_for_keywords(&z1, &searched_keywords, false));
260
261 let searched_keywords = vec!["example".to_string(),
263 "foo".to_string()];
264 assert!(!search_zettel_for_keywords(&z1, &searched_keywords, true));
265
266 let searched_keywords = vec!["example".to_string(),
268 "first".to_string()];
269 assert!(search_zettel_for_keywords(&z1, &searched_keywords, true));
270
271 let searched_keywords = vec!["example".to_string(),
273 "first".to_string(),
274 "foo".to_string()];
275 assert!(search_zettel_for_keywords(&z1, &searched_keywords, false));
276
277 let searched_keywords = vec!["example".to_string(),
279 "first".to_string(),
280 "foo".to_string()];
281 assert!(!search_zettel_for_keywords(&z1, &searched_keywords, true));
282 }
283
284 #[test]
285 fn test_get_keywords() {
286 let index = setup();
287
288 let keywords = get_keywords(&index);
289 assert_eq!(keywords.len(), 7);
290 assert!(keywords.contains("example"));
291 assert!(keywords.contains("test"));
292 assert!(keywords.contains("first"));
293 assert!(keywords.contains("second"));
294 assert!(keywords.contains("third"));
295 assert!(keywords.contains("fourth"));
296 assert!(keywords.contains("pureyaml"));
297 }
298
299 #[test]
300 fn test_count_keywords() {
301 let index = setup();
302
303 let keyword_count = count_keywords(&index);
304 assert_eq!(Some(&5), keyword_count.get("example"));
305 assert_eq!(Some(&1), keyword_count.get("test"));
306 assert_eq!(Some(&1), keyword_count.get("first"));
307 assert_eq!(Some(&1), keyword_count.get("second"));
308 assert_eq!(Some(&1), keyword_count.get("third"));
309 assert_eq!(Some(&1), keyword_count.get("fourth"));
310 assert_eq!(Some(&1), keyword_count.get("pureyaml"));
311 }
312
313 #[test]
314 fn test_search_index_for_keywords() {
315 let index = setup();
316
317 let searched_keywords = vec!["first".to_string()];
319 let matching_zettels = search_index_for_keywords(&index,
320 searched_keywords,
321 false);
322 assert_eq!(matching_zettels.len(), 1);
323 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
324
325 let searched_keywords = vec!["first".to_string(),
327 "foo".to_string()];
328 let matching_zettels = search_index_for_keywords(&index,
329 searched_keywords,
330 false);
331 assert_eq!(matching_zettels.len(), 1);
332 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
333
334 let searched_keywords = vec!["first".to_string(),
336 "foo".to_string()];
337 let matching_zettels = search_index_for_keywords(&index,
338 searched_keywords,
339 true);
340 assert_eq!(matching_zettels.len(), 0);
341
342
343 let searched_keywords = vec!["first".to_string(),
345 "second".to_string()];
346 let matching_zettels = search_index_for_keywords(&index,
347 searched_keywords,
348 false);
349 assert_eq!(matching_zettels.len(), 2);
350 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
351 assert!(matching_zettels.contains(&PathBuf::from("file2.md")));
352
353 let searched_keywords = vec!["first".to_string(),
355 "second".to_string()];
356 let matching_zettels = search_index_for_keywords(&index,
357 searched_keywords,
358 true);
359 assert_eq!(matching_zettels.len(), 0);
360
361 let searched_keywords = vec!["first".to_string(),
363 "example".to_string()];
364 let matching_zettels = search_index_for_keywords(&index,
365 searched_keywords,
366 true);
367 assert_eq!(matching_zettels.len(), 1);
368 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
369 }
370
371 #[test]
372 fn test_search_zettel_title() {
373 let index = setup();
375 let z1 = index.get_zettel(Path::new("file1.md"));
376 assert!(z1.is_some());
377 let z1 = z1.unwrap();
378
379 assert!(search_zettel_title(&z1, "File 1", false));
381
382 assert!(search_zettel_title(&z1, "File 1", true));
384
385 assert!(search_zettel_title(&z1, "File", false));
387
388 assert!(!search_zettel_title(&z1, "File", true));
390
391 assert!(!search_zettel_title(&z1, "foo", false));
393
394 assert!(!search_zettel_title(&z1, "foo", true));
396 }
397
398 #[test]
399 fn test_search_index_for_title() {
400 let index = setup();
401
402 let matching_zettels = search_index_for_title(&index, "File 1", false);
404 assert_eq!(matching_zettels.len(), 1);
405 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
406
407 let matching_zettels = search_index_for_title(&index, "File 1", true);
409 assert_eq!(matching_zettels.len(), 1);
410 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
411
412 let matching_zettels = search_index_for_title(&index, "File", false);
414 assert_eq!(matching_zettels.len(), 5);
415 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
416 assert!(matching_zettels.contains(&PathBuf::from("file2.md")));
417 assert!(matching_zettels.contains(&PathBuf::from("file3.md")));
418 assert!(matching_zettels.contains(&PathBuf::from("subdir/file4.md")));
419 assert!(matching_zettels.contains(&PathBuf::from("subdir/file5.md")));
420
421 let matching_zettels = search_index_for_title(&index, "File", true);
423 assert_eq!(matching_zettels.len(), 0);
424
425 let matching_zettels = search_index_for_title(&index, "foo", false);
427 assert_eq!(matching_zettels.len(), 0);
428
429 let matching_zettels = search_index_for_title(&index, "foo", true);
431 assert_eq!(matching_zettels.len(), 0);
432 }
433
434 #[test]
435 fn test_combi_search() {
436 let index = setup();
437
438 let searched_keywords = vec!["first".to_string()];
439
440 let matching_zettels = combi_search(&index,
441 searched_keywords.clone(),
442 true,
443 "File",
444 true);
445 assert_eq!(matching_zettels.len(), 0);
446
447 let matching_zettels = combi_search(&index,
448 searched_keywords.clone(),
449 true,
450 "File",
451 false);
452 assert_eq!(matching_zettels.len(), 1);
453 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
454
455 let matching_zettels = combi_search(&index,
456 searched_keywords,
457 true,
458 "File 1",
459 true);
460 assert_eq!(matching_zettels.len(), 1);
461 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
462
463 let searched_keywords = vec!["example".to_string()];
464
465 let matching_zettels = combi_search(&index,
466 searched_keywords.clone(),
467 true,
468 "File 1",
469 true);
470 assert_eq!(matching_zettels.len(), 1);
471 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
472
473 let matching_zettels = combi_search(&index,
474 searched_keywords,
475 false,
476 "File 1",
477 true);
478 assert_eq!(matching_zettels.len(), 5);
479 assert!(matching_zettels.contains(&PathBuf::from("file1.md")));
480 assert!(matching_zettels.contains(&PathBuf::from("file2.md")));
481 assert!(matching_zettels.contains(&PathBuf::from("file3.md")));
482 assert!(matching_zettels.contains(&PathBuf::from("subdir/file4.md")));
483 assert!(matching_zettels.contains(&PathBuf::from("onlies/pure-yaml.yaml")));
484 }
485
486 #[test]
487 fn test_inspect_incoming() {
488 let index = setup();
490 let mut inspected_zettels = HashSet::new();
491 inspected_zettels.insert(PathBuf::from("file2.md"));
492 inspected_zettels.insert(PathBuf::from("file3.md"));
493
494 let incoming = inspect_incoming(&index, &inspected_zettels, false);
501 assert_eq!(incoming.len(), 3);
502 assert!(incoming.contains(&PathBuf::from("file1.md")));
503 assert!(incoming.contains(&PathBuf::from("file2.md")));
504 assert!(incoming.contains(&PathBuf::from("onlies/markdown-only.md")));
505
506 let incoming = inspect_incoming(&index, &inspected_zettels, true);
508 assert_eq!(incoming.len(), 1);
509 assert!(incoming.contains(&PathBuf::from("file1.md")));
510
511 let mut inspected_zettels = HashSet::new();
513 inspected_zettels.insert(PathBuf::from("file2.md"));
514 inspected_zettels.insert(PathBuf::from("file42.md"));
515
516
517 let incoming = inspect_incoming(&index, &inspected_zettels, false);
519 assert_eq!(incoming.len(), 2);
520 assert!(incoming.contains(&PathBuf::from("file1.md")));
521 assert!(incoming.contains(&PathBuf::from("onlies/markdown-only.md")));
522
523 let incoming = inspect_incoming(&index, &inspected_zettels, true);
525 assert_eq!(incoming.len(), 0);
526 }
527}