1use std::str;
2use chrono::NaiveDateTime;
3use sanakirja::{Commit, Env, Error, MutTxn};
4use sanakirja::btree;
5use crate::{invalid_data_error, StoreRw, ID_SQ, DB_DUE_DATES, DB_LINKS, DB_NAMES, DB_RLINKS, DB_RSESSIONS, DB_SESSIONS};
6use crate::reader::ChildIds;
7use crate::types::{DueDate, RTriple, Session};
8
9pub type StoreWriter<'env> = StoreRw<MutTxn<&'env Env, ()>>;
10
11impl<'env> StoreWriter<'env> {
12 pub fn add_child(&mut self, pid: u64, name: &str) -> Result<u64, Error> {
13 assert_ne!(name.len(), 0);
14 let next = self.get_child(pid)?.unwrap_or(0);
15
16 let id = self.id;
17 btree::del(&mut self.txn, &mut self.links, &pid, None)?;
18 btree::put(&mut self.txn, &mut self.links, &pid, &id)?;
19
20 btree::put(&mut self.txn, &mut self.rlinks, &id, &RTriple { pid, next, prev: 0 })?;
21 if next > 0 { self.modify_rt(next, pid, |rt| rt.prev = id)? };
22
23 btree::put(&mut self.txn, &mut self.names, &id, name.as_bytes())?;
24 self.id += 1;
25 Ok(id)
26 }
27
28 pub fn add_session(&mut self, id: u64, session: &Session) -> Result<(), Error> {
29 btree::put(&mut self.txn, &mut self.sessions, &id, session)?;
30 btree::put(&mut self.txn, &mut self.rsessions, session, &id)?;
31 Ok(())
32 }
33
34 pub fn delete(&mut self, pid: u64, id: u64) -> Result<(), Error> {
35 btree::del(&mut self.txn, &mut self.links, &pid, Some(&id))?;
36
37 let rt = self.get_rt(id, pid)?.ok_or_else(invalid_data_error)?;
38 if rt.prev > 0 { self.modify_rt(rt.prev, pid, |prt| prt.next = rt.next)?; }
39 else if rt.next > 0 { btree::put(&mut self.txn, &mut self.links, &pid, &rt.next)?; }
40 if rt.next > 0 { self.modify_rt(rt.next, pid, |nrt| nrt.prev = rt.prev)? };
41
42 self.delete_helper(pid, id)
43 }
44
45 pub fn delete_session(&mut self, id: u64, session: &Session) -> Result<(), Error> {
46 btree::del(&mut self.txn, &mut self.sessions, &id, Some(session))?;
47 btree::del(&mut self.txn, &mut self.rsessions, session, Some(&id))?;
48 Ok(())
49 }
50
51 pub fn rename(&mut self, id: u64, name: &str) -> Result<(), Error> {
52 btree::del(&mut self.txn, &mut self.names, &id, None)?;
53 btree::put(&mut self.txn, &mut self.names, &id, name.as_bytes())?;
54 Ok(())
55 }
56
57 pub fn set_due_date(&mut self, id: u64, date: NaiveDateTime) -> Result<(), Error> {
58 btree::del(&mut self.txn, &mut self.due_dates, &id, None)?;
59 btree::put(&mut self.txn, &mut self.due_dates, &id, &DueDate(date))?;
60 Ok(())
61 }
62
63 pub fn unset_due_date(&mut self, id: u64) -> Result<(), Error> {
64 btree::del(&mut self.txn, &mut self.due_dates, &id, None)?;
65 Ok(())
66 }
67
68 pub fn move_up(&mut self, pid: u64, id: u64) -> Result<(), Error> {
69 let rt = self.get_rt(id, pid)?.ok_or_else(invalid_data_error)?;
70 let prt = if rt.prev > 0 {
71 self.get_rt(rt.prev, pid)?.ok_or_else(invalid_data_error)?
72 } else { return Ok(()) };
73
74 self.modify_rt(id, pid, |crt| {
75 crt.next = rt.prev;
76 crt.prev = prt.prev;
77 })?;
78
79 if rt.next > 0 {
80 self.modify_rt(rt.next, pid, |nrt| nrt.prev = rt.prev)?;
81 }
82
83 self.modify_rt(rt.prev, pid, |prt| {
84 prt.next = rt.next;
85 prt.prev = id;
86 })?;
87
88 if prt.prev > 0 {
89 self.modify_rt(prt.prev, pid, |pprt| pprt.next = id)?;
90 } else {
91 btree::del(&mut self.txn, &mut self.links, &pid, None)?;
92 btree::put(&mut self.txn, &mut self.links, &pid, &id)?;
93 }
94
95 Ok(())
96 }
97
98 pub fn move_down(&mut self, pid: u64, id: u64) -> Result<(), Error> {
99 let rt = self.get_rt(id, pid)?.ok_or_else(invalid_data_error)?;
100 let nrt = if rt.next > 0 {
101 self.get_rt(rt.next, pid)?.ok_or_else(invalid_data_error)?
102 } else { return Ok(()) };
103
104 self.modify_rt(id, pid, |crt| {
105 crt.prev = rt.next;
106 crt.next = nrt.next;
107 })?;
108
109 if rt.prev > 0 {
110 self.modify_rt(rt.prev, pid, |prt| prt.next = rt.next)?;
111 } else {
112 btree::del(&mut self.txn, &mut self.links, &pid, None)?;
113 btree::put(&mut self.txn, &mut self.links, &pid, &rt.next)?;
114 }
115
116 self.modify_rt(rt.next, pid, |nrt| {
117 nrt.prev = rt.prev;
118 nrt.next = id;
119 })?;
120
121 if nrt.next > 0 {
122 self.modify_rt(nrt.next, pid, |nnrt| nnrt.prev = id)?;
123 }
124 Ok(())
125 }
126
127 pub fn share(&mut self, src: u64, dest: u64) -> Result<bool, Error> {
128 if self.is_descendent_of(dest, src)? { return Ok(false) };
129 if self.get_rt(src, dest)?.is_some() { return Ok(false) };
130
131 let next = self.get_child(dest)?.unwrap_or(0);
132
133 btree::del(&mut self.txn, &mut self.links, &dest, None)?;
134 btree::put(&mut self.txn, &mut self.links, &dest, &src)?;
135
136 btree::put(&mut self.txn, &mut self.rlinks, &src, &RTriple { pid: dest, next, prev: 0 })?;
137 if next > 0 { self.modify_rt(next, dest, |rt| rt.prev = src)? };
138
139 Ok(true)
140 }
141
142 pub fn cut(&mut self, src_pid: u64, src: u64, dest: u64) -> Result<bool, Error> {
143 if !self.share(src, dest)? { return Ok(false) };
144
145 let is_head = btree::del(&mut self.txn, &mut self.links, &src_pid, Some(&src))?;
146
147 let rt = self.get_rt(src, src_pid)?.ok_or_else(invalid_data_error)?;
148 if is_head { btree::put(&mut self.txn, &mut self.links, &src_pid, &rt.next)?; }
149 if rt.prev > 0 { self.modify_rt(rt.prev, src_pid, |prt| prt.next = rt.next)? };
150 if rt.next > 0 { self.modify_rt(rt.next, src_pid, |nrt| nrt.prev = rt.prev)? };
151 btree::del(&mut self.txn, &mut self.rlinks, &src, Some(&rt))?;
152
153 Ok(true)
154 }
155
156 pub fn commit(mut self) -> Result<(), Error> {
157 self.txn.set_root(ID_SQ, self.id);
158 self.txn.set_root(DB_LINKS, self.links.db.into());
159 self.txn.set_root(DB_RLINKS, self.rlinks.db.into());
160 self.txn.set_root(DB_NAMES, self.names.db.into());
161 self.txn.set_root(DB_DUE_DATES, self.due_dates.db.into());
162 self.txn.set_root(DB_SESSIONS, self.sessions.db.into());
163 self.txn.set_root(DB_RSESSIONS, self.rsessions.db.into());
164 self.txn.commit()
165 }
166
167 fn modify_rt(&mut self, id: u64, pid: u64, f: impl Fn(&mut RTriple)) -> Result<(), Error> {
168 let mut rt = self.get_rt(id, pid)?.ok_or_else(invalid_data_error)?;
169 btree::del(&mut self.txn, &mut self.rlinks, &id, Some(&rt))?;
170 f(&mut rt);
171 btree::put(&mut self.txn, &mut self.rlinks, &id, &rt)?;
172 Ok(())
173 }
174
175 fn delete_helper(&mut self, pid: u64, id: u64) -> Result<(), Error> {
176 let rt = self.get_rt(id, pid)?.ok_or_else(invalid_data_error)?;
177 btree::del(&mut self.txn, &mut self.rlinks, &id, Some(&rt))?;
178
179 if let Some((&eid, _)) = btree::get(&self.txn, &self.rlinks, &id, None)? {
180 if eid == id { return Ok(()) };
181 }
182
183 btree::del(&mut self.txn, &mut self.names, &id, None)?;
184 btree::del(&mut self.txn, &mut self.due_dates, &id, None)?;
185 self.delete_id_sessions(id)?;
186
187 let mut child_ids = ChildIds::new(self, id)?;
188 if let Some(first) = child_ids.next(self)? {
189 btree::del(&mut self.txn, &mut self.links, &id, Some(&first))?;
190 self.delete(id, first)?;
191 }
192 while let Some(child_id) = child_ids.next(self)? {
193 self.delete(id, child_id)?;
194 }
195 Ok(())
196 }
197
198 fn is_descendent_of(&self, subj: u64, pred: u64) -> Result<bool, Error> {
199 if subj == pred { return Ok(true) };
200 for child_id in self.child_ids(pred)? {
201 if self.is_descendent_of(subj, child_id?)? { return Ok(true) };
202 }
203 Ok(false)
204 }
205
206 fn delete_id_sessions(&mut self, id: u64) -> Result<(), Error> {
207 while let Some((&eid, &session)) = btree::get(&self.txn, &self.sessions, &id, None)? {
208 if eid != id { break };
209 self.delete_session(id, &session)?;
210 }
211 Ok(())
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
218 use sanakirja::{Env, Error};
219 use crate::Store;
220 use crate::types::Session;
221
222 #[test]
223 fn add_child() -> Result<(), Error> {
224 let store = Store { env: Env::new_anon(1 << 14, 2)? };
225 store.create_base()?;
226
227 let mut writer = store.writer()?;
228 writer.add_child(0, "first")?;
229 writer.add_child(1, "second")?;
230 writer.commit()?;
231
232 let reader = store.reader()?;
233 let mut iter = reader.child_ids(0)?;
234
235 let id = iter.next().unwrap()?;
236 let data = reader.name(id)?.unwrap();
237 assert_eq!(id, 1);
238 assert_eq!(data, "first");
239 assert!(iter.next().is_none());
240
241 let mut iter = reader.child_ids(1)?;
242 let id = iter.next().unwrap()?;
243 let data = reader.name(id)?.unwrap();
244 assert_eq!(id, 2);
245 assert_eq!(data, "second");
246 assert!(iter.next().is_none());
247
248 Ok(())
249 }
250
251 #[test]
252 fn add_session() -> Result<(), Error> {
253 let store = Store { env: Env::new_anon(1 << 14, 2)? };
254 store.create_base()?;
255
256 let mut writer = store.writer()?;
257 let start = NaiveDateTime::new(
258 NaiveDate::from_ymd_opt(2023, 1, 1).unwrap(),
259 NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
260 );
261 let end = NaiveDateTime::new(
262 NaiveDate::from_ymd_opt(2023, 1, 1).unwrap(),
263 NaiveTime::from_hms_opt(10, 0, 0).unwrap(),
264 );
265 let session = Session { start, end };
266 writer.add_session(0, &session)?;
267 writer.commit()?;
268
269 let reader = store.reader()?;
270 assert_eq!(reader.first_session(0)?.unwrap(), session);
271
272 Ok(())
273 }
274}