diff options
Diffstat (limited to 'libimagruby/src')
-rw-r--r-- | libimagruby/src/cache.rs | 42 | ||||
-rw-r--r-- | libimagruby/src/entry.rs | 282 | ||||
-rw-r--r-- | libimagruby/src/imag.rs | 148 | ||||
-rw-r--r-- | libimagruby/src/lib.rs | 53 | ||||
-rw-r--r-- | libimagruby/src/ruby_utils.rs | 210 | ||||
-rw-r--r-- | libimagruby/src/store.rs | 559 | ||||
-rw-r--r-- | libimagruby/src/storeid.rs | 154 | ||||
-rw-r--r-- | libimagruby/src/toml_utils.rs | 65 | ||||
-rw-r--r-- | libimagruby/src/util.rs | 102 |
9 files changed, 1615 insertions, 0 deletions
diff --git a/libimagruby/src/cache.rs b/libimagruby/src/cache.rs new file mode 100644 index 00000000..94c2b869 --- /dev/null +++ b/libimagruby/src/cache.rs @@ -0,0 +1,42 @@ +// +// 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::collections::BTreeMap; +use std::sync::Arc; +use std::sync::Mutex; + +use uuid::Uuid; + +use libimagstore::store::Store; + +#[derive(Clone, Debug, Ord, Hash, Eq, PartialOrd, PartialEq)] +pub struct StoreHandle(Uuid); + +impl StoreHandle { + pub fn new() -> StoreHandle { + StoreHandle(Uuid::new_v4()) + } +} + +lazy_static! { + pub static ref RUBY_STORE_CACHE: Arc<Mutex<BTreeMap<StoreHandle, Store>>> = { + Arc::new(Mutex::new(BTreeMap::new())) + }; +} + diff --git a/libimagruby/src/entry.rs b/libimagruby/src/entry.rs new file mode 100644 index 00000000..4614feab --- /dev/null +++ b/libimagruby/src/entry.rs @@ -0,0 +1,282 @@ +// +// 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::collections::BTreeMap; +use std::error::Error; +use std::ops::Deref; +use std::ops::DerefMut; + +use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject}; +use uuid::Uuid; + +use libimagstore::store::FileLockEntry as FLE; +use libimagstore::store::EntryHeader; +use libimagstore::store::EntryContent; +use libimagstore::store::Entry; +use libimagstore::storeid::StoreId; + +use ruby_utils::IntoToml; +use toml_utils::IntoRuby; +use util::Wrap; +use util::Unwrap; +use cache::RUBY_STORE_CACHE; +use cache::StoreHandle; + +pub struct FileLockEntryHandle(StoreHandle, StoreId); + +impl FileLockEntryHandle { + pub fn new(sh: StoreHandle, id: StoreId) -> FileLockEntryHandle { + FileLockEntryHandle(sh, id) + } + + pub fn store_handle(&self) -> &StoreHandle { + &self.0 + } + + pub fn fle_handle(&self) -> &StoreId { + &self.1 + } +} + +wrappable_struct!(FileLockEntryHandle, FileLockEntryWrapper, FLE_WRAPPER); +class!(RFileLockEntryHandle); +impl_wrap!(FileLockEntryHandle => FLE_WRAPPER); +impl_unwrap!(RFileLockEntryHandle => FileLockEntryHandle => FLE_WRAPPER); +impl_verified_object!(RFileLockEntryHandle); + +/// Helper macro for operating on RUBY_STORE_CACHE object +/// +/// This helps us calling operations on FileLockEntry objects. +/// +/// What I do here: Fetch the Store object from the cache, fetch the appropriate FileLockEntry and +/// call the operation on it. +/// +/// This could be improved with another cache, so not the store is cached but the FileLockEntry +/// only, but then we run into lifetime problems with the Store and its FileLockEntry objects. +/// Feel free to fix this, but for now, this is a workable solution. +/// +#[macro_export] +macro_rules! call_on_fle_from_store { + ($itself:ident ($wrapper:ident) -> $name:ident -> $operation:block) => {{ + let handle = $itself.get_data(&*$wrapper); + let store_handle = handle.store_handle(); + call_on_store_by_handle! { + store_handle named store inside { + match store.get(handle.fle_handle().clone()) { + Ok(Some(mut $name)) => { + $operation + }, + Ok(None) => { + VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist"); + NilClass::new().to_any_object() + }, + Err(e) => { + VM::raise(Class::from_existing("RuntimeError"), e.description()); + NilClass::new().to_any_object() + }, + } + } + } + }}; + ($itself:ident ($wrapper:ident) -> $name:ident -> $operation: block on fail return $ex:expr) => {{ + let handle = $itself.get_data(&*$wrapper); + let store_handle = handle.store_handle(); + call_on_store_by_handle! { + store_handle named store inside { + match store.get(handle.fle_handle().clone()) { + Ok(Some(mut $name)) => { + $operation + }, + Ok(None) => { + VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist"); + $ex + }, + Err(e) => { + VM::raise(Class::from_existing("RuntimeError"), e.description()); + $ex + }, + } + } + } + }}; +} + + +methods!( + RFileLockEntryHandle, + itself, + + fn r_get_location() -> AnyObject { + call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { fle.get_location().clone().wrap() }) + } + + fn r_get_header() -> AnyObject { + call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { fle.get_header().clone().wrap() }) + } + + fn r_set_header(hdr: Hash) -> NilClass { + use ruby_utils::IntoToml; + use toml_utils::IntoRuby; + use toml::Value; + + let entryheader = match typecheck!(hdr or return NilClass::new()).into_toml() { + Value::Table(t) => EntryHeader::from(t), + _ => { + let ec = Class::from_existing("RuntimeError"); + VM::raise(ec, "Something weird happened. Hash seems to be not a Hash"); + return NilClass::new(); + }, + }; + + call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { + *fle.get_header_mut() = entryheader; + NilClass::new().to_any_object() + }); + + NilClass::new() + } + + fn r_get_content() -> AnyObject { + call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { + fle.get_content().clone().wrap() + } on fail return NilClass::new().to_any_object()) + } + + fn r_set_content(ctt: RString) -> NilClass { + use ruby_utils::IntoToml; + use toml_utils::IntoRuby; + use toml::Value; + + let content = match typecheck!(ctt).into_toml() { + Value::String(s) => s, + _ => { + let ec = Class::from_existing("RuntimeError"); + VM::raise(ec, "Something weird happened. String seems to be not a String"); + return NilClass::new(); + }, + }; + + call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { + *fle.get_content_mut() = content; + NilClass::new().to_any_object() + }); + + NilClass::new() + } + +); + +wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER); +class!(REntryHeader); +impl_wrap!(EntryHeader => ENTRY_HEADER_WRAPPER); +impl_unwrap!(REntryHeader => EntryHeader => ENTRY_HEADER_WRAPPER); +impl_verified_object!(REntryHeader); + +methods!( + REntryHeader, + itself, + + fn r_entry_header_new() -> AnyObject { + EntryHeader::new().wrap() + } + + fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean { + let spec = typecheck!(spec or return Boolean::new(false)).to_string(); + let obj = obj.unwrap(); // possibly not safe... TODO + + match itself.get_data(&*ENTRY_HEADER_WRAPPER).insert(&spec, obj.into_toml()) { + Ok(b) => Boolean::new(b), + Err(e) => { + VM::raise(Class::from_existing("RuntimeError"), e.description()); + Boolean::new(false) + } + } + } + + fn r_entry_header_set(spec: RString, obj: AnyObject) -> AnyObject { + use ruru::NilClass; + + let spec = typecheck!(spec or return any Boolean::new(false)).to_string(); + let obj = obj.unwrap(); // possibly not safe... TODO + + match itself.get_data(&*ENTRY_HEADER_WRAPPER).set(&spec, obj.into_toml()) { + Ok(Some(v)) => v.into_ruby(), + Ok(None) => NilClass::new().to_any_object(), + Err(e) => { + VM::raise(Class::from_existing("RuntimeError"), e.description()); + return Boolean::new(false).to_any_object(); + } + } + } + + fn r_entry_header_get(spec: RString) -> AnyObject { + use ruru::NilClass; + + let spec = typecheck!(spec or return any Boolean::new(false)).to_string(); + + match itself.get_data(&*ENTRY_HEADER_WRAPPER).read(&spec) { + Ok(Some(v)) => v.into_ruby(), + Ok(None) => NilClass::new().to_any_object(), + Err(e) => { + VM::raise(Class::from_existing("RuntimeError"), e.description()); + return Boolean::new(false).to_any_object(); + } + } + } + +); + +wrappable_struct!(EntryContent, EntryContentWrapper, ENTRY_CONTENT_WRAPPER); +class!(REntryContent); +impl_wrap!(EntryContent => ENTRY_CONTENT_WRAPPER); +impl_unwrap!(REntryContent => EntryContent => ENTRY_CONTENT_WRAPPER); + +wrappable_struct!(Entry, EntryWrapper, ENTRY_WRAPPER); +class!(REntry); +impl_unwrap!(REntry => Entry => ENTRY_WRAPPER); + +pub fn setup_filelockentry() -> Class { + let mut class = Class::new("RFileLockEntryHandle", None); + class.define(|itself| { + itself.def("location", r_get_location); + itself.def("header" , r_get_header); + itself.def("header=" , r_set_header); + itself.def("content" , r_get_content); + itself.def("content=", r_set_content); + }); + class +} + +pub fn setup_entryheader() -> Class { + let mut class = Class::new("REntryHeader", None); + class.define(|itself| { + itself.def("insert", r_entry_header_insert); + itself.def("set" , r_entry_header_set); + itself.def("[]=" , r_entry_header_set); + itself.def("read" , r_entry_header_get); + itself.def("[]" , r_entry_header_get); + }); + class +} + +pub fn setup_entrycontent() -> Class { + let string = Class::from_existing("String"); + let mut class = Class::new("REntryContent", Some(&string)); + class +} diff --git a/libimagruby/src/imag.rs b/libimagruby/src/imag.rs new file mode 100644 index 00000000..a59610b2 --- /dev/null +++ b/libimagruby/src/imag.rs @@ -0,0 +1,148 @@ +// +// 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::error::Error; + +use ruru::{Class, Boolean, RString, NilClass, VM, Object}; + +use libimagrt::logger::ImagLogger; + +class!(RImag); + +methods!( + RImag, + itself, + + fn r_initialize_logger(debug: Boolean, verbose: Boolean, colored: Boolean) -> NilClass { + use std::env::var as env_var; + use env_logger; + use log; + use log::{Log, LogLevel, LogRecord, LogMetadata}; + use log::LogLevelFilter; + + let debug = match debug { + Ok(d) => d.to_bool(), + Err(ref e) => { + VM::raise(e.to_exception(), e.description()); + return NilClass::new(); + }, + }; + + let verbose = match verbose { + Ok(v) => v.to_bool(), + Err(ref e) => { + VM::raise(e.to_exception(), e.description()); + return NilClass::new(); + }, + }; + + let colored = match colored { + Ok(c) => c.to_bool(), + Err(ref e) => { + VM::raise(e.to_exception(), e.description()); + return NilClass::new(); + }, + }; + + if env_var("IMAG_LOG_ENV").is_ok() { + env_logger::init().unwrap(); + } else { + let lvl = if debug { + LogLevelFilter::Debug + } else if verbose { + LogLevelFilter::Info + } else { + LogLevelFilter::Warn + }; + + log::set_logger(|max_log_lvl| { + max_log_lvl.set(lvl); + debug!("Init logger with {}", lvl); + let lgr = ImagLogger::new(lvl.to_log_level().unwrap()) + .with_color(colored) + .with_prefix("[imag][ruby]".to_owned()) + .with_dbg_file_and_line(false); + Box::new(lgr) + }) + .map_err(|_| { + panic!("Could not setup logger"); + }) + .ok(); + } + + NilClass::new() + } + + fn r_log_trace(l: RString) -> NilClass { + match l { + Err(ref e) => VM::raise(e.to_exception(), e.description()), + Ok(s) => trace!("{}", s.to_string()), + } + NilClass::new() + } + + fn r_log_debug(l: RString) -> NilClass { + match l { + Err(ref e) => VM::raise(e.to_exception(), e.description()), + Ok(s) => debug!("{}", s.to_string()), + } + NilClass::new() + } + + fn r_log_info(l: RString) -> NilClass { + match l { + Err(ref e) => VM::raise(e.to_exception(), e.description()), + Ok(s) => info!("{}", s.to_string()), + } + NilClass::new() + } + + fn r_log_warn(l: RString) -> NilClass { + match l { + Err(ref e) => VM::raise(e.to_exception(), e.description()), + Ok(s) => warn!("{}", s.to_string()), + } + NilClass::new() + } + + fn r_log_error(l: RString) -> NilClass { + match l { + Err(ref e) => VM::raise(e.to_exception(), e.description()), + Ok(s) => error!("{}", s.to_string()), + } + NilClass::new() + } + +); + +pub fn setup() -> Class { + let mut class = Class::new("RImag", None); + class.define(|itself| { + itself.def_self("init_logger", r_initialize_logger); + itself.def_self("trace", r_log_trace); + itself.def_self("dbg", r_log_debug); + itself.def_self("debug", r_log_debug); + itself.def_self("info", r_log_info); + itself.def_self("warn", r_log_warn); + itself.def_self("error", r_log_error); + }); + class +} + diff --git a/libimagruby/src/lib.rs b/libimagruby/src/lib.rs new file mode 100644 index 00000000..13d933b3 --- /dev/null +++ b/libimagruby/src/lib.rs @@ -0,0 +1,53 @@ +// +// 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 +// + +#[macro_use] extern crate ruru; +#[macro_use] extern crate lazy_static; +#[macro_use] extern crate log; +extern crate env_logger; +extern crate toml; +extern crate uuid; + +#[macro_use] extern crate libimagerror; +extern crate libimagstore; +extern crate libimagstorestdhook; +extern crate libimagrt; +#[macro_use] extern crate libimagutil; + +#[macro_use] mod util; +#[macro_use] pub mod store; +mod cache; + +pub mod entry; +pub mod imag; +pub mod ruby_utils; +pub mod storeid; +pub mod toml_utils; + +#[no_mangle] +#[allow(non_snake_case)] +pub extern fn Init_liblibimagruby() { + self::store::setup(); + self::storeid::setup(); + self::entry::setup_filelockentry(); + self::entry::setup_entryheader(); + self::entry::setup_entrycontent(); + self::imag::setup(); +} + diff --git a/libimagruby/src/ruby_utils.rs b/libimagruby/src/ruby_utils.rs new file mode 100644 index 00000000..b40f684d --- /dev/null +++ b/libimagruby/src/ruby_utils.rs @@ -0,0 +1,210 @@ +// +// 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 +// + +// Ruby -> Toml translation primitives + +use std::collections::BTreeMap; + +use ruru::{Object, AnyObject, Class, RString, Fixnum, Float, Symbol, Hash, Array, VM}; +use ruru::types::ValueType; +use toml::Value; + + +pub trait AsToml : Sized { + fn as_toml(&self) -> Value; +} + +pub trait IntoToml : AsToml { + fn into_toml(self) -> Value { + self.as_toml() + } +} +impl<T: AsToml> IntoToml for T { } + +impl AsToml for AnyObject { + + fn as_toml(&self) -> Value { + match self.value().ty() { + ValueType::None => { + Value::Boolean(false) + }, + ValueType::Object => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Class => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Module => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Float => self.try_convert_to::<Float>().unwrap().as_toml(), + ValueType::RString => self.try_convert_to::<RString>().unwrap().as_toml(), + ValueType::Regexp => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Array => self.try_convert_to::<Array>().unwrap().as_toml(), + ValueType::Hash => self.try_convert_to::<Hash>().unwrap().as_toml(), + ValueType::Struct => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Bignum => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::File => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Data => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Match => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Complex => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Rational => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Nil => Value::Boolean(false), + ValueType::True => Value::Boolean(true), + ValueType::False => Value::Boolean(false), + ValueType::Symbol => self.try_convert_to::<Symbol>().unwrap().as_toml(), + ValueType::Fixnum => self.try_convert_to::<Fixnum>().unwrap().as_toml(), + ValueType::Undef => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Node => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::IClass => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Zombie => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + ValueType::Mask => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Cannot translate type '' to fit into TOML"); + Value::Boolean(false) + }, + } + } + +} + +impl AsToml for Hash { + + fn as_toml(&self) -> Value { + let mut btm = BTreeMap::new(); + self.try_convert_to::<Hash>() + .unwrap() + .each(|key, value| { + let key = match key.as_toml() { + Value::String(s) => s, + _ => { + let rte = Class::from_existing("TypeError"); + VM::raise(rte, "Can only have String or Symbol as Key for TOML maps"); + String::new() + } + }; + let value = value.as_toml(); + btm.insert(key, value); + }); + Value::Table(btm) + } + +} + +impl AsToml for Array { + + fn as_toml(&self) -> Value { + let vals = self + .try_convert_to::<Array>() + .unwrap() + .into_iter() + .map(|v| v.as_toml()) + .collect::<Vec<Value>>(); + + Value::Array(vals) + } + +} + +impl AsToml for RString { + + fn as_toml(&self) -> Value { + Value::String(self.try_convert_to::<RString>().unwrap().to_string()) + } + +} + +impl AsToml for Float { + + fn as_toml(&self) -> Value { + Value::Float(self.try_convert_to::<Float>().unwrap().to_f64()) + } + +} + +impl AsToml for Symbol { + + fn as_toml(&self) -> Value { + Value::String(self.try_convert_to::<Symbol>().unwrap().to_string()) + } + +} + +impl AsToml for Fixnum { + + fn as_toml(&self) -> Value { + Value::Integer(self.try_convert_to::<Fixnum>().unwrap().to_i64()) + } + +} + diff --git a/libimagruby/src/store.rs b/libimagruby/src/store.rs new file mode 100644 index 00000000..4c3a49a2 --- /dev/null +++ b/libimagruby/src/store.rs @@ -0,0 +1,559 @@ +// +// 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 libimagstore::store::Store; +use libimagerror::trace::trace_error; +use std::error::Error; +use std::ops::Deref; +use std::ops::DerefMut; + +use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject}; + +use ruby_utils::IntoToml; +use toml_utils::IntoRuby; +use util::Wrap; +use util::Unwrap; + +use storeid::RStoreId; +use entry::RFileLockEntryHandle; +use cache::StoreHandle; + +wrappable_struct!(StoreHandle, StoreWrapper, STORE_WRAPPER); +class!(RStoreHandle); +impl_wrap!(StoreHandle => STORE_WRAPPER); +impl_unwrap!(RStoreHandle => StoreHandle => STORE_WRAPPER); +impl_verified_object!(RStoreHandle); + +macro_rules! call_on_store_by_handle { + { + $store_handle:ident named $name:ident inside $operation:block + }=> {{ + call_on_store_by_handle! { + $store_handle + named $name + inside $operation + on fail return NilClass::new().to_any_object() + } + }}; + + { + $store_handle:ident named $name:ident inside $operation:block on fail return $ex:expr + } => {{ + use cache::RUBY_STORE_CACHE; + + let arc = RUBY_STORE_CACHE.clone(); + { + let lock = arc.lock(); + match lock { + Ok(mut hm) => { + match hm.get($store_handle) { + Some($name) => { $operation }, + None => { + VM::raise(Class::from_existing("RuntimeError"), + "Tried to operate on non-existing object"); + $ex + } + } + }, + Err(e) => { + VM::raise(Class::from_existing("RuntimeError"), e.description()); + $ex + } + } + } + }}; +} + +macro_rules! call_on_store { + { + $store_name:ident <- $itself:ident wrapped inside $wrapper:ident, + $fle_name:ident <- fetch $fle_handle_name:ident + operation $operation:block + } => { + call_on_store! { + $store_name <- $itself wrapped inside $wrapper, + $fle_name <- fetch $fle_handle_name, + operation $operation, + on fail return NilClass::new() + } + }; + + { + $store_name:ident <- $itself:ident wrapped inside $wrapper:ident, + $fle_name:ident <- fetch $fle_handle_name:ident, + operation $operation:block, + on fail return $fail_expr:expr + } => { + let handle = $itself.get_data(&*$wrapper); + call_on_store_by_handle! { + handle named $store_name inside { + let $fle_name = match $store_name.get($fle_handle_name) { + Ok(Some(fle)) => fle, + Ok(None) => { + VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist"); + return $fail_expr + }, + Err(e) => { + VM::raise(Class::from_existing("RuntimeError"), e.description()); + return $fail_expr + }, + }; + $operation + } + on fail return $fail_expr + } + }; + + { + $store_name:ident <- $itself:ident wrapped inside $wrapper:ident, + operation $operation:block, + on fail return $fail_expr:expr + } => { + let handle = $itself.get_data(&*$wrapper); + call_on_store_by_handle! { + handle named $store_name inside $operation on fail return $fail_expr + } + }; + + { + $store_name:ident <- $itself:ident wrapped inside $wrapper:ident, + operation $block + } => { + let handle = $itself.get_data(&*$wrapper); + call_on_store_by_handle! { handle named $name inside $operation } + }; +} + +methods!( + RStoreHandle, + itself, + + // Build a new Store object, return a handle to it. + // + // This function takes a boolean whether the store should include debugging functionality + // (namingly the debug hooks) and a runtimepath, where the store lifes. + // It then builds a Store object (raising errors on failure and returning Nil) and a handle for + // it. + // It puts the store object and the handle in the cache and returns the handle as object to the + // Ruby code. + // + // # Returns + // + // Nil on failure (including raising an error) + // StoreHandle on success + // + fn new(store_debugging: Boolean, rtp: RString) -> AnyObject { + use std::path::PathBuf; + use libimagerror::into::IntoError; + use libimagerror::trace::trace_error; + use libimagerror::trace::trace_error_dbg; + use libimagerror::trace::trace_error_exit; + use libimagrt::configuration::ConfigErrorKind; + use libimagrt::configuration::Configuration; + use libimagrt::error::RuntimeErrorKind; + use libimagstore::error::StoreErrorKind; + use libimagstore::hook::Hook; + use libimagstore::hook::position::HookPosition as HP; + use libimagstorestdhook::debug::DebugHook; + use libimagstorestdhook::vcs::git::delete::DeleteHook as GitDeleteHook; + use libimagstorestdhook::vcs::git::store_unload::StoreUnloadHook as GitStoreUnloadHook; + use libimagstorestdhook::vcs::git::update::UpdateHook as GitUpdateHook; + + use cache::RUBY_STORE_CACHE; + + let store_debugging = typecheck!(store_debugging or return any NilClass::new()).to_bool(); + let rtp = PathBuf::from(typecheck!(rtp or return any NilClass::new()).to_string()); + + if !rtp.exists() || !rtp.is_dir() { + VM::raise(Class::from_existing("RuntimeError"), "Runtimepath not a directory"); + return NilClass::new().to_any_object(); + } + + let store_config = match Configuration::new(&rtp) { + Ok(mut cfg) => cfg.store_config().cloned(), + Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound { + VM::raise(Class::from_existing("RuntimeError"), e.description()); + return NilClass::new().to_any_object(); + } else { + warn!("No config file found."); + warn!("Continuing without configuration file"); + None + }, + }; + + let storepath = { + let mut spath |