diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2017-01-25 11:23:21 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-25 11:23:21 +0100 |
commit | 1e3193ebb2028478aa26efd1b69697cddf00914f (patch) | |
tree | d1504a620dc7065f387467162d75276b311ab56c | |
parent | 636bfbb768f23c92d581ab660fcaa88927c859b1 (diff) | |
parent | 4804cf36ce9a45a0300d5e78850cf55f6b8b2c2c (diff) |
Merge pull request #847 from matthiasbeyer/imag-ruby
Imag ruby
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | libimagruby/.gitignore | 3 | ||||
-rw-r--r-- | libimagruby/Cargo.toml | 41 | ||||
-rw-r--r-- | libimagruby/Gemfile | 4 | ||||
-rw-r--r-- | libimagruby/Makefile | 20 | ||||
-rw-r--r-- | libimagruby/README.md | 106 | ||||
-rw-r--r-- | libimagruby/Rakefile | 2 | ||||
-rw-r--r-- | libimagruby/imag.gemspec | 26 | ||||
-rw-r--r-- | libimagruby/lib/imag.rb | 96 | ||||
-rw-r--r-- | libimagruby/lib/imag/version.rb | 3 | ||||
-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 | ||||
-rw-r--r-- | libimagruby/test/test_entries.rb | 27 | ||||
-rw-r--r-- | libimagruby/test/test_ruby.rb | 58 | ||||
-rw-r--r-- | libimagruby/test/test_store.rb | 16 |
23 files changed, 2021 insertions, 0 deletions
@@ -36,6 +36,7 @@ members = [ "libimagnotes", "libimagref", "libimagrt", + "libimagruby", "libimagstore", "libimagstorestdhook", "libimagtimeui", @@ -67,6 +67,9 @@ lib: $(LIB_TARGETS) lib-test: $(LIB_TARGETS_TEST) +lib-imag-ruby-test: + @$(MAKE) -C libimagruby + test: bin-test lib-test install: $(INSTALL_TARGETS) imag-bin-install diff --git a/libimagruby/.gitignore b/libimagruby/.gitignore new file mode 100644 index 00000000..2f7c731a --- /dev/null +++ b/libimagruby/.gitignore @@ -0,0 +1,3 @@ +.bundle +Gemfile.lock +vendor/bundle diff --git a/libimagruby/Cargo.toml b/libimagruby/Cargo.toml new file mode 100644 index 00000000..2d98a086 --- /dev/null +++ b/libimagruby/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "libimagruby" +version = "0.1.0" +authors = ["Matthias Beyer <mail@beyermatthias.de>"] + +description = "Library for the imag core distribution" + +keywords = ["imag", "PIM", "personal", "information", "management"] +readme = "../README.md" +license = "LGPL-2.1" + +documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html" +repository = "https://github.com/matthiasbeyer/imag" +homepage = "http://imag-pim.org" + +[lib] +crate-type = ["dylib"] + +[dependencies] +ruru = "0.9" +lazy_static = "0.2" +log = "0.3" +env_logger = "0.3" +toml = "0.2" +uuid = { version = "0.3", features = ["v4"] } + +[dependencies.libimagerror] +path = "../libimagerror" + +[dependencies.libimagrt] +path = "../libimagrt" + +[dependencies.libimagstore] +path = "../libimagstore" + +[dependencies.libimagstorestdhook] +path = "../libimagstorestdhook" + +[dependencies.libimagutil] +path = "../libimagutil" + diff --git a/libimagruby/Gemfile b/libimagruby/Gemfile new file mode 100644 index 00000000..aadb3470 --- /dev/null +++ b/libimagruby/Gemfile @@ -0,0 +1,4 @@ +# frozen_string_literal: true +source "https://rubygems.org" + +gemspec diff --git a/libimagruby/Makefile b/libimagruby/Makefile new file mode 100644 index 00000000..f7ed82d6 --- /dev/null +++ b/libimagruby/Makefile @@ -0,0 +1,20 @@ +ECHO=$(shell which echo) -e +RUBY=$(shell which ruby) +RUBY_TESTS=$(shell find ./test -maxdepth 1 -name "*.rb" -type f) +RUBY_TEST_TARGETS=$(foreach x,$(subst ,,$(RUBY_TESTS)),$(x)) + +all: lib + +lib: + $(MAKE) -C .. libimagruby + +lib-release: + $(MAKE) -C .. libimagruby-release + +test: lib $(RUBY_TEST_TARGETS) + +$(RUBY_TEST_TARGETS): %: lib .FORCE + @$(ECHO) "\t[TEST ]:\t$@" + @$(RUBY) $(subst -test,,$@) + +.FORCE: diff --git a/libimagruby/README.md b/libimagruby/README.md new file mode 100644 index 00000000..2f761fb1 --- /dev/null +++ b/libimagruby/README.md @@ -0,0 +1,106 @@ +# imag-ruby + +A Ruby gem for scripting imag modules. + +## How does this work? + +Well, as we have some problems with lifetimes here, we have a fairly complex +codebase in this crate. + +### The Problem + +The Problem is, that `libimagstore::store::FileLockEntry<'a>` has a lifetime. If +we would wrap this object into a ruru wrapper and pass to the Ruby code, we +couldn't guarantee anymore that the lifetime holds. + +The problem is simple, you see... + +### The solution? + +Never pass anything to the Ruby code. + +Yes, exactly. The Ruby code only sees 'handles'. It never actually gets the +`Store` object either. +We move the `Store` Object into a `Cache` object (actually, the Ruby code could +have multiple `Store` objects to work with this way) and return a `StoreHandle` +to the Ruby code (which is a UUID underneath). + +Also, the Ruby code never actually touches a `FileLockEntry` - it only gets a +Handle for each `FileLockEntry` - which is a tuple of the `StoreHandle` and the +`libimagstore::storeid::StoreId` for the Entry. + +Each operation on a `FileLockEntry` is then wrapped by this very library. Each +time `FileLockEntry` is touched, this library fetches the appropriate `Store` +object from the static `Cache`, then fetches the `FileLockEntry` object from it, +does the operation and then drops the object (which implies that the actual +`FileLockEntry` is `update()`d!). + +### The Hell? + +Yes, I know this is a lot of overhead. But what are we talking about here? This +is Ruby code we're talking about here, so speed is not our concern. + +You could argue this is a hell of complexity introduced in this library and yes +it is. +If there are bugs (and I bet there are) they would be complex as hell. +But that's it... if you have a better approach, please file a PR. + +## Tests? + +We have tests Ruby scripts in `./test`, they are not executed by travis-ci, as +we need Ruby `2.3.0` for this and travis has `2.2.0` as latest version. +But I hope we get it in travis soonish. + +## Ruby gem? + +This crate will contain both the Rust bindings for imag using `ruru` and a bunch +of wrapper code for the actual `imag` gem. + +We are not there yet, though. + +### Why another layer of indirection? + +As "ruru" does not yet support modules (which is sad btw) we would end up with +functions for all the things. + +E.G.: `imag_runtime_setup()` instead of `Imag::Runtime::setup()` + +I want to add a Ruby gem to wrap these things. + +So basically a piece of Ruby which uses `imag.rb` (the Rust gem) to build +`imag` as a gem which then exports a fine module system. + +### Ideas for module system: + +```text +Imag (Module) + Runtime (Class) + Store (Class) + Entry (Class) + EntryHeader (Class) + EntryContent (Class (inherits from String)) + StoreId (Class) +``` + +I would name the types the same as in the Rust codebase, to avoid confusion. +Only exception would be the `Entry` class, which would be a `FileLockEntry` +underneath. + +If we adapt `libimagentrytag` and the other `libimagentry*` +libraries, we would extend this type. + +## More plans + +I want to pull these libraries into the Ruby bindings: + +* libimagentryedit +* libimagentryfilter +* libimagentrylink +* libimagentrylist +* libimagentrymarkdown +* libimagentrytag +* libimagentryview + +Which all provide functions on top of `libimagstore::store::{FileLock,}Entry`, +so we will implement them on `Imag::Entry`. + diff --git a/libimagruby/Rakefile b/libimagruby/Rakefile new file mode 100644 index 00000000..43022f71 --- /dev/null +++ b/libimagruby/Rakefile @@ -0,0 +1,2 @@ +require "bundler/gem_tasks" +task :default => :spec diff --git a/libimagruby/imag.gemspec b/libimagruby/imag.gemspec new file mode 100644 index 00000000..c6066912 --- /dev/null +++ b/libimagruby/imag.gemspec @@ -0,0 +1,26 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'imag/version' + +Gem::Specification.new do |spec| + spec.name = "imag" + spec.version = Imag::VERSION + spec.authors = ["Matthias Beyer"] + spec.email = ["mail@beyermatthias.de"] + + spec.summary = %q{A Ruby gem to script imag.} + spec.description = %q{A Ruby gem to script imag, the personal information management suite for the commandline} + spec.homepage = "http://imag-pim.org" + + spec.files = `git ls-files -z`.split("\x0").reject do |f| + f.match(%r{^(test|spec|features)/}) + end + + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_development_dependency "bundler", "~> 1.13" + spec.add_development_dependency "rake", "~> 10.0" +end diff --git a/libimagruby/lib/imag.rb b/libimagruby/lib/imag.rb new file mode 100644 index 00000000..b8c91115 --- /dev/null +++ b/libimagruby/lib/imag.rb @@ -0,0 +1,96 @@ +#!/usr/bin/env ruby + +module Imag + + IMAG_INIT_FN_NAME = 'imag_ruby_initialize' + + def self.setup binary_path + require binary_path + + self.core_setup + self.classes_setup + end + + module Logger + + def self.init debug, verbose, color + RImag.init_logger debug, verbose, color + end + + def self.trace msg + RImag.trace msg + end + + def self.dbg msg + RImag.dbg msg + end + + def self.debug msg + RImag.debug msg + end + + def self.info msg + RImag.info msg + end + + def self.warn msg + RImag.warn msg + end + + def self.error msg + RImag.error msg + end + + end + + private + + def self.class_names + [ + :StoreId , + :StoreHandle , + :FileLockEntryHandle , + :EntryHeader , + :EntryContent , + ] + end + + def self.core_setup + self.class_names.map {|n| [n, "R#{n}".to_sym ] }.each do |elem| + Imag.const_set elem.first, Kernel.const_get(elem.last) + end + end + + def self.classes_setup + self.class_storeid_setup + end + + def self.class_storeid_setup + Imag::StoreId.class_exec do + def to_s + self.to_str + end + end + end + +end + +if __FILE__ == $0 + puts "Running some tests..." + puts "I hope you passed the library object as first argument..." + begin + Imag.setup ARGV.first + rescue Exception => e + puts "Seems not to be the case... or something else went wrong..." + puts e + exit 1 + end + + Imag::Logger.init true, true, true + Imag::Logger.info "The Logger should work now" + + Imag::Logger.info "Lets see whether we have properly setup StoreId" + Imag::Logger.info Imag::StoreId::new_baseless("baselessId").to_s + Imag::Logger.info "Seems good." +end + diff --git a/libimagruby/lib/imag/version.rb b/libimagruby/lib/imag/version.rb new file mode 100644 index 00000000..c2a2473f --- /dev/null +++ b/libimagruby/lib/imag/version.rb @@ -0,0 +1,3 @@ +module Imag + VERSION = "0.1.0" +end 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 |