diary_cli/moc/
collection.rs

1use soulog::*;
2use lazy_db::*;
3use crate::entry::*;
4use toml::{Table, Value};
5
6// Some ease of life macros
7macro_rules! get {
8    ($key:ident at ($moc:ident, $idx:ident) from $table:ident as $func:ident with $logger:ident) => {{
9        let key = stringify!($key);
10        let obj = unwrap_opt!(($table.get(key)) with $logger, format: Collection("moc '{0}', collection {1} must have '{key}' attribute", $moc, $idx));
11
12        unwrap_opt!((obj.$func()) with $logger, format: Collection("moc '{0}', collection {1}'s '{key}' attribute must be of the correct type", $moc, $idx))
13    }};
14
15    ($var:ident = $key:ident at ($moc:ident, $idx:ident) from $table:ident as $func:ident with $logger:ident or $default:expr) => {
16        let key = stringify!($key);
17        let default = $default;
18        let $var = $table.get(key)
19            .map(|x| unwrap_opt!((x.$func()) with $logger, format: Collection("moc '{0}'s '{key}' attribute must be of the correct type", $moc)))
20            .unwrap_or(&default);
21    };
22}
23
24pub struct Collection {
25    pub container: LazyContainer,
26    pub title: Option<String>,
27    pub notes: Option<Box<[String]>>,
28    pub include: Option<Box<[String]>>,
29}
30
31impl Collection {
32    pub fn new(table: &Table, container: LazyContainer, moc: &str, idx: u8, mut logger: impl Logger) -> Self {
33        log!((logger) Collection("Parsing moc '{moc}'s collection {idx}..."));
34
35        // Get the basic needed data
36        log!((logger) Collection("Reading collection's data..."));
37        let title = get!(title at (moc, idx) from table as as_str with logger).to_string();
38        get!(raw_notes = notes at (moc, idx) from table as as_array with logger or Vec::<toml::Value>::with_capacity(0));
39        let raw_include = get!(include at (moc, idx) from table as as_array with logger);
40
41        // Parse arrays
42        unpack_array!(notes from raw_notes with logger by x
43            => unwrap_opt!((x.as_str()) with logger, format: Collection("All notes in moc '{moc}', collection '{idx}' must be strings")).to_string()
44        );
45
46        unpack_array!(include from raw_include with logger by x
47            => unwrap_opt!((x.as_str()) with logger, format: Collection("All included groups in moc '{moc}', collection '{idx}' must be strings")).to_string()
48        );
49
50        log!((logger) Collection("Writing moc '{moc}'s collection {idx} into archive..."));
51        let mut this = Self {
52            container,
53            title: Some(title),
54            notes: Some(notes.into_boxed_slice()),
55            include: Some(include.into_boxed_slice()),
56        };
57
58        this.store_lazy(logger.hollow());
59        this.clear_cache();
60        log!((logger) Collection("Successfully parsed and written moc's collection {idx} into archive"));
61        log!((logger) Collection("")); // spacer
62        this
63    }
64
65    pub fn pull(&mut self, logger: impl Logger) -> Table {
66        let mut map = Table::new();
67
68        // Insert title and notes
69        map.insert("title".into(), Value::String(self.title(logger.hollow()).clone()));
70        map.insert("notes".into(), self.notes(logger.hollow()).to_vec().into());
71        map.insert("include".into(), self.include(logger.hollow()).to_vec().into());
72
73        self.clear_cache();
74
75        map
76    }
77
78    pub fn store_lazy(&self, mut logger: impl Logger) {
79        // Only store them if they are accessed (maybe modified)
80        if let Some(x) = &self.title { write_db_container!(Collection(self.container) title = new_string(x) with logger); }
81        if let Some(x) = &self.notes {
82            list::write(
83                x.as_ref(),
84                |file, data| LazyData::new_string(file, data),
85                &if_err!((logger) [Collection, err => ("While writing collection's notes to archive: {:?}", err)] retry self.container.new_container("notes")),
86                logger.hollow()
87            );
88        }
89        if let Some(x) = &self.include {
90            list::write(
91                x.as_ref(),
92                |file, data| LazyData::new_string(file, data),
93                &if_err!((logger) [Collection, err => ("While writing collection's included groups to archive: {:?}", err)] retry self.container.new_container("include")),
94                logger
95            );
96        }
97    }
98
99    pub fn load_lazy(container: LazyContainer) -> Self {
100        Self {
101            container,
102            title: None,
103            notes: None,
104            include: None,
105        }
106    }
107
108    pub fn clear_cache(&mut self) {
109        self.title = None;
110        self.notes = None;
111        self.include = None;
112    }
113
114    pub fn fill_cache(&mut self, logger: impl Logger) {
115        self.title(logger.hollow());
116        self.include(logger.hollow());
117        self.notes(logger.hollow());
118    }
119
120    cache_field!(notes(this, logger) -> Box<[String]> {
121        list::read(
122            |data| data.collect_string(),
123            &if_err!((logger) [Collection, err => ("While reading from collection's notes: {err:?}")] retry this.container.child_container("notes")),
124            logger
125        )
126    });
127
128    cache_field!(title(this, logger) -> String {
129        read_db_container!(title from Collection(this.container) as collect_string with logger)
130    });
131
132    cache_field!(include(this, logger) -> Box<[String]> {
133        list::read(
134            |data| data.collect_string(),
135            &if_err!((logger) [Collection, err => ("While reading from collection's included groups: {err:?}")] retry this.container.child_container("include")),
136            logger
137        )
138    });
139}