// // imag - the personal information management suite for the commandline // Copyright (C) 2015, 2016 Matthias Beyer and contributors // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; version // 2.1 of the License. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // use std::ops::DerefMut; use toml::Value; use toml_query::read::TomlValueReadExt; use toml_query::set::TomlValueSetExt; use std::collections::BTreeMap; use std::fmt; use std::fmt::Display; use libimagstore::store::Store; use libimagstore::storeid::StoreIdIterator; use libimagstore::store::FileLockEntry; use libimagstore::storeid::StoreId; use libimagstore::storeid::IntoStoreId; use libimagerror::into::IntoError; use module_path::ModuleEntryPath; use result::Result; use error::CounterError as CE; use error::CounterErrorKind as CEK; use error::ResultExt; pub type CounterName = String; #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct CounterUnit(String); impl Display for CounterUnit { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "({})", self.0) } } impl CounterUnit { pub fn new>(unit: S) -> CounterUnit { CounterUnit(unit.into()) } } pub struct Counter<'a> { fle: FileLockEntry<'a>, unit: Option, } impl<'a> Counter<'a> { pub fn new(store: &Store, name: CounterName, init: i64) -> Result { use std::ops::DerefMut; debug!("Creating new counter: '{}' with value: {}", name, init); let fle = { let id = try!(ModuleEntryPath::new(name.clone()) .into_storeid() .chain_err(|| CEK::StoreWriteError)); let mut lockentry = try!(store.create(id).chain_err(|| CEK::StoreWriteError)); { let entry = lockentry.deref_mut(); let header = entry.get_header_mut(); let setres = header.set(&String::from("counter"), Value::Table(BTreeMap::new())); if setres.is_err() { return Err(CEK::StoreWriteError.into_error()); } let setres = header.set(&String::from("counter.name"), Value::String(name)); if setres.is_err() { return Err(CEK::StoreWriteError.into_error()) } let setres = header.set(&String::from("counter.value"), Value::Integer(init)); if setres.is_err() { return Err(CEK::StoreWriteError.into_error()) } } lockentry }; Ok(Counter { fle: fle, unit: None }) } pub fn with_unit(mut self, unit: Option) -> Result> { self.unit = unit; if let Some(u) = self.unit.clone() { let header = self.fle.deref_mut().get_header_mut(); let setres = header.set(&String::from("counter.unit"), Value::String(u.0)); if setres.is_err() { self.unit = None; return Err(CEK::StoreWriteError.into_error()) } }; Ok(self) } pub fn inc(&mut self) -> Result<()> { let header = self.fle.deref_mut().get_header_mut(); let query = String::from("counter.value"); match try!(header.read(&query).chain_err(|| CEK::StoreReadError)) { Some(&Value::Integer(i)) => { header.set(&query, Value::Integer(i + 1)) .chain_err(|| CEK::StoreWriteError) .map(|_| ()) }, _ => Err(CE::from_kind(CEK::StoreReadError)), } } pub fn dec(&mut self) -> Result<()> { let header = self.fle.deref_mut().get_header_mut(); let query = String::from("counter.value"); match try!(header.read(&query).chain_err(|| CEK::StoreReadError)) { Some(&Value::Integer(i)) => { header.set(&query, Value::Integer(i - 1)) .chain_err(|| CEK::StoreWriteError) .map(|_| ()) }, _ => Err(CE::from_kind(CEK::StoreReadError)), } } pub fn reset(&mut self) -> Result<()> { self.set(0) } pub fn set(&mut self, v: i64) -> Result<()> { self.fle .deref_mut() .get_header_mut() .set(&String::from("counter.value"), Value::Integer(v)) .chain_err(|| CEK::StoreWriteError) .map(|_| ()) } pub fn name(&self) -> Result { self.read_header_at("counter.name", |v| match v { Some(&Value::String(ref s)) => Ok(s.clone()), Some(_) => Err(CEK::HeaderTypeError.into_error()), _ => Err(CEK::StoreReadError.into_error()), }) } pub fn value(&self) -> Result { self.read_header_at("counter.value", |v| match v { Some(&Value::Integer(i)) => Ok(i), Some(_) => Err(CEK::HeaderTypeError.into_error()), _ => Err(CEK::StoreReadError.into_error()), }) } pub fn unit(&self) -> Option<&CounterUnit> { self.unit.as_ref() } pub fn read_unit(&self) -> Result> { self.read_header_at("counter.unit", |s| match s { Some(&Value::String(ref s)) => Ok(Some(CounterUnit::new(s.clone()))), Some(_) => Err(CEK::HeaderTypeError.into_error()), _ => Err(CEK::StoreReadError.into_error()), }) } fn read_header_at(&self, name: &str, f: F) -> Result where F: FnOnce(Option<&Value>) -> Result { self.fle .get_header() .read(&String::from(name)) .chain_err(|| CEK::StoreWriteError) .and_then(f) } pub fn load(name: CounterName, store: &Store) -> Result { debug!("Loading counter: '{}'", name); let id = try!(ModuleEntryPath::new(name) .into_storeid() .chain_err(|| CEK::StoreWriteError)); Counter::from_storeid(store, id) } pub fn delete(name: CounterName, store: &Store) -> Result<()> { debug!("Deleting counter: '{}'", name); let id = try!(ModuleEntryPath::new(name) .into_storeid() .chain_err(|| CEK::StoreWriteError)); store.delete(id).chain_err(|| CEK::StoreWriteError) } pub fn all_counters(store: &Store) -> Result { store.retrieve_for_module("counter") .map(|iter| CounterIterator::new(store, iter)) .chain_err(|| CEK::StoreReadError) } } trait FromStoreId { fn from_storeid(&Store, StoreId) -> Result; } impl<'a> FromStoreId for Counter<'a> { fn from_storeid(store: &Store, id: StoreId) -> Result { debug!("Loading counter from storeid: '{:?}'", id); match store.retrieve(id) { Err(e) => Err(e).chain_err(|| CEK::StoreReadError), Ok(c) => { let mut counter = Counter { fle: c, unit: None }; counter.read_unit() .chain_err(|| CEK::StoreReadError) .and_then(|u| { counter.unit = u; Ok(counter) }) } } } } pub struct CounterIterator<'a> { store: &'a Store, iditer: StoreIdIterator, } impl<'a> CounterIterator<'a> { pub fn new(store: &'a Store, iditer: StoreIdIterator) -> CounterIterator<'a> { CounterIterator { store: store, iditer: iditer, } } } impl<'a> Iterator for CounterIterator<'a> { type Item = Result>; fn next(&mut self) -> Option>> { self.iditer .next() .map(|id| Counter::from_storeid(self.store, id)) } }