grus_lib/
writer.rs

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}