summaryrefslogtreecommitdiffstats
path: root/lib/domain/libimagcounter/src/counter.rs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/domain/libimagcounter/src/counter.rs')
-rw-r--r--lib/domain/libimagcounter/src/counter.rs266
1 files changed, 266 insertions, 0 deletions
diff --git a/lib/domain/libimagcounter/src/counter.rs b/lib/domain/libimagcounter/src/counter.rs
new file mode 100644
index 00000000..7b6c1d3c
--- /dev/null
+++ b/lib/domain/libimagcounter/src/counter.rs
@@ -0,0 +1,266 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::error::MapErrInto;
+
+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<S: Into<String>>(unit: S) -> CounterUnit {
+ CounterUnit(unit.into())
+ }
+}
+
+pub struct Counter<'a> {
+ fle: FileLockEntry<'a>,
+ unit: Option<CounterUnit>,
+}
+
+impl<'a> Counter<'a> {
+
+ pub fn new(store: &Store, name: CounterName, init: i64) -> Result<Counter> {
+ use std::ops::DerefMut;
+
+ debug!("Creating new counter: '{}' with value: {}", name, init);
+ let fle = {
+ let id = try!(ModuleEntryPath::new(name.clone())
+ .into_storeid()
+ .map_err_into(CEK::StoreWriteError));
+ let mut lockentry = try!(store.create(id).map_err_into(CEK::StoreWriteError));
+
+ {
+ let mut entry = lockentry.deref_mut();
+ let mut 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<CounterUnit>) -> Result<Counter<'a>> {
+ self.unit = unit;
+
+ if let Some(u) = self.unit.clone() {
+ let mut 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 mut header = self.fle.deref_mut().get_header_mut();
+ let query = String::from("counter.value");
+ match try!(header.read(&query).map_err_into(CEK::StoreReadError)) {
+ Some(&Value::Integer(i)) => {
+ header.set(&query, Value::Integer(i + 1))
+ .map_err_into(CEK::StoreWriteError)
+ .map(|_| ())
+ },
+ _ => Err(CE::new(CEK::StoreReadError, None)),
+ }
+ }
+
+ pub fn dec(&mut self) -> Result<()> {
+ let mut header = self.fle.deref_mut().get_header_mut();
+ let query = String::from("counter.value");
+ match try!(header.read(&query).map_err_into(CEK::StoreReadError)) {
+ Some(&Value::Integer(i)) => {
+ header.set(&query, Value::Integer(i - 1))
+ .map_err_into(CEK::StoreWriteError)
+ .map(|_| ())
+ },
+ _ => Err(CE::new(CEK::StoreReadError, None)),
+ }
+ }
+
+ pub fn reset(&mut self) -> Result<()> {
+ self.set(0)
+ }
+
+ pub fn set(&mut self, v: i64) -> Result<()> {
+ let mut header = self.fle.deref_mut().get_header_mut();
+ header.set(&String::from("counter.value"), Value::Integer(v))
+ .map_err_into(CEK::StoreWriteError)
+ .map(|_| ())
+ }
+
+ pub fn name(&self) -> Result<CounterName> {
+ 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<i64> {
+ 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<Option<CounterUnit>> {
+ 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<T, F>(&self, name: &str, f: F) -> Result<T>
+ where F: FnOnce(Option<&Value>) -> Result<T>
+ {
+
+ self.fle
+ .get_header()
+ .read(&String::from(name))
+ .map_err_into(CEK::StoreWriteError)
+ .and_then(f)
+ }
+
+ pub fn load(name: CounterName, store: &Store) -> Result<Counter> {
+ debug!("Loading counter: '{}'", name);
+ let id = try!(ModuleEntryPath::new(name)
+ .into_storeid()
+ .map_err_into(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()
+ .map_err_into(CEK::StoreWriteError));
+ store.delete(id).map_err_into(CEK::StoreWriteError)
+ }
+
+ pub fn all_counters(store: &Store) -> Result<CounterIterator> {
+ store.retrieve_for_module("counter")
+ .map(|iter| CounterIterator::new(store, iter))
+ .map_err_into(CEK::StoreReadError)
+ }
+
+}
+
+trait FromStoreId {
+ fn from_storeid(&Store, StoreId) -> Result<Counter>;
+}
+
+impl<'a> FromStoreId for Counter<'a> {
+
+ fn from_storeid(store: &Store, id: StoreId) -> Result<Counter> {
+ debug!("Loading counter from storeid: '{:?}'", id);
+ match store.retrieve(id) {
+ Err(e) => Err(CE::new(CEK::StoreReadError, Some(Box::new(e)))),
+ Ok(c) => {
+ let mut counter = Counter { fle: c, unit: None };
+ counter.read_unit()
+ .map_err_into(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<Counter<'a>>;
+
+ fn next(&mut self) -> Option<Result<Counter<'a>>> {
+ self.iditer
+ .next()
+ .map(|id| Counter::from_storeid(self.store, id))
+ }
+
+}
+