grus-lib 0.1.1

Library for accessing grus database
Documentation
pub mod reader;
pub mod types;
pub mod writer;

pub use sanakirja::Error;
use std::io::{Error as IoError, ErrorKind};
use std::path::Path;
use sanakirja::{Commit, Env, LoadPage, RootDb};
use sanakirja::btree;
use reader::StoreReader;
use types::{DueDatesDb, LinksDb, NamesDb, RLinksDb, RSessionsDb, SessionsDb};
use writer::StoreWriter;

const ID_SQ: usize = 0;
const DB_LINKS: usize = 1;
const DB_RLINKS: usize = 2;
const DB_NAMES: usize = 3;
const DB_DUE_DATES: usize = 4;
const DB_SESSIONS: usize = 5;
const DB_RSESSIONS: usize = 6;

pub struct Store {
	env: Env
}

impl Store {
	pub fn open<P: AsRef<Path>>(path: P, n_roots: usize) -> Result<Self, Error> {
		let store = Store { env: Env::new(path, 1 << 14, n_roots)? };
		store.create_base()?;
		Ok(store)
	}

	pub fn reader(&self) -> Result<StoreReader, Error> {
		let txn = Env::txn_begin(&self.env)?;
		let id = txn.root(ID_SQ);
		let links = txn.root_db(DB_LINKS).ok_or_else(invalid_data_error)?;
		let rlinks = txn.root_db(DB_RLINKS).ok_or_else(invalid_data_error)?;
		let names = txn.root_db(DB_NAMES).ok_or_else(invalid_data_error)?;
		let due_dates = txn.root_db(DB_DUE_DATES).ok_or_else(invalid_data_error)?;
		let sessions = txn.root_db(DB_SESSIONS).ok_or_else(invalid_data_error)?;
		let rsessions = txn.root_db(DB_RSESSIONS).ok_or_else(invalid_data_error)?;
		Ok(StoreReader { txn, id, links, rlinks, names, due_dates, sessions, rsessions })
	}

	pub fn writer(&self) -> Result<StoreWriter, Error> {
		let txn = Env::mut_txn_begin(&self.env)?;
		let id = txn.root(ID_SQ).ok_or_else(invalid_data_error)?;
		let links = txn.root_db(DB_LINKS).ok_or_else(invalid_data_error)?;
		let rlinks = txn.root_db(DB_RLINKS).ok_or_else(invalid_data_error)?;
		let names = txn.root_db(DB_NAMES).ok_or_else(invalid_data_error)?;
		let due_dates = txn.root_db(DB_DUE_DATES).ok_or_else(invalid_data_error)?;
		let sessions = txn.root_db(DB_SESSIONS).ok_or_else(invalid_data_error)?;
		let rsessions = txn.root_db(DB_RSESSIONS).ok_or_else(invalid_data_error)?;
		Ok(StoreWriter { txn, id, links, rlinks, names, due_dates, sessions, rsessions })
	}

	fn create_base(&self) -> Result<(), Error> {
		let mut txn = Env::mut_txn_begin(&self.env)?;

		let id = txn.root(ID_SQ);
		let links: Option<LinksDb> = txn.root_db(DB_LINKS);
		let rlinks: Option<RLinksDb> = txn.root_db(DB_RLINKS);
		let names: Option<NamesDb> = txn.root_db(DB_NAMES);
		let due_dates: Option<DueDatesDb> = txn.root_db(DB_DUE_DATES);
		let sessions: Option<SessionsDb> = txn.root_db(DB_SESSIONS);
		let rsessions: Option<RSessionsDb> = txn.root_db(DB_RSESSIONS);
		match (id, links, rlinks, names, due_dates, sessions, rsessions) {
			(Some(_), Some(_), Some(_), Some(_), Some(_), Some(_), Some(_)) => Ok(()),
			(None, None, None, None, None, None, None) => {
				unsafe {
					let links: LinksDb = btree::create_db(&mut txn)?;
					let rlinks: RLinksDb = btree::create_db(&mut txn)?;
					let mut names: NamesDb = btree::create_db_(&mut txn)?;
					let due_dates: DueDatesDb = btree::create_db(&mut txn)?;
					let sessions: SessionsDb = btree::create_db(&mut txn)?;
					let rsessions: RSessionsDb = btree::create_db(&mut txn)?;

					btree::put(&mut txn, &mut names, &0, b"/")?;

					txn.set_root(ID_SQ, 1);
					txn.set_root(DB_LINKS, links.db.into());
					txn.set_root(DB_RLINKS, rlinks.db.into());
					txn.set_root(DB_NAMES, names.db.into());
					txn.set_root(DB_DUE_DATES, due_dates.db.into());
					txn.set_root(DB_SESSIONS, sessions.db.into());
					txn.set_root(DB_RSESSIONS, rsessions.db.into());
				}
				txn.commit()
			}
			_ => {
				Err(invalid_data_error())
			}
		}
	}
}


pub struct StoreRw<T: LoadPage> {
	txn: T,
	id: u64,
	links: LinksDb,
	rlinks: RLinksDb,
	names: NamesDb,
	due_dates: DueDatesDb,
	sessions: SessionsDb,
	rsessions: RSessionsDb,
}

fn invalid_data_error() -> Error {
	Error::IO(IoError::new(
		ErrorKind::InvalidData,
		"Database is invalid or corrupted"
	))
}