1pub mod reader;
2pub mod types;
3pub mod writer;
4
5pub use sanakirja::Error;
6use std::io::{Error as IoError, ErrorKind};
7use std::path::Path;
8use sanakirja::{Commit, Env, LoadPage, RootDb};
9use sanakirja::btree;
10use reader::StoreReader;
11use types::{DueDatesDb, LinksDb, NamesDb, RLinksDb, RSessionsDb, SessionsDb};
12use writer::StoreWriter;
13
14const ID_SQ: usize = 0;
15const DB_LINKS: usize = 1;
16const DB_RLINKS: usize = 2;
17const DB_NAMES: usize = 3;
18const DB_DUE_DATES: usize = 4;
19const DB_SESSIONS: usize = 5;
20const DB_RSESSIONS: usize = 6;
21
22pub struct Store {
23 env: Env
24}
25
26impl Store {
27 pub fn open<P: AsRef<Path>>(path: P, n_roots: usize) -> Result<Self, Error> {
28 let store = Store { env: Env::new(path, 1 << 14, n_roots)? };
29 store.create_base()?;
30 Ok(store)
31 }
32
33 pub fn reader(&self) -> Result<StoreReader, Error> {
34 let txn = Env::txn_begin(&self.env)?;
35 let id = txn.root(ID_SQ);
36 let links = txn.root_db(DB_LINKS).ok_or_else(invalid_data_error)?;
37 let rlinks = txn.root_db(DB_RLINKS).ok_or_else(invalid_data_error)?;
38 let names = txn.root_db(DB_NAMES).ok_or_else(invalid_data_error)?;
39 let due_dates = txn.root_db(DB_DUE_DATES).ok_or_else(invalid_data_error)?;
40 let sessions = txn.root_db(DB_SESSIONS).ok_or_else(invalid_data_error)?;
41 let rsessions = txn.root_db(DB_RSESSIONS).ok_or_else(invalid_data_error)?;
42 Ok(StoreReader { txn, id, links, rlinks, names, due_dates, sessions, rsessions })
43 }
44
45 pub fn writer(&self) -> Result<StoreWriter, Error> {
46 let txn = Env::mut_txn_begin(&self.env)?;
47 let id = txn.root(ID_SQ).ok_or_else(invalid_data_error)?;
48 let links = txn.root_db(DB_LINKS).ok_or_else(invalid_data_error)?;
49 let rlinks = txn.root_db(DB_RLINKS).ok_or_else(invalid_data_error)?;
50 let names = txn.root_db(DB_NAMES).ok_or_else(invalid_data_error)?;
51 let due_dates = txn.root_db(DB_DUE_DATES).ok_or_else(invalid_data_error)?;
52 let sessions = txn.root_db(DB_SESSIONS).ok_or_else(invalid_data_error)?;
53 let rsessions = txn.root_db(DB_RSESSIONS).ok_or_else(invalid_data_error)?;
54 Ok(StoreWriter { txn, id, links, rlinks, names, due_dates, sessions, rsessions })
55 }
56
57 fn create_base(&self) -> Result<(), Error> {
58 let mut txn = Env::mut_txn_begin(&self.env)?;
59
60 let id = txn.root(ID_SQ);
61 let links: Option<LinksDb> = txn.root_db(DB_LINKS);
62 let rlinks: Option<RLinksDb> = txn.root_db(DB_RLINKS);
63 let names: Option<NamesDb> = txn.root_db(DB_NAMES);
64 let due_dates: Option<DueDatesDb> = txn.root_db(DB_DUE_DATES);
65 let sessions: Option<SessionsDb> = txn.root_db(DB_SESSIONS);
66 let rsessions: Option<RSessionsDb> = txn.root_db(DB_RSESSIONS);
67 match (id, links, rlinks, names, due_dates, sessions, rsessions) {
68 (Some(_), Some(_), Some(_), Some(_), Some(_), Some(_), Some(_)) => Ok(()),
69 (None, None, None, None, None, None, None) => {
70 unsafe {
71 let links: LinksDb = btree::create_db(&mut txn)?;
72 let rlinks: RLinksDb = btree::create_db(&mut txn)?;
73 let mut names: NamesDb = btree::create_db_(&mut txn)?;
74 let due_dates: DueDatesDb = btree::create_db(&mut txn)?;
75 let sessions: SessionsDb = btree::create_db(&mut txn)?;
76 let rsessions: RSessionsDb = btree::create_db(&mut txn)?;
77
78 btree::put(&mut txn, &mut names, &0, b"/")?;
79
80 txn.set_root(ID_SQ, 1);
81 txn.set_root(DB_LINKS, links.db.into());
82 txn.set_root(DB_RLINKS, rlinks.db.into());
83 txn.set_root(DB_NAMES, names.db.into());
84 txn.set_root(DB_DUE_DATES, due_dates.db.into());
85 txn.set_root(DB_SESSIONS, sessions.db.into());
86 txn.set_root(DB_RSESSIONS, rsessions.db.into());
87 }
88 txn.commit()
89 }
90 _ => {
91 Err(invalid_data_error())
92 }
93 }
94 }
95}
96
97
98pub struct StoreRw<T: LoadPage> {
99 txn: T,
100 id: u64,
101 links: LinksDb,
102 rlinks: RLinksDb,
103 names: NamesDb,
104 due_dates: DueDatesDb,
105 sessions: SessionsDb,
106 rsessions: RSessionsDb,
107}
108
109fn invalid_data_error() -> Error {
110 Error::IO(IoError::new(
111 ErrorKind::InvalidData,
112 "Database is invalid or corrupted"
113 ))
114}