kitchen_fridge/utils/
mod.rs

1//! Some utility functions
2
3use std::collections::{HashMap, HashSet};
4use std::sync::{Arc, Mutex};
5use std::hash::Hash;
6use std::io::{stdin, stdout, Read, Write};
7
8use minidom::Element;
9use url::Url;
10
11use crate::traits::CompleteCalendar;
12use crate::traits::DavCalendar;
13use crate::Item;
14use crate::item::SyncStatus;
15
16/// Walks an XML tree and returns every element that has the given name
17pub fn find_elems<S: AsRef<str>>(root: &Element, searched_name: S) -> Vec<&Element> {
18    let searched_name = searched_name.as_ref();
19    let mut elems: Vec<&Element> = Vec::new();
20
21    for el in root.children() {
22        if el.name() == searched_name {
23            elems.push(el);
24        } else {
25            let ret = find_elems(el, searched_name);
26            elems.extend(ret);
27        }
28    }
29    elems
30}
31
32/// Walks an XML tree until it finds an elements with the given name
33pub fn find_elem<S: AsRef<str>>(root: &Element, searched_name: S) -> Option<&Element> {
34    let searched_name = searched_name.as_ref();
35    if root.name() == searched_name {
36        return Some(root);
37    }
38
39    for el in root.children() {
40        if el.name() == searched_name {
41            return Some(el);
42        } else {
43            let ret = find_elem(el, searched_name);
44            if ret.is_some() {
45                return ret;
46            }
47        }
48    }
49    None
50}
51
52
53pub fn print_xml(element: &Element) {
54    let mut writer = std::io::stdout();
55
56    let mut xml_writer = minidom::quick_xml::Writer::new_with_indent(
57        std::io::stdout(),
58        0x20, 4
59    );
60    let _ = element.to_writer(&mut xml_writer);
61    let _ = writer.write(&[0x0a]);
62}
63
64/// A debug utility that pretty-prints calendars
65pub async fn print_calendar_list<C>(cals: &HashMap<Url, Arc<Mutex<C>>>)
66where
67    C: CompleteCalendar,
68{
69    for (url, cal) in cals {
70        println!("CAL {} ({})", cal.lock().unwrap().name(), url);
71        match cal.lock().unwrap().get_items().await {
72            Err(_err) => continue,
73            Ok(map) => {
74                for (_, item) in map {
75                    print_task(item);
76                }
77            },
78        }
79    }
80}
81
82/// A debug utility that pretty-prints calendars
83pub async fn print_dav_calendar_list<C>(cals: &HashMap<Url, Arc<Mutex<C>>>)
84where
85    C: DavCalendar,
86{
87    for (url, cal) in cals {
88        println!("CAL {} ({})", cal.lock().unwrap().name(), url);
89        match cal.lock().unwrap().get_item_version_tags().await {
90            Err(_err) => continue,
91            Ok(map) => {
92                for (url, version_tag) in map {
93                    println!("    * {} (version {:?})", url, version_tag);
94                }
95            },
96        }
97    }
98}
99
100pub fn print_task(item: &Item) {
101    match item {
102        Item::Task(task) => {
103            let completion = if task.completed() { "✓" } else { " " };
104            let sync = match task.sync_status() {
105                SyncStatus::NotSynced => ".",
106                SyncStatus::Synced(_) => "=",
107                SyncStatus::LocallyModified(_) => "~",
108                SyncStatus::LocallyDeleted(_) =>  "x",
109            };
110            println!("    {}{} {}\t{}", completion, sync, task.name(), task.url());
111        },
112        _ => return,
113    }
114}
115
116
117/// Compare keys of two hashmaps for equality
118pub fn keys_are_the_same<T, U, V>(left: &HashMap<T, U>, right: &HashMap<T, V>) -> bool
119where
120    T: Hash + Eq + Clone + std::fmt::Display,
121{
122    if left.len() != right.len() {
123        log::debug!("Count of keys mismatch: {} and {}", left.len(), right.len());
124        return false;
125    }
126
127    let keys_l: HashSet<T> = left.keys().cloned().collect();
128    let keys_r: HashSet<T> = right.keys().cloned().collect();
129    let result = keys_l == keys_r;
130    if result == false {
131        log::debug!("Keys of a map mismatch");
132        for key in keys_l {
133            log::debug!("   left: {}", key);
134        }
135        log::debug!("RIGHT:");
136        for key in keys_r {
137            log::debug!("  right: {}", key);
138        }
139    }
140    result
141}
142
143
144/// Wait for the user to press enter
145pub fn pause() {
146    let mut stdout = stdout();
147    stdout.write_all(b"Press Enter to continue...").unwrap();
148    stdout.flush().unwrap();
149    stdin().read_exact(&mut [0]).unwrap();
150}
151
152
153/// Generate a random URL with a given prefix
154pub fn random_url(parent_calendar: &Url) -> Url {
155    let random = uuid::Uuid::new_v4().to_hyphenated().to_string();
156    parent_calendar.join(&random).unwrap(/* this cannot panic since we've just created a string that is a valid URL */)
157}