summaryrefslogtreecommitdiffstats
path: root/libimagruby/src
diff options
context:
space:
mode:
Diffstat (limited to 'libimagruby/src')
-rw-r--r--libimagruby/src/cache.rs42
-rw-r--r--libimagruby/src/entry.rs282
-rw-r--r--libimagruby/src/imag.rs148
-rw-r--r--libimagruby/src/lib.rs53
-rw-r--r--libimagruby/src/ruby_utils.rs210
-rw-r--r--libimagruby/src/store.rs559
-rw-r--r--libimagruby/src/storeid.rs154
-rw-r--r--libimagruby/src/toml_utils.rs65
-rw-r--r--libimagruby/src/util.rs102
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