[niepce: 23/29] rust: now use library in Rust



commit 4d36f02a75206bcfa60bd421683ce9e4f3895da8
Author: Hubert Figuière <hub figuiere net>
Date:   Mon Sep 11 00:02:43 2017 -0400

    rust: now use library in Rust
    
    - remove sqlite C++ code
    - add more bindings
    - lib notifications are now in Rust

 .gitignore                                    |    4 -
 Cargo.toml                                    |    3 +-
 build.rs                                      |   44 +-
 camerawire/src/Makefile.am                    |    2 +
 configure.ac                                  |    2 -
 src/engine/Makefile.am                        |   20 +-
 src/engine/db/bindings.hpp                    |    7 +
 src/engine/db/filebundle.rs                   |   34 +
 src/engine/db/label.cpp                       |   38 +
 src/engine/db/label.hpp                       |   54 +-
 src/engine/db/label.rs                        |   37 +
 src/engine/db/libfile.cpp                     |   63 +-
 src/engine/db/libfile.hpp                     |   50 +-
 src/engine/db/libfile.rs                      |   24 +-
 src/engine/db/libfolder.cpp                   |   33 +-
 src/engine/db/libfolder.hpp                   |   27 +-
 src/engine/db/libfolder.rs                    |    1 +
 src/engine/db/libmetadata.cpp                 |  282 +++-----
 src/engine/db/libmetadata.hpp                 |   56 +-
 src/engine/db/libmetadata.rs                  |  182 +++++
 src/engine/db/library.cpp                     |  996 +------------------------
 src/engine/db/library.hpp                     |  200 +-----
 src/engine/db/library.rs                      |  295 +++++++-
 src/engine/db/mod.rs                          |    1 +
 src/engine/db/properties-def.hpp              |    2 +-
 src/engine/db/test_library.cpp                |  109 ---
 src/engine/importer/cameraimporter.cpp        |    2 +
 src/engine/library/commands.cpp               |  226 ------
 src/engine/library/commands.hpp               |   75 +--
 src/engine/library/commands.rs                |  239 ++++++
 src/engine/library/mod.rs                     |    4 +
 src/engine/library/notification.cpp           |   52 +-
 src/engine/library/notification.hpp           |  231 ++----
 src/engine/library/notification.rs            |  203 +++++
 src/engine/library/op.cpp                     |    4 +-
 src/engine/library/op.hpp                     |   10 +-
 src/engine/library/test_opqueue.cpp           |    2 +-
 src/engine/library/thumbnailcache.cpp         |    4 +-
 src/engine/library/thumbnailcache.hpp         |    2 +-
 src/engine/mod.rs                             |    2 +-
 src/fwk/base/colour.cpp                       |   73 +-
 src/fwk/base/colour.hpp                       |   35 +-
 src/fwk/base/date.cpp                         |   29 +-
 src/fwk/base/date.hpp                         |   32 +-
 src/fwk/base/date.rs                          |   11 +
 src/fwk/base/propertybag.cpp                  |   36 +
 src/fwk/base/propertybag.hpp                  |   43 +-
 src/fwk/base/rgbcolour.rs                     |   65 ++-
 src/fwk/base/rust.hpp                         |   12 +
 src/fwk/base/t/testpropertybag.cpp            |    2 +-
 src/fwk/toolkit/gdkutils.cpp                  |    9 +-
 src/fwk/toolkit/gdkutils.hpp                  |    2 +-
 src/fwk/toolkit/metadatawidget.cpp            |   10 +-
 src/fwk/utils/Makefile.am                     |   32 +-
 src/fwk/utils/db/Makefile.am                  |    3 -
 src/fwk/utils/db/iconnectiondriver.hpp        |  122 ---
 src/fwk/utils/db/iconnectionmanagerdriver.hpp |  106 ---
 src/fwk/utils/db/insertstatement.cpp          |  115 ---
 src/fwk/utils/db/insertstatement.hpp          |   61 --
 src/fwk/utils/db/sqlite/Makefile.am           |    4 -
 src/fwk/utils/db/sqlite/sqlitecnxdrv.cpp      |  587 ---------------
 src/fwk/utils/db/sqlite/sqlitecnxdrv.hpp      |  113 ---
 src/fwk/utils/db/sqlite/sqlitecnxmgrdrv.cpp   |  156 ----
 src/fwk/utils/db/sqlite/sqlitecnxmgrdrv.hpp   |   60 --
 src/fwk/utils/db/sqlstatement.cpp             |  133 ----
 src/fwk/utils/db/sqlstatement.hpp             |  138 ----
 src/fwk/utils/db/test_db.cpp                  |   56 --
 src/fwk/utils/db/test_db2.cpp                 |   58 --
 src/fwk/utils/db/test_db3.cpp                 |   83 --
 src/fwk/utils/db/test_db4.cpp                 |   62 --
 src/fwk/utils/exempi.cpp                      |  224 +------
 src/fwk/utils/exempi.hpp                      |   65 +--
 src/fwk/utils/exempi.rs                       |   59 ++-
 src/fwk/utils/files.cpp                       |   16 +
 src/fwk/utils/files.hpp                       |   27 +-
 src/lib.rs                                    |    8 +
 src/libraryclient/Makefile.am                 |    3 +-
 src/libraryclient/clientimpl.cpp              |   67 +-
 src/libraryclient/clientimpl.hpp              |    2 +-
 src/libraryclient/locallibraryserver.cpp      |    3 +-
 src/libraryclient/locallibraryserver.hpp      |   20 +-
 src/libraryclient/test_worker.cpp             |    4 +-
 src/libraryclient/uidataprovider.cpp          |   28 +-
 src/libraryclient/uidataprovider.hpp          |   15 +-
 src/niepce/Makefile.am                        |    1 +
 src/niepce/main.cpp                           |    2 +-
 src/niepce/modules/map/mapmodule.cpp          |   10 +-
 src/niepce/notificationcenter.cpp             |    8 +-
 src/niepce/notificationcenter.hpp             |    1 +
 src/niepce/ui/dialogs/editlabels.cpp          |   15 +-
 src/niepce/ui/dialogs/editlabels.hpp          |    2 +-
 src/niepce/ui/gridviewmodule.cpp              |   22 +-
 src/niepce/ui/imageliststore.cpp              |   48 +-
 src/niepce/ui/imageliststore.hpp              |    1 +
 src/niepce/ui/librarycellrenderer.cpp         |    2 +-
 src/niepce/ui/metadatapanecontroller.cpp      |    4 +-
 src/niepce/ui/metadatapanecontroller.hpp      |    2 +-
 src/niepce/ui/niepcewindow.cpp                |   46 +-
 src/niepce/ui/niepcewindow.hpp                |    1 +
 src/niepce/ui/selectioncontroller.cpp         |    4 +-
 src/niepce/ui/workspacecontroller.cpp         |   75 +--
 src/niepce/ui/workspacecontroller.hpp         |    4 +-
 102 files changed, 2015 insertions(+), 4644 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 7baae6b..6861622 100644
--- a/.gitignore
+++ b/.gitignore
@@ -83,10 +83,6 @@ src/fwk/base/testdate
 src/fwk/base/testmap
 src/fwk/base/testoption
 src/fwk/base/testpropertybag
-src/fwk/utils/test_db
-src/fwk/utils/test_db2
-src/fwk/utils/test_db3
-src/fwk/utils/test_db4
 src/fwk/utils/testfiles
 src/fwk/utils/testpathutils
 src/fwk/utils/teststringutils
diff --git a/Cargo.toml b/Cargo.toml
index e4aee8a..3892f64 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,8 +13,7 @@ gio = "0.2.0"
 #gphoto = "0.1.1"
 
 [build-dependencies]
-# We need git master as currently it the crate version fail on shared_ptr<>
-bindgen = { git = "https://github.com/servo/rust-bindgen.git"; }
+bindgen = "0.30.0"
 pkg-config = "0.3.9"
 
 [lib]
diff --git a/build.rs b/build.rs
index 1e1d139..6bd2964 100644
--- a/build.rs
+++ b/build.rs
@@ -1,27 +1,61 @@
 extern crate bindgen;
+extern crate pkg_config;
 
 use std::env;
 use std::path::PathBuf;
 
 fn main() {
+    let exempi = pkg_config::Config::new()
+        .print_system_libs(false)
+        .probe("exempi-2.0")
+        .unwrap();
     // The bindgen::Builder is the main entry point
     // to bindgen, and lets you build up options for
     // the resulting bindings.
-    let builder = bindgen::Builder::default()
-        // Do not generate unstable Rust code that
-        // requires a nightly rustc and enabling
-        // unstable features.
-        .no_unstable_rust()
+    let mut builder = bindgen::Builder::default()
         .enable_cxx_namespaces()
         .generate_comments(false)
+        .generate_inline_functions(true)
         // The input header we would like to generate
         // bindings for.
         .whitelisted_type("eng::NiepceProperties")
+        .whitelisted_type("eng::LibFileType")
+        .whitelisted_type("eng::LibNotificationType")
+        .whitelisted_type("eng::LnFileMove")
+        .whitelisted_type("eng::LnFolderCount")
+        .whitelisted_type("eng::QueriedContent")
+        .opaque_type("eng::QueriedContent")
+        .whitelisted_type("eng::metadata_desc_t")
+        .whitelisted_type("eng::LibraryManaged")
+        .whitelisted_type("fwk::FileList")
+        // PropertyValue is complicated
+        .whitelisted_type("fwk::PropertyValue")
+        .opaque_type("fwk::PropertyValue")
+        .whitelisted_type("fwk::StringArray")
+        .opaque_type("fwk::StringArray")
+        .whitelisted_function("fwk::is_empty")
+        .whitelisted_function("fwk::is_integer")
+        .whitelisted_function("fwk::is_string")
+        .whitelisted_function("fwk::is_string_array")
+        .whitelisted_function("fwk::is_date")
+        .whitelisted_function("fwk::get_integer")
+        .whitelisted_function("fwk::get_date")
+        .whitelisted_function("fwk::get_string_cstr")
+        .whitelisted_function("fwk::get_string_array")
+        .whitelisted_function("fwk::string_array_len")
+        .whitelisted_function("fwk::string_array_at_cstr")
+        .whitelisted_type("eng::IndexToXmp")
+        .whitelisted_function("eng::property_index_to_xmp")
         .header("src/engine/db/bindings.hpp")
         .clang_arg("--std=c++11")
         .clang_arg("-DRUST_BINDGEN=1")
         .clang_arg("-I./src");
 
+    for include in exempi.include_paths.into_iter().map(|path| {
+        format!("-I{}", path.to_str().unwrap_or("."))
+    }) {
+        builder = builder.clang_arg(include);
+    }
 
     // Finish the builder and generate the bindings.
     let bindings = builder.generate()
diff --git a/camerawire/src/Makefile.am b/camerawire/src/Makefile.am
index b414207..a5020f2 100644
--- a/camerawire/src/Makefile.am
+++ b/camerawire/src/Makefile.am
@@ -17,7 +17,9 @@ camerawire_LDADD = \
        $(top_builddir)/src/fwk/toolkit/libniepceframework.a \
        $(top_builddir)/src/fwk/utils/libniepceutils.a \
        $(top_builddir)/src/fwk/base/libfwkbase.a \
+       $(top_builddir)/target/debug/libniepce_rust.a \
        @FRAMEWORK_LIBS@ \
        @GPHOTO_LIBS@ \
        @OPENRAW_LIBS@ \
+       -ldl \
        $(NULL)
diff --git a/configure.ac b/configure.ac
index 368f4aa..9bb42bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -180,8 +180,6 @@ src/ext/libview/Makefile
 src/fwk/Makefile
 src/fwk/base/Makefile
 src/fwk/utils/Makefile
-src/fwk/utils/db/Makefile
-src/fwk/utils/db/sqlite/Makefile
 src/fwk/toolkit/Makefile
 src/engine/Makefile
 src/libraryclient/Makefile
diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am
index 2824724..f3e0da0 100644
--- a/src/engine/Makefile.am
+++ b/src/engine/Makefile.am
@@ -4,11 +4,12 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/  @FRAMEWORK_CFLAGS@ \
        @GPHOTO_CFLAGS@ \
        $(NULL)
 
-TESTS = test_library test_filebundle test_opqueue
+TESTS = test_filebundle test_opqueue
 
 TEST_LIBS =  \
        libniepceengine.a \
        $(top_builddir)/target/debug/libniepce_rust.a \
+       libniepceengineglue.a \
        $(top_builddir)/src/fwk/utils/libniepceutils.a \
        $(top_builddir)/src/fwk/toolkit/libniepceframework.a \
        $(top_builddir)/src/fwk/base/libfwkbase.a \
@@ -19,10 +20,7 @@ TEST_LIBS =  \
        $(NULL)
 
 
-check_PROGRAMS = test_library test_filebundle test_opqueue
-
-test_library_SOURCES = db/test_library.cpp
-test_library_LDADD = $(TEST_LIBS)
+check_PROGRAMS = test_filebundle test_opqueue
 
 test_filebundle_SOURCES = db/test_filebundle.cpp
 test_filebundle_LDADD = $(TEST_LIBS)
@@ -30,14 +28,14 @@ test_filebundle_LDADD = $(TEST_LIBS)
 test_opqueue_SOURCES = library/test_opqueue.cpp library/opqueue.hpp
 test_opqueue_LDADD = $(TEST_LIBS)
 
-noinst_LIBRARIES = libniepceengine.a
+noinst_LIBRARIES = libniepceengine.a libniepceengineglue.a
 
 libniepceengine_a_SOURCES = \
        db/library.hpp db/library.cpp \
        db/librarytypes.hpp \
        db/libfile.hpp db/libfile.cpp \
        db/libfolder.hpp db/libfolder.cpp \
-       db/label.hpp \
+       db/label.hpp db/label.cpp \
        db/libmetadata.hpp db/libmetadata.cpp \
        db/keyword.hpp db/keyword.cpp \
        db/storage.hpp \
@@ -47,9 +45,8 @@ libniepceengine_a_SOURCES = \
        db/properties.hpp db/properties.cpp \
        db/properties-def.hpp \
        library/clienttypes.hpp \
-       library/notification.hpp library/notification.cpp \
        library/op.hpp library/op.cpp  \
-       library/commands.hpp library/commands.cpp \
+       library/commands.hpp \
        library/thumbnailcache.hpp library/thumbnailcache.cpp \
        library/thumbnailnotification.hpp \
        importer/iimporter.hpp \
@@ -60,3 +57,8 @@ libniepceengine_a_SOURCES = \
        importer/importedfile.hpp \
        importer/importedfile.cpp \
        $(NULL)
+
+# glue code called from Rust. Library order issue.
+libniepceengineglue_a_SOURCES = \
+       library/notification.hpp library/notification.cpp \
+       $(NULL)
diff --git a/src/engine/db/bindings.hpp b/src/engine/db/bindings.hpp
index 1d234bc..d1db23f 100644
--- a/src/engine/db/bindings.hpp
+++ b/src/engine/db/bindings.hpp
@@ -1,3 +1,10 @@
 /** @brief what to generate bindings for in Rust */
 
 #include "properties-enum.hpp"
+
+#include "engine/db/libmetadata.hpp"
+#include "engine/db/library.hpp"
+#include "engine/library/clienttypes.hpp"
+#include "engine/library/notification.hpp"
+#include "fwk/base/propertybag.hpp"
+#include "fwk/utils/files.hpp"
diff --git a/src/engine/db/filebundle.rs b/src/engine/db/filebundle.rs
index f63808c..e7e7528 100644
--- a/src/engine/db/filebundle.rs
+++ b/src/engine/db/filebundle.rs
@@ -20,9 +20,12 @@
 use libc::c_char;
 use std::ffi::CStr;
 use std::ffi::CString;
+use std::ffi::OsString;
+use std::path::Path;
 
 use super::libfile::FileType;
 use fwk::MimeType;
+use root::fwk::FileList;
 
 pub struct FileBundle {
     file_type: FileType,
@@ -48,6 +51,37 @@ impl FileBundle {
         }
     }
 
+    pub fn filter_bundles(files: &mut FileList) -> Vec<FileBundle> {
+        let mut bundles: Vec<FileBundle> = vec!();
+        unsafe { files.sort(); }
+        let len = unsafe { files.size() };
+        let mut current_base = OsString::new();
+        let mut current_bundle: Option<FileBundle> = None;
+
+        for i in 0..len {
+            let f = unsafe { files.at_cstr(i) };
+            let cstr = unsafe { CStr::from_ptr(f) }.to_string_lossy();
+            let path = Path::new(&*cstr);
+            if let Some(basename) = path.file_stem() {
+                if basename == current_base {
+                    current_bundle.as_mut().unwrap().add(&basename.to_string_lossy());
+                } else {
+                    if current_bundle.is_some() {
+                        bundles.push(current_bundle.unwrap());
+                    }
+                    let mut bundle = FileBundle::new();
+                    bundle.add(&cstr);
+                    current_base = basename.to_os_string();
+                    current_bundle = Some(bundle);
+                }
+            }
+        }
+        if current_bundle.is_some() {
+            bundles.push(current_bundle.unwrap());
+        }
+        bundles
+    }
+
     pub fn add(&mut self, path: &str) -> bool {
         dbg_out!("path {}", path);
         let mime_type = MimeType::new(path);
diff --git a/src/engine/db/label.cpp b/src/engine/db/label.cpp
new file mode 100644
index 0000000..56d477b
--- /dev/null
+++ b/src/engine/db/label.cpp
@@ -0,0 +1,38 @@
+/*
+ * niepce - engine/db/label.cpp
+ *
+ * Copyright (C) 2017 Hubert Figuière
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "label.hpp"
+
+extern "C" {
+void engine_db_label_delete(eng::Label*);
+eng::Label* engine_db_label_clone(const eng::Label*);
+}
+
+namespace eng {
+
+LabelPtr label_wrap(Label* l)
+{
+    return LabelPtr(l, &engine_db_label_delete);
+}
+
+LabelPtr label_clone(const Label* l)
+{
+    return label_wrap(engine_db_label_clone(l));
+}
+}
diff --git a/src/engine/db/label.hpp b/src/engine/db/label.hpp
index f04ba24..ddbfac2 100644
--- a/src/engine/db/label.hpp
+++ b/src/engine/db/label.hpp
@@ -17,55 +17,35 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#pragma once
 
-#ifndef __NIEPCE_DB_LABEL_HPP__
-#define __NIEPCE_DB_LABEL_HPP__
-
+#include <memory>
 #include <string>
 #include <vector>
-#include <memory>
 
-#include "fwk/base/colour.hpp"
 #include "engine/db/librarytypes.hpp"
 
-namespace eng {
-
-/** represent a label assigned to a library object
- * There shouldn't be much instances of this.
- */
-class Label
-{
-public:
-    typedef std::shared_ptr<Label> Ptr;
-    typedef std::vector<Ptr> List;
-    typedef std::shared_ptr<List> ListPtr;
+namespace fwk {
+class RgbColour;
+}
 
-    Label(library_id_t _id, const std::string & _label, const std::string & _colourstring)
-        : m_id(_id), m_label(_label)
-        , m_colour(_colourstring)
-        {
-        }
+namespace eng {
 
-    library_id_t id() const
-        { return m_id; }
-    const std::string & label()
-        { return m_label; }
-    void set_label(const std::string & l)
-        { m_label = l; }
-    const fwk::RgbColour & colour() const
-        { return m_colour; }
-    void set_colour(const fwk::RgbColour & c)
-        { m_colour = c; }
+class Label;
+typedef std::shared_ptr<Label> LabelPtr;
+typedef std::vector<LabelPtr> LabelList;
+typedef std::shared_ptr<LabelList> LabelListPtr;
 
-private:
-    library_id_t          m_id;
-    std::string      m_label;
-    fwk::RgbColour    m_colour;
-};
+LabelPtr label_wrap(Label*);
+LabelPtr label_clone(const Label*);
+}
 
+extern "C" {
+eng::library_id_t engine_db_label_id(const eng::Label*);
+const fwk::RgbColour* engine_db_label_colour(const eng::Label*);
+const char* engine_db_label_label(const eng::Label*);
 }
 
-#endif
 /*
   Local Variables:
   mode:c++
diff --git a/src/engine/db/label.rs b/src/engine/db/label.rs
index 0b57103..9a5a435 100644
--- a/src/engine/db/label.rs
+++ b/src/engine/db/label.rs
@@ -17,6 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+use libc::c_char;
+use std::ffi::CString;
 use std::str::FromStr;
 use rusqlite;
 
@@ -24,9 +26,11 @@ use super::LibraryId;
 use super::FromDb;
 use fwk::base::rgbcolour::RgbColour;
 
+#[derive(Clone)]
 pub struct Label {
     id: LibraryId,
     label: String,
+    pub cstr: CString,
     colour: RgbColour,
 }
 
@@ -37,6 +41,7 @@ impl Label {
         Label {
             id: id,
             label: String::from(label),
+            cstr: CString::new("").unwrap(),
             colour: colour
         }
     }
@@ -77,3 +82,35 @@ impl FromDb for Label {
         Label::new(row.get(0), &label, &colour)
     }
 }
+
+
+#[no_mangle]
+pub fn engine_db_label_delete(l: *mut Label) {
+    unsafe { Box::from_raw(l) };
+}
+
+#[no_mangle]
+pub fn engine_db_label_clone(l: &Label) -> *mut Label {
+    Box::into_raw(Box::new(l.clone()))
+}
+
+#[no_mangle]
+pub fn engine_db_label_id(l: &Label) -> LibraryId {
+    l.id()
+}
+
+#[no_mangle]
+pub fn engine_db_label_label(this: &mut Label) -> *const c_char {
+    let cstr;
+    {
+        let s = this.label();
+        cstr = CString::new(s).unwrap();
+    }
+    this.cstr = cstr;
+    this.cstr.as_ptr()
+}
+
+#[no_mangle]
+pub fn engine_db_label_colour(l: &Label) -> *const RgbColour {
+    l.colour()
+}
diff --git a/src/engine/db/libfile.cpp b/src/engine/db/libfile.cpp
index 2c21cf5..206e585 100644
--- a/src/engine/db/libfile.cpp
+++ b/src/engine/db/libfile.cpp
@@ -17,25 +17,47 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
-#include "fwk/base/debug.hpp"
 #include "libfile.hpp"
+#include "engine/library/notification.hpp"
+#include "fwk/base/debug.hpp"
 #include "properties.hpp"
 
+extern "C" {
+eng::LibFile *engine_db_libfile_new(eng::library_id_t id,
+                                    eng::library_id_t folder_id,
+                                    eng::library_id_t fs_file_id,
+                                    const char *path, const char *name);
+void engine_db_libfile_delete(eng::LibFile *);
+}
 
-extern "C" eng::LibFile* engine_db_libfile_new(eng::library_id_t id,
-                                               eng::library_id_t folder_id,
-                                               eng::library_id_t fs_file_id,
-                                               const char* path, const char* name);
-extern "C" void engine_db_libfile_delete(eng::LibFile*);
+namespace eng {
+
+// some glue for rust
+QueriedContent::QueriedContent(library_id_t _container)
+    : container(_container)
+    , files(new LibFileList)
+{
+}
+
+void QueriedContent::push(LibFile *f)
+{
+    files->push_back(eng::libfile_wrap(f));
+}
+}
 
 namespace eng {
 
-LibFilePtr libfile_new(library_id_t id, library_id_t folder_id, library_id_t fs_file_id,
-                       const char* path, const char* name) {
-    return LibFilePtr(
-        engine_db_libfile_new(id, folder_id, fs_file_id, path, name),
-        &engine_db_libfile_delete);
+LibFilePtr libfile_new(library_id_t id, library_id_t folder_id,
+                       library_id_t fs_file_id, const char *path,
+                       const char *name)
+{
+    return libfile_wrap(
+        engine_db_libfile_new(id, folder_id, fs_file_id, path, name));
+}
+
+LibFilePtr libfile_wrap(LibFile *lf)
+{
+    return LibFilePtr(lf, &engine_db_libfile_delete);
 }
 
 /**
@@ -46,25 +68,16 @@ LibFilePtr libfile_new(library_id_t id, library_id_t folder_id, library_id_t fs_
  */
 LibFileType mimetype_to_filetype(fwk::MimeType mime)
 {
-    if(mime.isDigicamRaw())
-    {
+    if (mime.isDigicamRaw()) {
         return LibFileType::RAW;
-    }
-    else if(mime.isImage())
-    {
+    } else if (mime.isImage()) {
         return LibFileType::IMAGE;
-    }
-    else if(mime.isMovie())
-    {
+    } else if (mime.isMovie()) {
         return LibFileType::VIDEO;
-    }
-    else
-    {
+    } else {
         return LibFileType::UNKNOWN;
     }
 }
-
-
 }
 /*
   Local Variables:
diff --git a/src/engine/db/libfile.hpp b/src/engine/db/libfile.hpp
index 00c45a6..17a47ff 100644
--- a/src/engine/db/libfile.hpp
+++ b/src/engine/db/libfile.hpp
@@ -19,14 +19,17 @@
 
 #pragma once
 
-#include <string>
 #include <list>
 #include <memory>
+#include <string>
 
+#if !RUST_BINDGEN
 #include "fwk/toolkit/mimetype.hpp"
-#include "fwk/base/propertybag.hpp"
-#include "engine/db/librarytypes.hpp"
+#endif
+
 #include "engine/db/keyword.hpp"
+#include "engine/db/librarytypes.hpp"
+#include "fwk/base/propertybag.hpp"
 
 namespace eng {
 
@@ -38,32 +41,39 @@ enum class LibFileType {
     VIDEO = 4
 };
 
+#if !RUST_BINDGEN
 LibFileType mimetype_to_filetype(fwk::MimeType mime);
+#endif
 
 class LibFile;
 typedef std::shared_ptr<LibFile> LibFilePtr;
-typedef std::weak_ptr< LibFile> LibFileWeakPtr;
+typedef std::weak_ptr<LibFile> LibFileWeakPtr;
 typedef std::list<LibFilePtr> LibFileList;
 typedef std::shared_ptr<LibFileList> LibFileListPtr;
 
-LibFilePtr libfile_new(library_id_t, library_id_t, library_id_t, const char*, const char*);
+LibFilePtr libfile_new(library_id_t, library_id_t, library_id_t, const char *,
+                       const char *);
+LibFilePtr libfile_wrap(LibFile *);
 }
 
-extern "C" const char* engine_db_libfile_path(const eng::LibFile*);
-extern "C" eng::library_id_t engine_db_libfile_id(const eng::LibFile*);
-extern "C" eng::library_id_t engine_db_libfile_folderid(const eng::LibFile*);
-extern "C" int32_t engine_db_libfile_orientation(const eng::LibFile*);
-extern "C" int32_t engine_db_libfile_rating(const eng::LibFile*);
-extern "C" int32_t engine_db_libfile_label(const eng::LibFile*);
-extern "C" int32_t engine_db_libfile_flag(const eng::LibFile*);
-extern "C" int32_t engine_db_libfile_property(const eng::LibFile*, fwk::PropertyIndex);
-extern "C" eng::LibFileType engine_db_libfile_file_type(const eng::LibFile*);
-extern "C" void engine_db_libfile_set_orientation(eng::LibFile*, int32_t);
-extern "C" void engine_db_libfile_set_rating(eng::LibFile*, int32_t);
-extern "C" void engine_db_libfile_set_label(eng::LibFile*, int32_t);
-extern "C" void engine_db_libfile_set_flag(eng::LibFile*, int32_t);
-extern "C" void engine_db_libfile_set_property(const eng::LibFile*, fwk::PropertyIndex, int32_t);
-extern "C" void engine_db_libfile_set_file_type(eng::LibFile*, eng::LibFileType);
+extern "C" {
+const char *engine_db_libfile_path(const eng::LibFile *);
+eng::library_id_t engine_db_libfile_id(const eng::LibFile *);
+eng::library_id_t engine_db_libfile_folderid(const eng::LibFile *);
+int32_t engine_db_libfile_orientation(const eng::LibFile *);
+int32_t engine_db_libfile_rating(const eng::LibFile *);
+int32_t engine_db_libfile_label(const eng::LibFile *);
+int32_t engine_db_libfile_flag(const eng::LibFile *);
+int32_t engine_db_libfile_property(const eng::LibFile *, fwk::PropertyIndex);
+eng::LibFileType engine_db_libfile_file_type(const eng::LibFile *);
+void engine_db_libfile_set_orientation(eng::LibFile *, int32_t);
+void engine_db_libfile_set_rating(eng::LibFile *, int32_t);
+void engine_db_libfile_set_label(eng::LibFile *, int32_t);
+void engine_db_libfile_set_flag(eng::LibFile *, int32_t);
+void engine_db_libfile_set_property(const eng::LibFile *, fwk::PropertyIndex,
+                                    int32_t);
+void engine_db_libfile_set_file_type(eng::LibFile *, eng::LibFileType);
+}
 
 /*
   Local Variables:
diff --git a/src/engine/db/libfile.rs b/src/engine/db/libfile.rs
index c42a9c7..4b8c899 100644
--- a/src/engine/db/libfile.rs
+++ b/src/engine/db/libfile.rs
@@ -29,19 +29,9 @@ use super::LibraryId;
 use super::fsfile::FsFile;
 use fwk::base::PropertyIndex;
 use root::eng::NiepceProperties as Np;
+pub use root::eng::LibFileType as FileType;
 use fwk;
 
-#[repr(i32)]
-#[allow(non_camel_case_types)]
-#[derive(Clone,PartialEq)]
-pub enum FileType {
-    UNKNOWN = 0,
-    RAW = 1,
-    RAW_JPEG = 2,
-    IMAGE = 3,
-    VIDEO = 4
-}
-
 impl From<i32> for FileType {
     fn from(t: i32) -> Self {
         match t {
@@ -55,6 +45,18 @@ impl From<i32> for FileType {
     }
 }
 
+impl Into<i32> for FileType {
+    fn into(self) -> i32 {
+        match self {
+            FileType::UNKNOWN => 0,
+            FileType::RAW => 1,
+            FileType::RAW_JPEG => 2,
+            FileType::IMAGE => 3,
+            FileType::VIDEO => 4,
+        }
+    }
+}
+
 pub struct LibFile {
     id: LibraryId,
     folder_id: LibraryId,
diff --git a/src/engine/db/libfolder.cpp b/src/engine/db/libfolder.cpp
index 176f60e..76b3cbd 100644
--- a/src/engine/db/libfolder.cpp
+++ b/src/engine/db/libfolder.cpp
@@ -19,38 +19,17 @@
 
 #include "libfolder.hpp"
 
-extern "C" eng::LibFolder* engine_db_libfolder_new(eng::library_id_t id, const char* name);
-extern "C" void engine_db_libfolder_delete(eng::LibFolder*);
+extern "C" eng::LibFolder *engine_db_libfolder_new(eng::library_id_t id,
+                                                   const char *name);
+extern "C" void engine_db_libfolder_delete(eng::LibFolder *);
 
 namespace eng {
 
-LibFolderPtr libfolder_new(eng::library_id_t id, const char* name) {
-  return LibFolderPtr(
-    engine_db_libfolder_new(id, name), &engine_db_libfolder_delete);
-}
-
-const char* libfolder_read_db_columns()
-{
-    return "id,name,virtual,locked,expanded";
-}
-
-LibFolderPtr libfolder_read_from(const db::IConnectionDriver::Ptr & db)
+LibFolderPtr libfolder_new(eng::library_id_t id, const char *name)
 {
-    library_id_t id;
-    std::string name;
-    int32_t virt_type, locked, expanded;
-    db->get_column_content(0, id);
-    db->get_column_content(1, name);
-    db->get_column_content(2, virt_type);
-    db->get_column_content(3, locked);
-    db->get_column_content(4, expanded);
-    LibFolderPtr f(libfolder_new(id, name.c_str()));
-    engine_db_libfolder_set_virtual_type(f.get(), virt_type);
-    engine_db_libfolder_set_locked(f.get(), (bool)locked);
-    engine_db_libfolder_set_expanded(f.get(), (bool)expanded);
-    return f;
+    return LibFolderPtr(engine_db_libfolder_new(id, name),
+                        &engine_db_libfolder_delete);
 }
-
 }
 /*
   Local Variables:
diff --git a/src/engine/db/libfolder.hpp b/src/engine/db/libfolder.hpp
index d654bc3..1e2a4ec 100644
--- a/src/engine/db/libfolder.hpp
+++ b/src/engine/db/libfolder.hpp
@@ -17,15 +17,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #pragma once
 
-#include <string>
 #include <list>
 #include <memory>
+#include <string>
 
 #include "engine/db/librarytypes.hpp"
-#include "fwk/utils/db/iconnectiondriver.hpp"
 
 namespace eng {
 class LibFolder;
@@ -40,21 +38,18 @@ enum class LibFolderVirtualType {
     _LAST
 };
 
-const char * libfolder_read_db_columns();
-LibFolderPtr libfolder_read_from(const db::IConnectionDriver::Ptr & db);
-
-LibFolderPtr libfolder_new(library_id_t id, const char* name);
-
-extern "C" library_id_t engine_db_libfolder_id(const LibFolder*);
-extern "C" const char* engine_db_libfolder_name(const LibFolder*);
-extern "C" int32_t engine_db_libfolder_virtual_type(const LibFolder*);
-extern "C" bool engine_db_libfolder_expanded(const LibFolder*);
-extern "C" void engine_db_libfolder_set_locked(const LibFolder*, bool);
-extern "C" void engine_db_libfolder_set_expanded(const LibFolder*, bool);
-extern "C" void engine_db_libfolder_set_virtual_type(const LibFolder*, int32_t);
+LibFolderPtr libfolder_new(library_id_t id, const char *name);
 
+extern "C" {
+library_id_t engine_db_libfolder_id(const LibFolder *);
+const char *engine_db_libfolder_name(const LibFolder *);
+int32_t engine_db_libfolder_virtual_type(const LibFolder *);
+bool engine_db_libfolder_expanded(const LibFolder *);
+void engine_db_libfolder_set_locked(const LibFolder *, bool);
+void engine_db_libfolder_set_expanded(const LibFolder *, bool);
+void engine_db_libfolder_set_virtual_type(const LibFolder *, int32_t);
+}
 }
-
 
 /*
   Local Variables:
diff --git a/src/engine/db/libfolder.rs b/src/engine/db/libfolder.rs
index 824bef2..d87b7ae 100644
--- a/src/engine/db/libfolder.rs
+++ b/src/engine/db/libfolder.rs
@@ -42,6 +42,7 @@ impl From<i32> for VirtualType {
     }
 }
 
+#[derive(Clone)]
 pub struct LibFolder {
     id: LibraryId,
     name: String,
diff --git a/src/engine/db/libmetadata.cpp b/src/engine/db/libmetadata.cpp
index 7728012..9075ae1 100644
--- a/src/engine/db/libmetadata.cpp
+++ b/src/engine/db/libmetadata.cpp
@@ -1,7 +1,7 @@
 /*
  * niepce - db/libmetadata.cpp
  *
- * Copyright (C) 2008-2013 Hubert Figuiere
+ * Copyright (C) 2008-2017 Hubert Figuière
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,156 +17,83 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <string.h>
-#include <time.h>
 #include <exempi/xmpconsts.h>
 #include <exempi/xmperrors.h>
+#include <string.h>
+#include <time.h>
 
+#include "fwk/base/date.hpp"
 #include "fwk/base/debug.hpp"
+#include "fwk/base/rust.hpp"
+#include "fwk/utils/exempi.hpp"
 #include "fwk/utils/stringutils.hpp"
 #include "libmetadata.hpp"
 #include "properties.hpp"
 
 namespace eng {
 
-typedef std::map<fwk::PropertyIndex, std::pair<const char*, const char *> > PropsToXmpMap;
+typedef std::map<fwk::PropertyIndex, std::pair<const char *, const char *>>
+    PropsToXmpMap;
 
 /** get the mapping of properties to XMP */
-const PropsToXmpMap & props_to_xmp_map();
+const PropsToXmpMap &props_to_xmp_map();
 
-
-const PropsToXmpMap & props_to_xmp_map()
+const PropsToXmpMap &props_to_xmp_map()
 {
     static PropsToXmpMap s_props_map;
-    if(s_props_map.empty()) {
+    if (s_props_map.empty()) {
 
-#define DEFINE_PROPERTY(a,b,c,d,e)                                       \
-        s_props_map.insert(std::make_pair(b, std::make_pair(c,d)));
+#define DEFINE_PROPERTY(a, b, c, d, e)                                         \
+    s_props_map.insert(std::make_pair(b, std::make_pair(c, d)));
 #include "engine/db/properties-def.hpp"
 #undef DEFINE_PROPERTY
-
     }
     return s_props_map;
 }
 
-bool
-LibMetadata::property_index_to_xmp(fwk::PropertyIndex index, 
-                                   const char * & ns, const char * & property)
+IndexToXmp property_index_to_xmp(fwk::PropertyIndex index)
 {
-    const PropsToXmpMap & propmap = props_to_xmp_map();
+    const PropsToXmpMap &propmap = props_to_xmp_map();
     PropsToXmpMap::const_iterator iter = propmap.find(index);
-    if(iter == propmap.end()) {
+    if (iter == propmap.end()) {
         // not found
-        return false;
+        return {ns : nullptr, property : nullptr};
     }
-    if(iter->second.first == NULL || iter->second.second == NULL) {
+    if (iter->second.first == NULL || iter->second.second == NULL) {
         // no XMP equivalent
-        return false;
+        return {ns : nullptr, property : nullptr};
     }
-    ns = iter->second.first;
-    property = iter->second.second;
-    return true;
+    return {ns : iter->second.first, property : iter->second.second};
 }
 
-
-
-LibMetadata::LibMetadata(library_id_t _id)
-       : XmpMeta(),
-      m_id(_id)
+bool get_meta_data(const LibMetadata *meta, fwk::PropertyIndex p,
+                   fwk::PropertyValue &value)
 {
-}
-
-
-bool LibMetadata::setMetaData(fwk::PropertyIndex meta, 
-                              const fwk::PropertyValue & value)
-{
-    const char * ns = NULL;
-    const char * property = NULL;
-    bool result = false;
-
-    result = property_index_to_xmp(meta, ns, property);
-    if(result) {
-
-        if(fwk::is_empty(value)) {
-            result = xmp_delete_property(xmp(), ns, property);
-        }
-        else if(fwk::is_integer(value)) {
-            result = xmp_set_property_int32(xmp(), ns, property,
-                                            fwk::get_integer(value), 0);
-        }
-        else if(value.type() == typeid(std::string)) {
-            std::string val = boost::get<std::string>(value);
-            if (val.empty()) {
-                result = xmp_delete_property(xmp(), ns, property);
-            }
-            else {
-                result = xmp_set_property(xmp(), ns, property, val.c_str(), 0);
-                // FIXME we should know in advance it is localized.
-                if(!result && (xmp_get_error() == XMPErr_BadXPath)) {
-                    result = xmp_set_localized_text(xmp(), ns, property,
-                                                    "", "x-default",
-                                                    val.c_str(), 0);
-                }
-            }
-        }
-        else if(value.type() == typeid(fwk::StringArray)) {
-            fwk::StringArray v = boost::get<fwk::StringArray>(value);
-            // TODO see if we can get that without deleting the whole property
-            result = xmp_delete_property(xmp(), ns, property);
-            std::for_each(v.begin(), v.end(),
-                          [this, ns, property] (const std::string & val) {
-                              /*result =*/
-                              xmp_append_array_item(this->xmp(),
-                                                    ns, property,
-                                                    XMP_PROP_VALUE_IS_ARRAY,
-                                                    val.c_str(), 0);
-                          });
-        }
-        else if(value.type() == typeid(fwk::Date)) {
-            fwk::Date d = boost::get<fwk::Date>(value);
-
-            result = xmp_set_property_date(xmp(), ns, property,
-                                           &d.xmp_date(), 0);
-
-        }
-        if(!result) {
-            ERR_OUT("error setting property %s:%s %d", ns, property, 
-                    xmp_get_error());
-        }
-    }
-    else {
-        ERR_OUT("unknown property");
-    }
-    return result;
-}
-
-
-bool LibMetadata::getMetaData(fwk::PropertyIndex p, 
-                              fwk::PropertyValue & value) const
-{
-    const PropsToXmpMap & propmap = props_to_xmp_map();
+    const PropsToXmpMap &propmap = props_to_xmp_map();
     PropsToXmpMap::const_iterator iter = propmap.find(p);
-    if(iter == propmap.end()) {
+    if (iter == propmap.end()) {
         // not found
         return false;
     }
-    if(iter->second.first == NULL || iter->second.second == NULL) {
+    if (iter->second.first == NULL || iter->second.second == NULL) {
         // no XMP equivalent
         return false;
     }
     xmp::ScopedPtr<XmpStringPtr> xmp_value(xmp_string_new());
     uint32_t prop_bits = 0;
-    const char * ns = iter->second.first;
-    const char * xmp_prop = iter->second.second;
-    bool found = xmp_get_property(xmp(), ns, xmp_prop, xmp_value, &prop_bits);
-    if(found && XMP_IS_ARRAY_ALTTEXT(prop_bits)) {
-        found = xmp_get_localized_text(xmp(), ns, xmp_prop, "", "x-default", 
-                                       NULL, xmp_value, NULL);
+    const char *ns = iter->second.first;
+    const char *xmp_prop = iter->second.second;
+    bool found = xmp_get_property(engine_libmetadata_get_xmp(meta), ns,
+                                  xmp_prop, xmp_value, &prop_bits);
+    if (found && XMP_IS_ARRAY_ALTTEXT(prop_bits)) {
+        found = xmp_get_localized_text(engine_libmetadata_get_xmp(meta), ns,
+                                       xmp_prop, "", "x-default", nullptr,
+                                       xmp_value, nullptr);
     }
-    if(found) {
-        const char * v = NULL;
+    if (found) {
+        const char *v = NULL;
         v = xmp_string_cstr(xmp_value);
-        if(v) {
+        if (v) {
             value = fwk::PropertyValue(v);
             return true;
         }
@@ -175,84 +102,69 @@ bool LibMetadata::getMetaData(fwk::PropertyIndex p,
     return false;
 }
 
-void LibMetadata::to_properties(const fwk::PropertySet & propset,
-                                fwk::PropertyBag & props)
-{
-    std::for_each(propset.begin(), propset.end(),
-                  [&props, this] (fwk::PropertySet::key_type prop_id) {
-                      switch(prop_id) {
-                      case NpXmpRatingProp:
-                          props.set_value_for_property(prop_id,
-                                                       fwk::PropertyValue(rating()));
-                          break;
-                      case NpXmpLabelProp:
-                          props.set_value_for_property(prop_id,
-                                                       fwk::PropertyValue(label()));
-                          break;
-                      case NpTiffOrientationProp:
-                          props.set_value_for_property(prop_id,
-                                                       fwk::PropertyValue(orientation()));
-                          break;
-                      case NpExifDateTimeOriginalProp:
-                          props.set_value_for_property(prop_id,
-                                                       fwk::PropertyValue(creation_date()));
-                          break;
-                      case NpIptcKeywordsProp:
-                      {
-                          xmp::ScopedPtr<XmpIteratorPtr>
-                              iter(xmp_iterator_new(xmp(), NS_DC,
-                                                    "subject", XMP_ITER_JUSTLEAFNODES));
-                          fwk::StringArray vec;
-                          xmp::ScopedPtr<XmpStringPtr> value(xmp_string_new());
-                          while(xmp_iterator_next(iter, NULL, NULL, value, NULL)) {
-                              vec.push_back(xmp_string_cstr(value));
-                          }
-                          fwk::PropertyValue v(vec);
-                          //DBG_ASSERT(check_property_type(prop_id, v.type()), "wrong type");
-                          props.set_value_for_property(prop_id, v);
-                          break;
-                      }
-                      default:
-                      {
-                          fwk::PropertyValue propval;
-                          if(getMetaData(prop_id, propval)) {
-                              //DBG_ASSERT(check_property_type(prop_id, propval.type()), "wrong type");
-                              props.set_value_for_property(prop_id, propval);
-                          }
-                          else {
-                              DBG_OUT("missing prop %u", prop_id);
-                          }
-                          break;
-                      }
-                      }
-                  }
-        );
-}
-
-
-bool LibMetadata::touch()
+void libmetadata_to_properties(const LibMetadata *meta,
+                               const fwk::PropertySet &propset,
+                               fwk::PropertyBag &props)
 {
-    bool result = false;
-    XmpDateTime date;
-    struct tm dt, *dt2;
-    time_t currenttime = time(NULL);
-    dt2 = gmtime_r(&currenttime, &dt);
-    if(dt2 == &dt) {
-        memset(&date, 0, sizeof date);
-        date.second = dt.tm_sec;
-        date.minute = dt.tm_min;
-        date.hour = dt.tm_hour;
-        date.day = dt.tm_mday;
-        date.month = dt.tm_mon;
-        date.year = dt.tm_year + 1900;
-
-        result = xmp_set_property_date(xmp(), NS_XAP, "MetadataDate",
-                                       &date, 0);
-    }
-    return result;
+    std::for_each(
+        propset.begin(), propset.end(),
+        [&props, meta](fwk::PropertySet::key_type prop_id) {
+            auto xmpmeta = engine_libmetadata_get_xmpmeta(meta);
+            switch (prop_id) {
+            case NpXmpRatingProp:
+                props.set_value_for_property(
+                    prop_id,
+                    fwk::PropertyValue(fwk_xmp_meta_get_rating(xmpmeta)));
+                break;
+            case NpXmpLabelProp: {
+                char *str = fwk_xmp_meta_get_label(xmpmeta);
+                if (str) {
+                    props.set_value_for_property(prop_id,
+                                                 fwk::PropertyValue(str));
+                    rust_cstring_delete(str);
+                }
+                break;
+            }
+            case NpTiffOrientationProp:
+                props.set_value_for_property(
+                    prop_id,
+                    fwk::PropertyValue(fwk_xmp_meta_get_orientation(xmpmeta)));
+                break;
+            case NpExifDateTimeOriginalProp: {
+                fwk::DatePtr date =
+                    fwk::date_wrap(fwk_xmp_meta_get_creation_date(xmpmeta));
+                props.set_value_for_property(prop_id, fwk::PropertyValue(date));
+                break;
+            }
+            case NpIptcKeywordsProp: {
+                xmp::ScopedPtr<XmpIteratorPtr> iter(
+                    xmp_iterator_new(engine_libmetadata_get_xmp(meta), NS_DC,
+                                     "subject", XMP_ITER_JUSTLEAFNODES));
+                fwk::StringArray vec;
+                xmp::ScopedPtr<XmpStringPtr> value(xmp_string_new());
+                while (xmp_iterator_next(iter, NULL, NULL, value, NULL)) {
+                    vec.push_back(xmp_string_cstr(value));
+                }
+                fwk::PropertyValue v(vec);
+                // DBG_ASSERT(check_property_type(prop_id, v.type()), "wrong
+                // type");
+                props.set_value_for_property(prop_id, v);
+                break;
+            }
+            default: {
+                fwk::PropertyValue propval;
+                if (get_meta_data(meta, prop_id, propval)) {
+                    // DBG_ASSERT(check_property_type(prop_id, propval.type()),
+                    // "wrong type");
+                    props.set_value_for_property(prop_id, propval);
+                } else {
+                    DBG_OUT("missing prop %u", prop_id);
+                }
+                break;
+            }
+            }
+        });
 }
-
-
 }
 
 /*
diff --git a/src/engine/db/libmetadata.hpp b/src/engine/db/libmetadata.hpp
index 680236c..5fd15aa 100644
--- a/src/engine/db/libmetadata.hpp
+++ b/src/engine/db/libmetadata.hpp
@@ -1,7 +1,7 @@
 /*
  * niepce - eng/db/libmetadata.hpp
  *
- * Copyright (C) 2008-2013 Hubert Figuiere
+ * Copyright (C) 2008-2017 Hubert Figuière
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,57 +17,39 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef __DB_LIBMETADATA_H_
-#define __DB_LIBMETADATA_H_
+#pragma once
 
-#include <vector>
-#include <string>
 #include <memory>
+#include <string>
+#include <vector>
 
 #include <boost/any.hpp>
 
-#include "fwk/base/propertybag.hpp"
-#include "fwk/utils/exempi.hpp"
 #include "engine/db/librarytypes.hpp"
 #include "engine/db/metadata.hpp"
+#include "fwk/base/propertybag.hpp"
+#include "fwk/utils/exempi.hpp"
 
 namespace eng {
 
-class LibMetadata
-    : public fwk::XmpMeta
-{
-public:
-    typedef std::shared_ptr<LibMetadata> Ptr;
-
-    LibMetadata(library_id_t _id);
-
-    library_id_t id() const
-        { return m_id; }
-    bool setMetaData(fwk::PropertyIndex meta, const fwk::PropertyValue & value);
-    bool getMetaData(fwk::PropertyIndex meta, fwk::PropertyValue & value) const;
+class LibMetadata;
 
-    /** convert XMP to a set of properties
-     * @param propset the property set requested
-     * @param props the output properties
-     */
-    void to_properties(const fwk::PropertySet & propset,
-                       fwk::PropertyBag & properties);
-    /** do like the unix "touch". Update the MetadataDate 
-     * to the current time, in UTC.
-     */
-    bool touch();
-private:
-    LibMetadata(const LibMetadata &);
+void libmetadata_to_properties(const LibMetadata *meta,
+                               const fwk::PropertySet &propset,
+                               fwk::PropertyBag &props);
 
-    static bool property_index_to_xmp(fwk::PropertyIndex index, 
-                                      const char * & ns, const char * & property);
-
-    library_id_t m_id;
+struct IndexToXmp {
+    const char *ns;
+    const char *property;
 };
-
+IndexToXmp property_index_to_xmp(fwk::PropertyIndex index);
 }
 
-#endif
+extern "C" {
+eng::library_id_t engine_libmetadata_get_id(const eng::LibMetadata *meta);
+XmpPtr engine_libmetadata_get_xmp(const eng::LibMetadata *meta);
+fwk::XmpMeta *engine_libmetadata_get_xmpmeta(const eng::LibMetadata *meta);
+}
 
 /*
   Local Variables:
diff --git a/src/engine/db/libmetadata.rs b/src/engine/db/libmetadata.rs
new file mode 100644
index 0000000..cddbde1
--- /dev/null
+++ b/src/engine/db/libmetadata.rs
@@ -0,0 +1,182 @@
+/*
+ * niepce - eng/db/libmetadata.rs
+ *
+ * Copyright (C) 2017 Hubert Figuière
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+use std::ffi::CStr;
+use rusqlite;
+use exempi;
+
+use fwk;
+use fwk::{
+    XmpMeta,
+    make_xmp_date_time
+};
+use fwk::utils::exempi::NS_XAP;
+use super::{
+    FromDb,
+    LibraryId
+};
+use root::eng::NiepceProperties as Np;
+use root::fwk::{
+    is_empty,
+    is_integer,
+    is_string,
+    is_string_array,
+    is_date,
+    get_string_cstr,
+    get_string_array,
+    get_integer,
+    get_date,
+    string_array_len,
+    string_array_at_cstr,
+    PropertyValue
+};
+
+pub struct LibMetadata {
+    xmp: XmpMeta,
+    id: LibraryId
+}
+
+struct IndexToXmp {
+    pub ns: String,
+    pub property: String
+}
+
+fn property_index_to_xmp(meta: Np) -> Option<IndexToXmp> {
+    let index = unsafe { ::root::eng::property_index_to_xmp(meta as u32) };
+    if index.ns.is_null() || index.property.is_null() {
+        err_out!("property {} not found", meta as u32);
+        return None;
+    }
+    Some(IndexToXmp {
+        ns: String::from(unsafe { CStr::from_ptr(index.ns) }.to_string_lossy()),
+        property: String::from(unsafe { CStr::from_ptr(index.property) }.to_string_lossy())
+    })
+}
+
+
+impl LibMetadata {
+
+    pub fn new(id: LibraryId) -> LibMetadata {
+        LibMetadata{
+            xmp: XmpMeta::new(),
+            id: id
+        }
+    }
+
+    pub fn new_with_xmp(id: LibraryId, xmp: XmpMeta) -> LibMetadata {
+        LibMetadata{
+            xmp: xmp,
+            id: id
+        }
+    }
+
+    pub fn serialize_inline(&self) -> String {
+        self.xmp.serialize_inline()
+    }
+
+    pub fn set_metadata(&mut self, meta: Np, value: &PropertyValue) -> bool {
+        if let Some(ix) = property_index_to_xmp(meta) {
+            if unsafe { is_empty(value) } {
+                return self.xmp.xmp.delete_property(&ix.ns, &ix.property);
+            } else if unsafe { is_integer(value) } {
+                return self.xmp.xmp.set_property_i32(
+                    &ix.ns, &ix.property, unsafe { get_integer(value) }, exempi::PROP_NONE);
+            } else if unsafe { is_string(value) } {
+                let cstr = unsafe { CStr::from_ptr(get_string_cstr(value)) };
+                if cstr.to_bytes().len() == 0 {
+                    return self.xmp.xmp.delete_property(&ix.ns, &ix.property);
+                } else if !self.xmp.xmp.set_property(
+                    &ix.ns, &ix.property, &*cstr.to_string_lossy(), exempi::PROP_NONE) {
+                    if exempi::get_error() == exempi::Error::BadXPath {
+                        return self.xmp.xmp.set_localized_text(
+                            &ix.ns, &ix.property, "", "x-default",
+                            &*cstr.to_string_lossy(), exempi::PROP_NONE);
+                    }
+                } else {
+                    return true;
+                }
+            } else if unsafe { is_string_array(value) } {
+                self.xmp.xmp.delete_property(&ix.ns, &ix.property);
+                let a = unsafe { get_string_array(value) };
+                let count = unsafe { string_array_len(a) };
+                for i in 0..count {
+                    let cstr = unsafe { CStr::from_ptr(string_array_at_cstr(a, i)) };
+                    self.xmp.xmp.append_array_item(&ix.ns, &ix.property,
+                                                   exempi::ARRAY_NONE,
+                                                   &*cstr.to_string_lossy(),
+                                                   exempi::ITEM_NONE);
+                }
+                return true;
+            } else if unsafe { is_date(value) } {
+                let d = unsafe { get_date(value) } as *const fwk::Date;
+                return self.xmp.xmp.set_property_date(
+                    &ix.ns, &ix.property, unsafe { &(*d) }.xmp_date(), exempi::PROP_NONE);
+            }
+            err_out!("error setting property {}:{} {}", ix.ns, ix.property,
+                     exempi::get_error() as u32);
+        }
+        err_out!("Unknown property {:?}", meta);
+        false
+    }
+
+    pub fn touch(&mut self) -> bool {
+        let mut xmpdate = exempi::DateTime::new();
+        if make_xmp_date_time(fwk::Date::now(), &mut xmpdate) {
+            return self.xmp.xmp.set_property_date(NS_XAP, "MetadataDate",
+                                                  &xmpdate, exempi::PROP_NONE);
+        }
+        false
+    }
+}
+
+impl FromDb for LibMetadata {
+
+    fn read_db_columns() -> &'static str {
+        "id,xmp"
+    }
+
+    fn read_db_tables() -> &'static str {
+        "files"
+    }
+
+    fn read_from(row: &rusqlite::Row) -> Self {
+        let id: LibraryId = row.get(0);
+        let xmp: String = row.get(1);
+
+        let mut xmpmeta = XmpMeta::new();
+        xmpmeta.unserialize(&xmp);
+        LibMetadata::new_with_xmp(id, xmpmeta)
+    }
+
+}
+
+#[no_mangle]
+pub fn engine_libmetadata_get_id(meta: &LibMetadata) -> LibraryId {
+    return meta.id;
+}
+
+#[no_mangle]
+pub fn engine_libmetadata_get_xmp(meta: &LibMetadata) -> exempi::Xmp {
+    return meta.xmp.xmp.clone();
+}
+
+#[no_mangle]
+pub fn engine_libmetadata_get_xmpmeta(meta: &LibMetadata) -> *const XmpMeta {
+    return &meta.xmp;
+}
diff --git a/src/engine/db/library.cpp b/src/engine/db/library.cpp
index 0c1e6fc..50e1583 100644
--- a/src/engine/db/library.cpp
+++ b/src/engine/db/library.cpp
@@ -17,1003 +17,19 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <time.h>
-#include <stdio.h>
-
-#include <iostream>
-
-#include <boost/format.hpp>
-
-#include <glibmm/i18n.h>
-
-#include "fwk/base/colour.hpp"
-#include "niepce/notifications.hpp"
 #include "library.hpp"
-#include "metadata.hpp"
-#include "properties.hpp"
-#include "fwk/base/debug.hpp"
-#include "fwk/utils/exception.hpp"
-#include "fwk/utils/exempi.hpp"
-#include "fwk/utils/pathutils.hpp"
-#include "fwk/utils/db/sqlite/sqlitecnxmgrdrv.hpp"
-#include "fwk/utils/db/sqlite/sqlitecnxdrv.hpp"
-#include "fwk/utils/db/sqlstatement.hpp"
-#include "fwk/toolkit/notificationcenter.hpp"
-#include "fwk/toolkit/mimetype.hpp"
 
-using ::fwk::NotificationCenter;
-using ::db::SQLStatement;
+extern "C" eng::Library *engine_db_library_new(const char *dir,
+                                               uint64_t notif_id);
+extern "C" void engine_db_library_delete(eng::Library *);
 
 namespace eng {
 
-const char * s_databaseName = "niepcelibrary.db";
-
-
-Library::Library(const std::string & dir, uint64_t notif_id)
-    : m_maindir(dir),
-      m_dbname(m_maindir + "/" + s_databaseName),
-      m_dbmgr(new db::sqlite::SqliteCnxMgrDrv()),
-      m_notif_id(notif_id),
-      m_inited(false)
-{
-    DBG_OUT("dir = %s", dir.c_str());
-    db::DBDesc desc("", 0, m_dbname);
-    try {
-        m_dbdrv = m_dbmgr->connect_to_db(desc, "", "");
-        m_inited = init();
-
-        m_dbdrv->create_function0(
-            "rewrite_xmp",
-            [this] () {
-                DBG_OUT("rewrite_xmp");
-                notify(
-                    LibNotification::make<LibNotification::Type::XMP_NEEDS_UPDATE>({}));
-            });
-    }
-    catch(const std::exception &e)
-    {
-        ERR_OUT("Exception: %s", e.what());
-    }
-}
-
-Library::~Library()
-{
-}
-
-void Library::notify(LibNotification&& ln)
-{
-    auto wnc = fwk::NotificationCenter::get_nc(m_notif_id);
-    auto nc = wnc.lock();
-    if (nc) {
-        DBG_OUT("notif");
-        // pass the notification
-        fwk::Notification::Ptr n(new fwk::Notification(niepce::NOTIFICATION_LIB));
-        n->setData(boost::any(ln));
-        nc->post(std::move(n));
-    }
-    else {
-        DBG_OUT("try to send a notification without notification center");
-    }
-}
-
-/** init the database
- * @return true is the DB is inited. false if it fail.
- */
-bool Library::init()
-{
-    int version = checkDatabaseVersion();
-    if(version == -1) {
-        // error
-        DBG_OUT("version check -1");
-    }
-    else if(version == 0) {
-        // let's create our DB
-        DBG_OUT("version == 0");
-        return _initDb();
-    }
-    else if(version != DB_SCHEMA_VERSION)
-    {
-    }
-    return true;
-}
-
-bool Library::_initDb()
-{
-    SQLStatement adminTable("CREATE TABLE admin (key TEXT NOT NULL,"
-                            " value TEXT)");
-    SQLStatement adminVersion(boost::format("INSERT INTO admin (key, value) "
-                                            " VALUES ('version', '%1%')") %
-                              DB_SCHEMA_VERSION);
-    SQLStatement vaultTable("CREATE TABLE vaults (id INTEGER PRIMARY KEY,"
-                            " path TEXT)");
-    SQLStatement folderTable("CREATE TABLE folders (id INTEGER PRIMARY KEY,"
-                             " path TEXT, name TEXT, "
-                             " vault_id INTEGER DEFAULT 0, "
-                             " locked INTEGER DEFAULT 0, "
-                             " virtual INTEGER DEFAULT 0,"
-                             " expanded INTEGER DEFAULT 0,"
-                             " parent_id INTEGER)");
-
-    SQLStatement initialFolders(
-        boost::format("insert into folders (name, locked, virtual, parent_id) "
-                      " values ('%1%', 1, %2%, 0)")
-        % _("Trash")
-        % int(LibFolderVirtualType::TRASH));
-    SQLStatement fileTable("CREATE TABLE files (id INTEGER PRIMARY KEY,"
-                           " main_file INTEGER, name TEXT, parent_id INTEGER,"
-                           " orientation INTEGER, file_type INTEGER, "
-                           " file_date INTEGER, rating INTEGER DEFAULT 0, "
-                           " label INTEGER, flag INTEGER DEFAULT 0, "
-                           " import_date INTEGER, mod_date INTEGER, "
-                           " xmp TEXT, xmp_date INTEGER, xmp_file INTEGER,"
-                           " jpeg_file INTEGER)");
-    SQLStatement fsFileTable("CREATE TABLE fsfiles (id INTEGER PRIMARY KEY,"
-                             " path TEXT)");
-    SQLStatement keywordTable("CREATE TABLE keywords (id INTEGER PRIMARY KEY,"
-                              " keyword TEXT, parent_id INTEGER DEFAULT 0)");
-    SQLStatement keywordingTable("CREATE TABLE keywording (file_id INTEGER,"
-                                 " keyword_id INTEGER,"
-                                 " UNIQUE(file_id, keyword_id))");
-    SQLStatement labelTable("CREATE TABLE labels (id INTEGER PRIMARY KEY,"
-                            " name TEXT, color TEXT)");
-    SQLStatement xmpUpdateQueueTable("CREATE TABLE xmp_update_queue "
-                                     " (id INTEGER UNIQUE)");
-//             SQLStatement collsTable("CREATE TABLE collections (id INTEGER PRIMARY KEY,"
-//                                                             " name TEXT)");
-//             SQLStatement collectingTable("CREATE TABLE collecting (file_id INTEGER,"
-//                                                                      " collection_id INTEGER)");
-
-    SQLStatement fileUpdateTrigger(
-        "CREATE TRIGGER file_update_trigger UPDATE ON files "
-        " BEGIN"
-        "  UPDATE files SET mod_date = strftime('%s','now');"
-        " END");
-    SQLStatement xmpUpdateTrigger(
-        "CREATE TRIGGER xmp_update_trigger UPDATE OF xmp ON files "
-        " BEGIN"
-        "  INSERT OR IGNORE INTO xmp_update_queue (id) VALUES(new.id);"
-        "  SELECT rewrite_xmp(); "
-        " END");
-
-    m_dbdrv->execute_statement(adminTable);
-    m_dbdrv->execute_statement(adminVersion);
-    m_dbdrv->execute_statement(vaultTable);
-    m_dbdrv->execute_statement(folderTable);
-    m_dbdrv->execute_statement(initialFolders);
-    m_dbdrv->execute_statement(fileTable);
-    m_dbdrv->execute_statement(fsFileTable);
-    m_dbdrv->execute_statement(keywordTable);
-    m_dbdrv->execute_statement(keywordingTable);
-    m_dbdrv->execute_statement(labelTable);
-    m_dbdrv->execute_statement(xmpUpdateQueueTable);
-//             m_dbdrv->execute_statement(collsTable);
-//             m_dbdrv->execute_statement(collectingTable);
-
-    m_dbdrv->execute_statement(fileUpdateTrigger);
-    m_dbdrv->execute_statement(xmpUpdateTrigger);
-    notify(LibNotification::make<LibNotification::Type::NEW_LIBRARY_CREATED>({}));
-    return true;
-}
-
-/** check that database verion
- * @return the DB version. -1 in case of error. 0 is can't read it.
- */
-int Library::checkDatabaseVersion()
-{
-    int v = 0;
-    std::string version;
-    try {
-        SQLStatement sql("SELECT value FROM admin WHERE key='version'");
-
-        if(m_dbdrv->execute_statement(sql)) {
-            if(m_dbdrv->read_next_row()
-               && m_dbdrv->get_column_content(0, version)) {
-                v = std::stoi(version);
-            }
-        }
-    }
-    catch(const fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-        v = -1;
-    }
-    catch(const std::bad_cast &)
-    {
-        DBG_OUT("version is %s, can't convert to int", version.c_str());
-        v = 0;
-    }
-    catch(...)
-    {
-        v = -1;
-    }
-    return v;
-}
-
-
-int64_t Library::addFsFile(const std::string & file)
-{
-    int64_t ret = -1;
-
-    SQLStatement sql(boost::format("INSERT INTO fsfiles (path)"
-                                   " VALUES ('%1%')")
-                     % file);
-    if(m_dbdrv->execute_statement(sql)) {
-        int64_t id = m_dbdrv->last_row_id();
-        DBG_OUT("last row inserted %d", (int)id);
-        ret = id;
-    }
-    return ret;
-}
-
-std::string Library::getFsFile(library_id_t id)
-{
-    std::string p;
-    SQLStatement sql(boost::format("SELECT path FROM fsfiles"
-                                   " WHERE id='%1%'")
-                     % id);
-    try {
-        if(m_dbdrv->execute_statement(sql) &&
-           m_dbdrv->read_next_row()) {
-            std::string path;
-            m_dbdrv->get_column_content(0, path);
-            p = path;
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-
-    return p;
-}
-
-
-library_id_t Library::addFile(library_id_t folder_id, const std::string & file,
-                              LibraryManaged manage)
-{
-    library_id_t ret = -1;
-    DBG_ASSERT(manage == LibraryManaged::NO, "manage not supported");
-    DBG_ASSERT(folder_id != -1, "invalid folder ID");
-    try {
-        int32_t rating, orientation, flag;
-        library_id_t label_id;
-        std::string label;
-        fwk::MimeType mime = fwk::MimeType(file);
-        eng::LibFileType file_type = eng::mimetype_to_filetype(mime);
-        fwk::XmpMeta meta(file, file_type == eng::LibFileType::RAW);
-        label_id = 0;
-        orientation = meta.orientation();
-        rating = meta.rating();
-        label = meta.label();
-        flag = meta.flag();
-        time_t creation_date = fwk::make_time_value(meta.creation_date());
-        if(creation_date == -1) {
-            creation_date = 0;
-        }
-
-        library_id_t fs_file_id = addFsFile(file);
-        if(fs_file_id <= 0) {
-            throw(fwk::Exception("add fsfile failed"));
-        }
-        SQLStatement sql(boost::format("INSERT INTO files ("
-                                       " main_file, name, parent_id, "
-                                       " import_date, mod_date, "
-                                       " orientation, file_date, rating, label, "
-                                       " file_type, flag, xmp) "
-                                       " VALUES ("
-                                       " '%1%', '%2%', '%3%', "
-                                       " '%4%', '%4%',"
-                                       " '%5%', '%6%', '%7%', '%8%', '%9%',"
-                                       " '%10%',"
-                                       " ?1);")
-                         % fs_file_id % fwk::path_basename(file) % folder_id
-                         % time(NULL)
-                         % orientation % creation_date % rating
-                         % label_id % static_cast<int>(file_type) % flag);
-        std::string buf = meta.serialize_inline();
-        sql.bind(1, buf);
-        if(m_dbdrv->execute_statement(sql)) {
-            library_id_t id = m_dbdrv->last_row_id();
-            DBG_OUT("last row inserted %d", (int)id);
-            ret = id;
-            auto & keywords = meta.keywords();
-            for(auto k : keywords) {
-                library_id_t kwid = makeKeyword(k);
-                if(kwid != -1) {
-                    assignKeyword(kwid, id);
-                }
-            }
-        }
-    }
-    catch(const fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-        ret = -1;
-    }
-    catch(const std::exception & e)
-    {
-        DBG_OUT("unknown exception %s", e.what());
-        ret = -1;
-    }
-    return ret;
-}
-
-
-library_id_t Library::addFileAndFolder(const std::string & folder,
-                                       const std::string & file,
-                                       LibraryManaged manage)
-{
-    LibFolderPtr f;
-    f = getFolder(folder);
-    if(f == NULL) {
-        ERR_OUT("Folder %s not found", folder.c_str());
-    }
-    return addFile(f ? engine_db_libfolder_id(f.get()) : -1, file, manage);
-}
-
-library_id_t Library::addBundle(library_id_t folder_id,
-                                const eng::FileBundlePtr & bundle,
-                                LibraryManaged manage)
-{
-    library_id_t file_id = 0;
-    file_id = addFile(folder_id, engine_db_filebundle_main(bundle.get()), manage);
-    if(file_id > 0) {
-        library_id_t fsfile_id;
-        bool success;
-        // addXmpSidecar
-        if(engine_db_filebundle_sidecar(bundle.get())[0] == 0) {
-            fsfile_id = addFsFile(engine_db_filebundle_sidecar(bundle.get()));
-            if(fsfile_id > 0) {
-                success = addSidecarFileToBundle(file_id, fsfile_id);
-            }
-        }
-        // addJpeg
-        if(engine_db_filebundle_jpeg(bundle.get())[0] == 0) {
-            fsfile_id = addFsFile(engine_db_filebundle_jpeg(bundle.get()));
-            if(fsfile_id > 0) {
-                success = addJpegFileToBundle(file_id, fsfile_id);
-            }
-        }
-    }
-    return file_id;
-}
-
-bool Library::addSidecarFileToBundle(library_id_t file_id,
-                                     library_id_t fsfile_id)
-{
-    SQLStatement sql(boost::format("UPDATE files SET xmp_file='%2%'"
-                                   " WHERE id='%1%';")
-                     % file_id % fsfile_id);
-    try {
-        return m_dbdrv->execute_statement(sql);
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    return false;
-}
-
-
-bool Library::addJpegFileToBundle(library_id_t file_id, library_id_t fsfile_id)
-{
-    SQLStatement sql(boost::format("UPDATE files SET jpeg_file='%2%',"
-                                   " file_type='%3%' "
-                                   " WHERE id='%1%';")
-                     % file_id % fsfile_id
-                     % static_cast<int>(LibFileType::RAW_JPEG));
-    try {
-        return m_dbdrv->execute_statement(sql);
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    return false;
-}
-
-
-LibFolderPtr Library::getFolder(const std::string & folder)
-{
-    LibFolderPtr f;
-    SQLStatement sql(boost::format("SELECT %1% "
-                                   "FROM folders WHERE path='%2%'")
-                     % libfolder_read_db_columns() % folder);
-
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            if(m_dbdrv->read_next_row()) {
-                f = libfolder_read_from(m_dbdrv);
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    return f;
-}
-
-
-LibFolderPtr Library::addFolder(const std::string & folder)
-{
-    LibFolderPtr f;
-    SQLStatement sql(boost::format("INSERT INTO folders "
-                                   "(path,name,vault_id,parent_id) "
-                                   "VALUES('%1%', '%2%', '0', '0')")
-                     % folder % fwk::path_basename(folder));
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            library_id_t id = m_dbdrv->last_row_id();
-            DBG_OUT("last row inserted %Ld", (long long)id);
-            f = libfolder_new(id, fwk::path_basename(folder).c_str());
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    return f;
-}
-
-
-void Library::getAllFolders(const LibFolderListPtr & l)
-{
-    SQLStatement sql(boost::format("SELECT %1% FROM folders")
-                     % libfolder_read_db_columns());
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            while(m_dbdrv->read_next_row()) {
-                LibFolderPtr f = libfolder_read_from(m_dbdrv);
-                l->push_back(f);
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-}
-
-static LibFilePtr getFileFromDbRow(const db::IConnectionDriver::Ptr & dbdrv)
-{
-    library_id_t id;
-    library_id_t fid;
-    library_id_t fsfid;
-    std::string pathname;
-    std::string name;
-    DBG_ASSERT(dbdrv->get_number_of_columns() == 10, "wrong number of columns");
-    dbdrv->get_column_content(0, id);
-    dbdrv->get_column_content(1, fid);
-    dbdrv->get_column_content(2, pathname);
-    dbdrv->get_column_content(3, name);
-    dbdrv->get_column_content(8, fsfid);
-    DBG_OUT("found %s", pathname.c_str());
-    LibFilePtr f(libfile_new(id, fid, fsfid,
-                             pathname.c_str(), name.c_str()));
-    int32_t val;
-    dbdrv->get_column_content(4, val);
-    engine_db_libfile_set_orientation(f.get(), val);
-    dbdrv->get_column_content(5, val);
-    engine_db_libfile_set_rating(f.get(), val);
-    dbdrv->get_column_content(6, val);
-    engine_db_libfile_set_label(f.get(), val);
-    dbdrv->get_column_content(9, val);
-    engine_db_libfile_set_flag(f.get(), val);
-
-    /* Casting needed. Remember that a simple enum like this is just a couple
-     * of #define for integers.
-     */
-    dbdrv->get_column_content(7, val);
-    engine_db_libfile_set_file_type(f.get(),(eng::LibFileType)val);
-    return f;
-}
-
-void Library::getFolderContent(library_id_t folder_id, const LibFileListPtr & fl)
-{
-    SQLStatement sql(boost::format("SELECT files.id,parent_id,fsfiles.path,"
-                                   "name,"
-                                   "orientation,rating,label,file_type,"
-                                   "fsfiles.id,flag"
-                                   " FROM files,fsfiles "
-                                   " WHERE parent_id='%1%' "
-                                   " AND files.main_file=fsfiles.id")
-                     % folder_id);
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            while(m_dbdrv->read_next_row()) {
-                LibFilePtr f(getFileFromDbRow(m_dbdrv));
-                fl->push_back(f);
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-}
-
-int Library::countFolder(library_id_t folder_id)
+LibraryPtr library_new(const char *dir, uint64_t notif_id)
 {
-    int count = -1;
-    SQLStatement sql(boost::format("SELECT COUNT(id) FROM files WHERE parent_id='%1%';")
-                     % folder_id);
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            if(m_dbdrv->read_next_row()) {
-                m_dbdrv->get_column_content(0, count);
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    return count;
+    return LibraryPtr(engine_db_library_new(dir, notif_id),
+                      &engine_db_library_delete);
 }
-
-void Library::getAllKeywords(const KeywordListPtr & l)
-{
-    SQLStatement sql("SELECT id,keyword FROM keywords ORDER BY keyword");
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            while(m_dbdrv->read_next_row()) {
-                library_id_t id;
-                std::string name;
-                m_dbdrv->get_column_content(0, id);
-                m_dbdrv->get_column_content(1, name);
-                l->push_back(keyword_new(id, name.c_str()));
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-}
-
-library_id_t Library::makeKeyword(const std::string & keyword)
-{
-    library_id_t keyword_id = -1;
-    SQLStatement sql("SELECT id FROM keywords WHERE "
-                     "keyword=?1;");
-    sql.bind(1, keyword);
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            if(m_dbdrv->read_next_row()) {
-                m_dbdrv->get_column_content(0, keyword_id);
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    if(keyword_id == -1) {
-        SQLStatement sql2("INSERT INTO keywords (keyword, parent_id) "
-                          " VALUES(?1, 0);");
-        sql2.bind(1, keyword);
-        try {
-            if(m_dbdrv->execute_statement(sql2)) {
-                keyword_id = m_dbdrv->last_row_id();
-                KeywordPtr kw(keyword_new(keyword_id, keyword.c_str()));
-                notify(LibNotification::make<LibNotification::Type::ADDED_KEYWORD>({kw}));
-            }
-        }
-        catch(fwk::Exception & e)
-        {
-            DBG_OUT("db exception %s", e.what());
-        }
-    }
-
-    return keyword_id;
-}
-
-
-bool Library::unassignAllKeywordsForFile(library_id_t file_id)
-{
-    bool ret = false;
-    SQLStatement sql(boost::format("DELETE FROM keywording"
-                                   " WHERE file_id='%1%'")
-                     % file_id);
-    try {
-        ret = m_dbdrv->execute_statement(sql);
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    return ret;
-}
-
-bool Library::assignKeyword(library_id_t kw_id, library_id_t file_id)
-{
-    bool ret = false;
-    // we must IGNORE as there is a unicity constraint
-    // that way setting a keyword relationship is solid
-    SQLStatement sql(boost::format("INSERT OR IGNORE INTO keywording"
-                                   " (file_id, keyword_id) "
-                                   " VALUES('%1%', '%2%');")
-                     % file_id % kw_id );
-    try {
-        ret = m_dbdrv->execute_statement(sql);
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    return ret;
-}
-
-
-void Library::getKeywordContent(library_id_t keyword_id, const LibFileListPtr & fl)
-{
-    SQLStatement sql(boost::format("SELECT files.id,parent_id,fsfiles.path,"
-                                   "name,orientation,rating,label,file_type,"
-                                   " fsfiles.id,flag"
-                                   " FROM files,fsfiles "
-                                   " WHERE files.id IN "
-                                   " (SELECT file_id FROM keywording "
-                                   " WHERE keyword_id='%1%') "
-                                   " AND fsfiles.id = files.main_file;")
-                     % keyword_id);
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            while(m_dbdrv->read_next_row()) {
-                LibFilePtr f(getFileFromDbRow(m_dbdrv));
-                fl->push_back(f);
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-}
-
-
-void Library::getMetaData(library_id_t file_id, const LibMetadata::Ptr & meta)
-{
-    SQLStatement sql(boost::format("SELECT xmp FROM files "
-                                   " WHERE id='%1%';")
-                     % file_id);
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            while(m_dbdrv->read_next_row()) {
-                std::string xml;
-                m_dbdrv->get_column_content(0, xml);
-                meta->unserialize(xml.c_str());
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-}
-
-
-
-
-bool Library::setInternalMetaDataInt(library_id_t file_id, const char* col,
-                                     int32_t value)
-{
-    bool ret = false;
-    DBG_OUT("setting metadata in column %s", col);
-    SQLStatement sql(boost::format("UPDATE files SET %1%='%2%' "
-                                   " WHERE id='%3%';")
-                     % col % value % file_id);
-    try {
-        ret = m_dbdrv->execute_statement(sql);
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-        ret = false;
-    }
-    return ret;
-}
-
-/** set metadata block
- * @param file_id the file ID to set the metadata block
- * @param meta the metadata block
- * @return false on error
- */
-bool Library::setMetaData(library_id_t file_id, const LibMetadata::Ptr & meta)
-{
-    bool ret = false;
-    SQLStatement sql(boost::format("UPDATE files SET xmp=?1 "
-                                   " WHERE id='%1%';")
-                     % file_id);
-    sql.bind(1, meta->serialize_inline());
-    try {
-        ret = m_dbdrv->execute_statement(sql);
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-        ret = false;
-    }
-    return ret;
-}
-
-
-/** set metadata
- * @param file_id the file ID to set the metadata block
- * @param meta the metadata index
- * @param value the value to set
- * @return false on error
- */
-bool Library::setMetaData(library_id_t file_id, fwk::PropertyIndex meta,
-                          const fwk::PropertyValue & value)
-{
-    bool retval = false;
-    DBG_OUT("setting metadata %x", meta);
-    DBG_ASSERT(check_property_type(meta, value.type()),
-               "wrong property value type");
-    switch(meta) {
-    case eng::NpXmpRatingProp:
-    case eng::NpXmpLabelProp:
-    case eng::NpTiffOrientationProp:
-    case eng::NpNiepceFlagProp:
-        if(is_empty(value) || is_integer(value)) {
-            // internal.
-            // make the column mapping more generic.
-            const char * col = NULL;
-            switch(meta) {
-            case eng::NpXmpRatingProp:
-                col = "rating";
-                break;
-            case eng::NpTiffOrientationProp:
-                col = "orientation";
-                break;
-            case eng::NpXmpLabelProp:
-                col = "label";
-                break;
-            case eng::NpNiepceFlagProp:
-                col = "flag";
-                break;
-            }
-            if(col) {
-                retval = setInternalMetaDataInt(file_id, col,
-                                                get_integer(value));
-            }
-        }
-        break;
-    case eng::NpIptcKeywordsProp:
-    {
-        // unassign all keywords
-        unassignAllKeywordsForFile(file_id);
-
-        fwk::StringArray keywords(boost::get<fwk::StringArray>(value));
-        for_each(keywords.begin(), keywords.end(),
-                 [this, file_id](const std::string & s) {
-                     library_id_t kwid = makeKeyword(s);
-                     if(kwid != -1) {
-                         assignKeyword(kwid, file_id);
-                     }
-                 });
-        break;
-    }
-    default:
-        // external
-        // TODO add the external metadata
-        //
-        break;
-    }
-    LibMetadata::Ptr metablock(new LibMetadata(file_id));
-    getMetaData(file_id, metablock);
-    retval = metablock->setMetaData(meta, value);
-    retval = metablock->touch();
-    retval = setMetaData(file_id, metablock);
-    return retval;
-}
-
-bool Library::writeMetaData(library_id_t file_id)
-{
-    return rewriteXmpForId(file_id, true);
-}
-
-bool Library::moveFileToFolder(library_id_t file_id, library_id_t folder_id)
-{
-    SQLStatement sql(boost::format("SELECT id FROM folders WHERE id = %1%;") % folder_id);
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            if(m_dbdrv->read_next_row()) {
-                // we have the destination folder
-                SQLStatement sql2(boost::format("UPDATE files SET parent_id = %1% "
-                                     " WHERE id = %2%;") % folder_id % file_id);
-                if(m_dbdrv->execute_statement(sql2)) {
-                    return true;
-                }
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    return false;
-}
-
-void Library::getAllLabels(const Label::ListPtr & l)
-{
-    SQLStatement sql("SELECT id,name,color FROM labels ORDER BY id");
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            while(m_dbdrv->read_next_row()) {
-                int32_t id;
-                std::string name;
-                std::string colour;
-                m_dbdrv->get_column_content(0, id);
-                m_dbdrv->get_column_content(1, name);
-                m_dbdrv->get_column_content(2, colour);
-                l->push_back(Label::Ptr(new Label(id, name, colour)));
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-}
-
-
-library_id_t Library::addLabel(const std::string & name, const std::string & colour)
-{
-    library_id_t ret = -1;
-
-    SQLStatement sql(boost::format("INSERT INTO labels (name,color)"
-                                   " VALUES ('%1%', '%2%')") 
-                     % name % colour);
-    if(m_dbdrv->execute_statement(sql)) {
-        library_id_t id = m_dbdrv->last_row_id();
-        DBG_OUT("last row inserted %d", (int)id);
-        ret = id;
-    }
-    return ret;
-}
-
-
-library_id_t Library::addLabel(const std::string & name,
-                               const fwk::RgbColour & c)
-{
-    return addLabel(name, c.to_string());
-}
-
-
-bool Library::updateLabel(library_id_t label_id, const std::string & name,
-                          const std::string & colour)
-{
-    SQLStatement sql(boost::format("UPDATE labels SET name='%2%', color='%3%'"
-                                   " WHERE id='%1%';")
-                     % label_id % name % colour);
-    try {
-        return m_dbdrv->execute_statement(sql);
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    return false;
-}
-
-
-bool Library::deleteLabel(library_id_t label_id)
-{
-    SQLStatement sql(boost::format("DELETE FROM labels "
-                                   " WHERE id='%1%';") % label_id);
-    try {
-        return m_dbdrv->execute_statement(sql);
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-    }
-    return false;
-}
-
-
-bool Library::getXmpIdsInQueue(std::vector<library_id_t> & ids)
-{
-    SQLStatement sql("SELECT id  FROM xmp_update_queue;");
-    try {
-        if(m_dbdrv->execute_statement(sql)) {
-            while(m_dbdrv->read_next_row()) {
-                library_id_t id;
-                m_dbdrv->get_column_content(0, id);
-                ids.push_back(id);
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-        return false;
-    }
-    return true;
-}
-
-
-bool Library::rewriteXmpForId(library_id_t id, bool write_xmp)
-{
-    SQLStatement del(boost::format("DELETE FROM xmp_update_queue "
-                                   " WHERE id='%1%';") % id);
-    SQLStatement getxmp(boost::format("SELECT xmp, main_file, xmp_file FROM files "
-                                   " WHERE id='%1%';") % id);
-    try {
-
-        if(m_dbdrv->execute_statement(del)
-           && m_dbdrv->execute_statement(getxmp)) {
-            while(write_xmp && m_dbdrv->read_next_row()) {
-                std::string xmp_buffer;
-                library_id_t main_file_id;
-                library_id_t xmp_file_id;
-                m_dbdrv->get_column_content(0, xmp_buffer);
-                m_dbdrv->get_column_content(1, main_file_id);
-                m_dbdrv->get_column_content(2, xmp_file_id);
-                std::string spath = getFsFile(main_file_id);
-                DBG_ASSERT(!spath.empty(), "couldn't find the main file");
-                std::string p;
-                if(xmp_file_id > 0) {
-                    p = getFsFile(xmp_file_id);
-                    DBG_ASSERT(!p.empty(), "couldn't find the xmp file path");
-                }
-                if(p.empty()) {
-                    p = fwk::path_replace_extension(spath, ".xmp");
-                    DBG_ASSERT(p != spath, "path must have been changed");
-                }
-                if(fwk::path_exists(p)) {
-                    DBG_OUT("%s already exist", p.c_str());
-                    // TODO backup
-                }
-                // TODO probably a faster way to do that
-                fwk::XmpMeta xmppacket;
-                xmppacket.unserialize(xmp_buffer.c_str());
-                // TODO use different API
-                FILE * f = fopen(p.c_str(), "w");
-                if(f) {
-                    std::string sidecar = xmppacket.serialize();
-                    fwrite(sidecar.c_str(), sizeof(std::string::value_type),
-                           sidecar.size(), f);
-                    fclose(f);
-                    if(xmp_file_id <= 0) {
-                        xmp_file_id = addFsFile(p);
-                        DBG_ASSERT(xmp_file_id > 0, "couldn't add xmp_file");
-                        bool res = addSidecarFileToBundle(id, xmp_file_id);
-                        DBG_ASSERT(res, "addSidecarFileToBundle failed");
-                    }
-                }
-                // TODO rewrite the modified date in the files table
-                // caveat: this will trigger this rewrite recursively.
-            }
-        }
-    }
-    catch(fwk::Exception & e)
-    {
-        DBG_OUT("db exception %s", e.what());
-        return false;
-    }
-
-    return true;
-}
-
-
-bool Library::processXmpUpdateQueue(bool write_xmp)
-{
-    bool retval = false;
-    std::vector<library_id_t> ids;
-    retval = getXmpIdsInQueue(ids);
-    if (retval) {
-        std::for_each(ids.begin(), ids.end(),
-                      [this, write_xmp] (auto id){
-                          this->rewriteXmpForId(id, write_xmp);
-                      });
-    }
-    return retval;
-}
-
-
 }
 /*
   Local Variables:
diff --git a/src/engine/db/library.hpp b/src/engine/db/library.hpp
index 60de203..bc3cfc0 100644
--- a/src/engine/db/library.hpp
+++ b/src/engine/db/library.hpp
@@ -17,206 +17,23 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
-
-#ifndef _DB_LIBRARY_H_
-#define _DB_LIBRARY_H_
-
-#include <string>
+#pragma once
 
 #include <memory>
-#include <boost/any.hpp>
-
-#include "fwk/toolkit/notificationcenter.hpp"
-#include "fwk/utils/db/iconnectiondriver.hpp"
-#include "fwk/utils/db/iconnectionmanagerdriver.hpp"
-#include "engine/db/librarytypes.hpp"
-#include "engine/db/libfolder.hpp"
-#include "engine/db/libfile.hpp"
-#include "engine/db/libmetadata.hpp"
-#include "engine/db/filebundle.hpp"
-#include "engine/db/keyword.hpp"
-#include "engine/db/label.hpp"
-#include "engine/library/notification.hpp"
-
-// The database schema version. Increase at each change.
-// Some will be persistent and have a conversion TBD.
-#define DB_SCHEMA_VERSION 6
-
-namespace fwk {
-class RgbColour;
-}
 
 namespace eng {
 
-/** Whether to import managed. */
-enum class LibraryManaged {
-    NO = 0,
-    YES
-};
-
-class Library
-{
-public:
-    typedef std::shared_ptr<Library> Ptr;
-
-    Library(const std::string & dir, uint64_t notif_id);
-    virtual ~Library();
-
-    bool ok() const
-        { return m_inited; }
-    /** set the main library directory */
-//             void setMainDir(const std::string & dir)
-//                     { m_maindir = dir; }
-    /** return the main directory */
-    const std::string & mainDir() const
-        { return m_maindir; }
-    /** get the path to the DB file */
-    const std::string & dbName() const
-        { return m_dbname; }
-
-    void notify(LibNotification&& n);
-
-    /** add a file to the library
-     * @param folder the path of the containing folder
-     * @param file the file path
-     * @param manage pass LibraryManaged::YES if the library *manage* the file.
-     * Currently unsupported.
-     */
-    library_id_t addFileAndFolder(const std::string & folder,
-                                  const std::string & file, LibraryManaged manage);
-
-    /** add a fs file to the library
-     * @param file the file path
-     * @return the id of the fs_file, -1 in case of error
-     */
-    library_id_t addFsFile(const std::string & file);
-
-    /** Get a FsFile from an id
-     * @param id the id of the FsFile
-     * @return the path. Empty if not found.
-     */
-    std::string getFsFile(library_id_t id);
-
-    /** add a file to the library
-     * @param folder_id the id of the containing folder
-     * @param file the file path
-     * @param manage pass LibraryManaged::YES if the library *manage* the file.
-     * Currently unsupported.
-     */
-    library_id_t addFile(library_id_t folder_id, const std::string & file, LibraryManaged manage);
+class Library;
+typedef std::shared_ptr<Library> LibraryPtr;
 
-    /** add a bundle of files to the library
-     * @param folder_id the id of the containing folder
-     * @param bundle the bundle
-     * @param manage pass LibraryManaged::YES if the library *manage* the file.
-     * Currently unsupported.
-     */
-    library_id_t addBundle(library_id_t folder_id,
-                           const eng::FileBundlePtr& bundle,
-                           LibraryManaged manage);
-    /** add a sidecar fsfile to a bundle (file)
-     * @param file_id the id of the file bundle
-     * @param fsfile_id the id of the fsfile
-     * @return true if success
-     */
-    bool addSidecarFileToBundle(library_id_t file_id, library_id_t fsfile_id);
-    /** add a jpeg fsfile to a bundle (file)
-     * @param file_id the id of the file bundle
-     * @param fsfile_id the id of the fsfile
-     * @return true if success
-     */
-    bool addJpegFileToBundle(library_id_t file_id, library_id_t fsfile_id);
-
-    /** Get a specific folder id from the library
-     * @param folder the folder path to check
-     * @return the folder, NULL if not found
-     */
-    LibFolderPtr getFolder(const std::string & folder);
-
-    /** Add a folder
-     * @param folder the folder path
-     */
-    LibFolderPtr addFolder(const std::string & folder);
-    /** List all the folders.
-     * @param l the list of LibFolder
-     */
-    void getAllFolders(const LibFolderListPtr & l);
-
-    /** List the folder content
-     * @param folder_id id of the folder
-     * @param fl the resulting file list
-     */
-    void getFolderContent(library_id_t folder_id, const LibFileListPtr & fl);
-    int countFolder(library_id_t folder_id);
-    void getAllKeywords(const KeywordListPtr & l);
-    void getKeywordContent(library_id_t keyword_id, const LibFileListPtr & fl);
-    /** get the metadata block (XMP) */
-    void getMetaData(library_id_t file_id, const LibMetadata::Ptr & );
-    /** set the metadata block (XMP) */
-    bool setMetaData(library_id_t file_id, const LibMetadata::Ptr & );
-    bool setMetaData(library_id_t file_id, fwk::PropertyIndex meta,
-                     const fwk::PropertyValue & value);
-    bool writeMetaData(library_id_t file_id);
-
-    bool moveFileToFolder(library_id_t file_id, library_id_t folder_id);
-
-    void getAllLabels(const eng::Label::ListPtr & l);
-    library_id_t addLabel(const std::string & name, const std::string & colour);
-    library_id_t addLabel(const std::string & name, const fwk::RgbColour & c);
-    bool updateLabel(library_id_t label_id, const std::string & name, const std::string & colour);
-    bool deleteLabel(library_id_t label_id);
-
-    /** Trigger the processing of the XMP update queue */
-    bool processXmpUpdateQueue(bool rewrite_xmp);
-
-    /** Locate the keyword, creating it if needed
-     * @param keyword the keyword to locate
-     * @return -1 if not found (shouldn't happen) or the id of the
-     * keyword, either found or just created.
-     */
-    library_id_t makeKeyword(const std::string & keyword);
-    /** Assign a keyword to a file.
-     * @param kw_id the keyword id
-     * @param file_id the file id
-     * @return true if success, false if error
-     */
-    bool assignKeyword(library_id_t kw_id, library_id_t file_id);
-    /** Unassign all keyword for a file.
-     * @param file_id the file id
-     * @return true if success, false if error
-     */
-    bool unassignAllKeywordsForFile(library_id_t file_id);
-
-    int checkDatabaseVersion();
-
-    db::IConnectionDriver::Ptr dbDriver()
-        { return m_dbdrv; }
-private:
-    bool init();
-    bool _initDb();
-
-    bool getXmpIdsInQueue(std::vector<library_id_t> & ids);
-    /** rewrite the XMP sidecar for the file whose id is %id
-     * and remove it from the queue.
-     */
-    bool rewriteXmpForId(library_id_t id, bool rewrite_xmp);
-
-    /** set an "internal" metadata of type int */
-    bool setInternalMetaDataInt(library_id_t file_id, const char* col,
-                                int32_t value);
-
-    std::string                       m_maindir;
-    std::string                       m_dbname;
-    db::IConnectionManagerDriver::Ptr m_dbmgr;
-    db::IConnectionDriver::Ptr        m_dbdrv;
-    uint64_t m_notif_id;
-    bool                              m_inited;
-};
+LibraryPtr library_new(const char *dir, uint64_t notif_id);
 
+/** Whether to import managed. */
+enum class LibraryManaged { NO = 0, YES };
 }
 
-#endif
+extern "C" bool engine_db_library_ok(const eng::Library *library);
+
 /*
   Local Variables:
   mode:c++
@@ -226,4 +43,3 @@ private:
   fill-column:99
   End:
 */
-
diff --git a/src/engine/db/library.rs b/src/engine/db/library.rs
index 5aa955b..739339b 100644
--- a/src/engine/db/library.rs
+++ b/src/engine/db/library.rs
@@ -1,5 +1,5 @@
 /*
- * niepce - eng/db/library.rs
+ * niepce - engine/db/library.rs
  *
  * Copyright (C) 2017 Hubert Figuière
  *
@@ -17,34 +17,49 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+use libc::{
+    c_char,
+    c_void
+};
 use std::path::{
     Path,
     PathBuf
 };
+use std::ffi::CStr;
 use std::fs::File;
 use std::io::Write;
 use rusqlite;
+
 use super::{
     FromDb,
     LibraryId
 };
+use super::label::Label;
 use super::libfolder;
 use super::libfolder::LibFolder;
 use super::libfile;
 use super::libfile::LibFile;
+use super::libmetadata::LibMetadata;
+use super::filebundle::FileBundle;
 use super::keyword::Keyword;
-
+use engine::library::notification::Notification as LibNotification;
+use engine::library::notification::engine_library_notify;
 use fwk;
+use root::fwk::{
+    PropertyValue,
+    is_empty,
+    is_integer,
+    get_integer,
+    get_string_array,
+    string_array_len,
+    string_array_at_cstr
+};
+use root::eng::NiepceProperties as Np;
+pub use root::eng::LibraryManaged as Managed;
 
 const DB_SCHEMA_VERSION: i32 = 6;
 const DATABASENAME: &str = "niepcelibrary.db";
 
-#[repr(i32)]
-pub enum Managed {
-    NO = 0,
-    YES = 1
-}
-
 pub struct Library {
     maindir: PathBuf,
     dbpath: PathBuf,
@@ -53,20 +68,13 @@ pub struct Library {
     notif_id: u64,
 }
 
-extern "C" {
-    pub fn lib_notification_notify_new_lib_created(notif_id: u64);
-    pub fn lib_notification_notify_xmp_needs_update(notif_id: u64);
-    pub fn lib_notification_notify_kw_added(notif_id: u64, keyword: *mut Keyword);
-}
-
-
 impl Library {
 
-    pub fn new(dir: &Path, notif_id: u64) -> Library {
-        let mut dbpath = PathBuf::from(dir);
+    pub fn new(dir: PathBuf, notif_id: u64) -> Library {
+        let mut dbpath = dir.clone();
         dbpath.push(DATABASENAME);
         let mut lib = Library {
-            maindir: PathBuf::from(dir),
+            maindir: dir,
             dbpath: dbpath,
             dbconn: None,
             inited: false,
@@ -82,7 +90,7 @@ impl Library {
         let conn_attempt = rusqlite::Connection::open(self.dbpath.clone());
         if let Ok(conn) = conn_attempt {
             conn.create_scalar_function("rewrite_xmp", 0, false, |_| {
-                unsafe { lib_notification_notify_xmp_needs_update(self.notif_id); }
+                self.notify(Box::new(LibNotification::XmpNeedsUpdate));
                 Ok(true)
             });
             self.dbconn = Some(conn);
@@ -181,12 +189,30 @@ impl Library {
                           SELECT rewrite_xmp();\
                           END", &[]).unwrap();
 
-            unsafe { lib_notification_notify_new_lib_created(self.notif_id); }
+            self.notify(Box::new(LibNotification::LibCreated));
             return true;
         }
         false
     }
 
+    pub fn notify(&self, notif: Box<LibNotification>) {
+        unsafe { engine_library_notify(self.notif_id, Box::into_raw(notif) as *mut c_void); }
+    }
+
+    pub fn add_jpeg_file_to_bundle(&self, file_id: LibraryId, fsfile_id: LibraryId) -> bool {
+        if let Some(ref conn) = self.dbconn {
+            let filetype: i32 = libfile::FileType::RAW_JPEG.into();
+            if let Ok(c) = conn.execute("UPDATE files SET jpeg_file=:1 file_type=:3 WHERE id=:2;",
+                                        &[&fsfile_id, &file_id,
+                                          &filetype]) {
+                if c == 1 {
+                    return true;
+                }
+            }
+        }
+        false
+    }
+
     pub fn add_sidecar_file_to_bundle(&self, file_id: LibraryId, fsfile_id: LibraryId) -> bool {
         if let Some(ref conn) = self.dbconn {
             if let Ok(c) = conn.execute("UPDATE files SET xmp_file=:1 WHERE id=:2;",
@@ -350,10 +376,30 @@ impl Library {
         None
     }
 
-    pub fn add_file(&self, folder_id: LibraryId, file: &str, _manage: Managed) -> LibraryId {
+    pub fn add_bundle(&self, folder_id: LibraryId, bundle: &FileBundle,
+                      manage: Managed) -> LibraryId {
+        let file_id = self.add_file(folder_id, bundle.main(), manage);
+        if file_id > 0 {
+            if !bundle.sidecar().is_empty() {
+                let fsfile_id = self.add_fs_file(bundle.sidecar());
+                if fsfile_id > 0 {
+                    self.add_sidecar_file_to_bundle(file_id, fsfile_id);
+                }
+            }
+            if !bundle.jpeg().is_empty() {
+                let fsfile_id = self.add_fs_file(bundle.jpeg());
+                if fsfile_id > 0 {
+                    self.add_jpeg_file_to_bundle(file_id, fsfile_id);
+                }
+            }
+        }
+        file_id
+    }
+
+    pub fn add_file(&self, folder_id: LibraryId, file: &str, manage: Managed) -> LibraryId {
         let mut ret: LibraryId = -1;
-        //DBG_ASSERT(manage == Managed::NO, "manage not supported");
-        //DBG_ASSERT(folder_id != -1, "invalid folder ID");
+        dbg_assert!(manage == Managed::NO, "manage not supported");
+        dbg_assert!(folder_id != -1, "invalid folder ID");
         let mime = fwk::MimeType::new(file);
         let file_type = libfile::mimetype_to_filetype(&mime);
         let label_id: LibraryId = 0;
@@ -450,9 +496,9 @@ impl Library {
                     return -1;
                 }
                 let keyword_id = conn.last_insert_rowid();
-                unsafe { lib_notification_notify_kw_added(
-                    self.notif_id,
-                    Box::into_raw(Box::new(Keyword::new(keyword_id, keyword)))); }
+                self.notify(
+                    Box::new(LibNotification::AddedKeyword(
+                        Keyword::new(keyword_id, keyword))));
                 return keyword_id;
             }
 
@@ -478,6 +524,120 @@ impl Library {
                                WHERE keyword_id=:1) ")
     }
 
+    pub fn get_metadata(&self, file_id: LibraryId) -> Option<LibMetadata> {
+        if let Some(ref conn) = self.dbconn {
+            let sql = format!("SELECT {} FROM {} WHERE id=:1",
+                              LibMetadata::read_db_columns(),
+                              LibMetadata::read_db_tables());
+            if let Ok(mut stmt) = conn.prepare(&sql) {
+                let mut rows = stmt.query(&[&file_id]).unwrap();
+                if let Some(Ok(row)) = rows.next() {
+                    let meta = LibMetadata::read_from(&row);
+                    return Some(meta);
+                }
+            }
+        }
+        None
+    }
+
+
+    fn unassign_all_keywords_for_file(&self, file_id: LibraryId) -> bool {
+        if let Some(ref conn) = self.dbconn {
+            if let Ok(_) = conn.execute("DELETE FROM keywording \
+                                         WHERE file_id=:1;",
+                                        &[&file_id]) {
+                // XXX check success.
+                return true;
+            }
+        }
+        false
+    }
+
+    fn set_internal_metadata(&self, file_id: LibraryId, column: &str, value: i32) -> bool {
+        if let Some(ref conn) = self.dbconn {
+            if let Ok(_) = conn.execute("UPDATE files SET :1=:2 \
+                                         WHERE id=:3;",
+                                        &[&column, &value, &file_id]) {
+                // XXX check success.
+                return true;
+            }
+        }
+        false
+    }
+
+    fn set_metadata_block(&self, file_id: LibraryId, metablock: &LibMetadata) -> bool {
+        let xmp = metablock.serialize_inline();
+        if let Some(ref conn) = self.dbconn {
+            if let Ok(_) = conn.execute("UPDATE files SET xmp=:1 \
+                                         WHERE id=:2;",
+                                        &[&xmp, &file_id]) {
+                // XXX check success.
+                return true;
+            }
+        }
+        false
+    }
+
+    pub fn set_metadata(&self, file_id: LibraryId, meta: Np,
+                        value: &PropertyValue) -> bool {
+        let mut retval = false;
+        match meta {
+            Np::NpXmpRatingProp |
+            Np::NpXmpLabelProp |
+            Np::NpTiffOrientationProp |
+            Np::NpNiepceFlagProp => {
+                if unsafe { is_empty(value) || is_integer(value) } {
+                    // internal
+                    // make the column mapping more generic.
+                    let column = match meta {
+                        Np::NpXmpRatingProp =>
+                            "rating",
+                        Np::NpXmpLabelProp =>
+                            "orientation",
+                        Np::NpTiffOrientationProp =>
+                            "label",
+                        Np::NpNiepceFlagProp =>
+                            "flag",
+                        _ =>
+                            unreachable!()
+                    };
+                    if !column.is_empty() {
+                        retval = self.set_internal_metadata(file_id, column,
+                                                            unsafe { get_integer(value) });
+                        if !retval {
+                            return false;
+                        }
+                    }
+                }
+            },
+            Np::NpIptcKeywordsProp => {
+                self.unassign_all_keywords_for_file(file_id);
+
+                let keywords = unsafe { get_string_array(value) };
+                let length = unsafe { string_array_len(keywords) };
+                for i in 0..length {
+                    let s = unsafe { string_array_at_cstr(keywords, i) };
+                    let cstr = unsafe { CStr::from_ptr(s) }.to_string_lossy();
+                    let id = self.make_keyword(&cstr);
+                    if id != -1 {
+                        self.assign_keyword(id, file_id);
+                    }
+                }
+            }
+            _ => {
+                // XXX TODO
+                dbg_out!("unhandled meta {}", meta as u32);
+            }
+        }
+        if let Some(mut metablock) = self.get_metadata(file_id) {
+            metablock.set_metadata(meta, value);
+            metablock.touch();
+            retval = self.set_metadata_block(file_id, &metablock);
+        }
+
+        retval
+    }
+
     pub fn move_file_to_folder(&self, file_id: LibraryId, folder_id: LibraryId) -> bool  {
         if let Some(ref conn) = self.dbconn {
             if let Ok(mut stmt) = conn.prepare("SELECT id FROM folders WHERE \
@@ -495,6 +655,65 @@ impl Library {
         false
     }
 
+    pub fn get_all_labels(&self) -> Vec<Label> {
+        if let Some(ref conn) = self.dbconn {
+            let sql = format!("SELECT {} FROM {} ORDER BY id;",
+                              Label::read_db_columns(),
+                              Label::read_db_tables());
+            if let Ok(mut stmt) = conn.prepare(&sql) {
+                let mut labels: Vec<Label> = vec!();
+                let mut rows = stmt.query(&[]).unwrap();
+                while let Some(Ok(row)) = rows.next() {
+                    let label = Label::read_from(&row);
+                    labels.push(label);
+                }
+                return labels;
+            }
+        }
+        vec!()
+    }
+
+    pub fn add_label(&self, name: &str, colour: &str) -> LibraryId {
+        if let Some(ref conn) = self.dbconn {
+            if let Ok(c) = conn.execute("INSERT INTO  labels (name,color) \
+                                         VALUES (:1, :2);",
+                                        &[&name, &colour]) {
+                if c != 1 {
+                    return -1;
+                }
+                let label_id = conn.last_insert_rowid();
+                dbg_out!("last row inserted {}", label_id);
+                return label_id;
+            }
+        }
+        -1
+    }
+
+    pub fn update_label(&self, label_id: LibraryId, name: &str,
+                        colour: &str) -> bool {
+        if let Some(ref conn) = self.dbconn {
+            if let Ok(_) = conn.execute("UPDATE labels SET name=:2, color=:3 \
+                                         FROM labels WHERE id=:1;",
+                                        &[&label_id, &name, &colour]) {
+                // XXX check success.
+                return true;
+            }
+        }
+        false
+
+    }
+
+    pub fn delete_label(&self, label_id: LibraryId) -> bool {
+        if let Some(ref conn) = self.dbconn {
+            if let Ok(_) = conn.execute("DELETE FROM labels WHERE id=:1;",
+                                        &[&label_id]) {
+                // XXX check success.
+                return true;
+            }
+        }
+        false
+    }
+
     fn get_xmp_ids_in_queue(&self) -> Option<Vec<LibraryId>> {
         if let Some(ref conn) = self.dbconn {
             if let Ok(mut stmt) = conn.prepare("SELECT id FROM xmp_update_queue;") {
@@ -510,6 +729,10 @@ impl Library {
         None
     }
 
+    pub fn write_metadata(&self, id: LibraryId) -> bool {
+        self.rewrite_xmp_for_id(id, true)
+    }
+
     fn rewrite_xmp_for_id(&self, id: LibraryId, write_xmp: bool) -> bool {
         // XXX
         // Rework this so that:
@@ -587,6 +810,24 @@ impl Library {
     }
 }
 
+#[no_mangle]
+pub extern fn engine_db_library_new(path: *const c_char, notif_id: u64) -> *mut Library {
+    let l = Box::new(
+        Library::new(PathBuf::from(&*unsafe { CStr::from_ptr(path) }.to_string_lossy()),
+                     notif_id));
+    Box::into_raw(l)
+}
+
+#[no_mangle]
+pub extern fn engine_db_library_delete(l: *mut Library) {
+    unsafe { Box::from_raw(l); }
+}
+
+#[no_mangle]
+pub extern fn engine_db_library_ok(l: &Library) -> bool {
+    l.is_ok()
+}
+
 #[cfg(test)]
 mod test {
     use std::path::Path;
@@ -611,7 +852,7 @@ mod test {
     fn library_works() {
         use super::Library;
 
-        let lib = Library::new(Path::new("."), 0);
+        let lib = Library::new(PathBuf::from("."), 0);
         let _autodelete = AutoDelete::new(lib.dbpath());
 
         assert!(lib.is_ok());
diff --git a/src/engine/db/mod.rs b/src/engine/db/mod.rs
index b946445..7b40054 100644
--- a/src/engine/db/mod.rs
+++ b/src/engine/db/mod.rs
@@ -23,6 +23,7 @@ pub mod keyword;
 pub mod label;
 pub mod libfile;
 pub mod libfolder;
+pub mod libmetadata;
 pub mod library;
 
 pub type LibraryId = i64;
diff --git a/src/engine/db/properties-def.hpp b/src/engine/db/properties-def.hpp
index ecd56de..373c594 100644
--- a/src/engine/db/properties-def.hpp
+++ b/src/engine/db/properties-def.hpp
@@ -44,7 +44,7 @@ DEFINE_PROPERTY(NpExifExposureBiasProp, MAKE_METADATA_IDX(META_NS_EXIF, META_EXI
 DEFINE_PROPERTY(NpExifFlashFiredProp, MAKE_METADATA_IDX(META_NS_EXIF, META_EXIF_FLASHFIRED), NS_EXIF, 
"Flash/exif:Fired", int32_t)
 DEFINE_PROPERTY(NpExifAuxFlashCompensationProp, MAKE_METADATA_IDX(META_NS_EXIF, 
META_EXIF_AUX_FLASHCOMPENSATION), NS_EXIF_AUX, "FlashCompensation", int32_t)
 DEFINE_PROPERTY(NpExifWbProp, MAKE_METADATA_IDX(META_NS_EXIF, META_EXIF_WB), NS_EXIF, "WhiteBalance", 
int32_t)
-DEFINE_PROPERTY(NpExifDateTimeOriginalProp, MAKE_METADATA_IDX(META_NS_EXIF, META_EXIF_DATETIMEORIGINAL), 
NS_EXIF, "DateTimeOriginal", fwk::Date)
+DEFINE_PROPERTY(NpExifDateTimeOriginalProp, MAKE_METADATA_IDX(META_NS_EXIF, META_EXIF_DATETIMEORIGINAL), 
NS_EXIF, "DateTimeOriginal", fwk::DatePtr)
 DEFINE_PROPERTY(NpExifFocalLengthProp, MAKE_METADATA_IDX(META_NS_EXIF, META_EXIF_FOCALLENGTH), NS_EXIF, 
"FocalLength", int32_t)
 DEFINE_PROPERTY(NpExifGpsLongProp, MAKE_METADATA_IDX(META_NS_EXIF, META_EXIF_GPSLONGITUDE), NS_EXIF, 
"GPSLongitude", std::string)
 DEFINE_PROPERTY(NpExifGpsLatProp, MAKE_METADATA_IDX(META_NS_EXIF, META_EXIF_GPSLATITUDE), NS_EXIF, 
"GPSLatitude", std::string)
diff --git a/src/engine/importer/cameraimporter.cpp b/src/engine/importer/cameraimporter.cpp
index 96a49ca..433de13 100644
--- a/src/engine/importer/cameraimporter.cpp
+++ b/src/engine/importer/cameraimporter.cpp
@@ -19,6 +19,8 @@
  */
 
 #include <glibmm/miscutils.h>
+#include <giomm/file.h>
+
 #include "cameraimporter.hpp"
 
 #include "fwk/base/debug.hpp"
diff --git a/src/engine/library/commands.hpp b/src/engine/library/commands.hpp
index 2f99878..73b1910 100644
--- a/src/engine/library/commands.hpp
+++ b/src/engine/library/commands.hpp
@@ -18,65 +18,34 @@
  */
 
 
+#pragma once
 
-#ifndef __LIBRARY_COMMANDS_H__
-#define __LIBRARY_COMMANDS_H__
-
-#include "op.hpp"
 #include "fwk/utils/files.hpp"
 #include "engine/db/library.hpp"
 
-namespace eng {
-
-/** Marshalling and demarshalling of commands ops */ 
-class Commands 
-{
-public:
-
-               // commands: execute an op
-//             static void cmdQueryFiles(const Library::Ptr & lib);
-//             static void cmdUpdateFiles(const Library::Ptr & lib);
-
-               static void cmdListAllFolders(const Library::Ptr & lib);
-               static void cmdListAllKeywords(const Library::Ptr & lib);
-    static void cmdImportFile(const Library::Ptr & lib,
-                              const std::string & path,
-                              LibraryManaged manage);
-    static void cmdImportFiles(const Library::Ptr & lib,
-                               const std::string & folder,
-                               const fwk::FileList::Ptr & files,
-                               LibraryManaged manage);
-               static void cmdQueryFolderContent(const Library::Ptr & lib, 
-                                      library_id_t folder_id);
-               static void cmdCountFolder(const Library::Ptr & lib, 
-                                                                  library_id_t folder_id);
-               static void cmdQueryKeywordContent(const Library::Ptr & lib, 
-                                       library_id_t keyword_id);
-               static void cmdRequestMetadata(const Library::Ptr & lib,
-                                   library_id_t file_id);
-    static void cmdSetMetadata(const Library::Ptr & lib,
-                               library_id_t file_id, fwk::PropertyIndex meta, 
-                               const fwk::PropertyValue & value);
-    static void cmdWriteMetadata(const Library::Ptr & lib,
-                                 library_id_t file_id);
-    static void cmdMoveFileToFolder(const Library::Ptr & lib, 
-                                    library_id_t file_id, library_id_t from_folder_id,
-                                    library_id_t to_folder_id);
-    static void cmdListAllLabels(const Library::Ptr & lib);
-    static void cmdCreateLabel(const Library::Ptr & lib, const std::string & s, 
-                               const std::string & color);
-    static void cmdDeleteLabel(const Library::Ptr & lib,
-                               int label_id);
-    static void cmdUpdateLabel(const Library::Ptr & lib,
-                               eng::library_id_t label_id, const std::string & name,
-                               const std::string & color);
-    static void cmdProcessXmpUpdateQueue(const Library::Ptr & lib, bool write_xmp);
-};
-
+extern "C" {
+    bool cmd_list_all_keywords(eng::Library* lib);
+    bool cmd_list_all_folders(eng::Library* lib);
+    bool cmd_import_file(eng::Library* lib, const char* path, eng::LibraryManaged);
+    bool cmd_import_files(eng::Library* lib, const char* folder, fwk::FileList* files,
+                          eng::LibraryManaged);
+    bool cmd_request_metadata(eng::Library* lib, eng::library_id_t id);
+    bool cmd_query_folder_content(eng::Library* lib, eng::library_id_t folder_id);
+    bool cmd_count_folder(eng::Library* lib, eng::library_id_t folder_id);
+    bool cmd_query_keyword_content(eng::Library* lib, eng::library_id_t id);
+    bool cmd_write_metadata(eng::Library* lib, eng::library_id_t id);
+    bool cmd_move_file_to_folder(eng::Library* lib, eng::library_id_t id,
+                                 eng::library_id_t from, eng::library_id_t to);
+    bool cmd_set_metadata(eng::Library* lib, eng::library_id_t id, fwk::PropertyIndex meta,
+                          const fwk::PropertyValue* value);
+    bool cmd_list_all_labels(eng::Library* lib);
+    bool cmd_create_label(eng::Library* lib, const char* name, const char* colour);
+    bool cmd_delete_label(eng::Library* lib, eng::library_id_t id);
+    bool cmd_update_label(eng::Library* lib, eng::library_id_t id, const char* name,
+                          const char* colour);
+    bool cmd_process_xmp_update_queue(eng::Library* lib, bool write_xmp);
 }
 
-
-#endif
 /*
   Local Variables:
   mode:c++
diff --git a/src/engine/library/commands.rs b/src/engine/library/commands.rs
new file mode 100644
index 0000000..4b8d4c6
--- /dev/null
+++ b/src/engine/library/commands.rs
@@ -0,0 +1,239 @@
+/*
+ * niepce - engine/library/commands.rs
+ *
+ * Copyright (C) 2017 Hubert Figuière
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+use libc::c_char;
+use std::ffi::CStr;
+use std::path::Path;
+
+use engine::db::LibraryId;
+use engine::db::library::{
+    Library,
+    Managed
+};
+use engine::db::filebundle::FileBundle;
+use engine::db::label::Label;
+use engine::db::libfolder::LibFolder;
+use super::notification::Notification as LibNotification;
+use super::notification::{
+    Content,
+    FileMove,
+    FolderCount,
+    MetadataChange,
+};
+use root::eng::LibFile as CLibFile;
+use root::eng::NiepceProperties as Np;
+use root::fwk::{
+    FileList,
+    PropertyValue
+};
+
+#[no_mangle]
+pub fn cmd_list_all_keywords(lib: &Library) -> bool {
+    let list = lib.get_all_keywords();
+    // XXX change this to "LoadKeywords"
+    for kw in list {
+        lib.notify(Box::new(LibNotification::AddedKeyword(kw)));
+    }
+    true
+}
+
+#[no_mangle]
+pub fn cmd_list_all_folders(lib: &Library) -> bool {
+    let list = lib.get_all_folders();
+    // XXX change this to "LoadedFodlers"
+    for folder in list {
+        lib.notify(Box::new(LibNotification::AddedFolder(folder)));
+    }
+    true
+}
+
+#[no_mangle]
+pub fn cmd_import_file(lib: &Library, file_path: *const c_char, manage: Managed) -> bool {
+    dbg_assert!(manage == Managed::NO, "managing file is currently unsupported");
+
+    let path = String::from(unsafe { CStr::from_ptr(file_path) }.to_string_lossy());
+
+    let mut bundle = FileBundle::new();
+    bundle.add(&path);
+
+    let folder = Path::new(&path).parent().unwrap_or(Path::new(""));
+
+    let libfolder: LibFolder;
+    match lib.get_folder(&*folder.to_string_lossy()) {
+        Some(lf) =>
+            libfolder = lf,
+        _ => {
+            if let Some(lf) = lib.add_folder(&*folder.to_string_lossy()) {
+                libfolder = lf.clone();
+                lib.notify(Box::new(LibNotification::AddedFolder(lf)));
+            } else {
+                return false;
+            }
+        }
+    }
+
+    lib.add_bundle(libfolder.id(), &bundle, manage);
+    lib.notify(Box::new(LibNotification::AddedFile));
+    true
+}
+
+#[no_mangle]
+pub fn cmd_import_files(lib: &Library, folder: *const c_char, files: &mut FileList,
+                        manage: Managed) -> bool {
+    dbg_assert!(manage == Managed::NO, "managing file is currently unsupported");
+
+    let bundles = FileBundle::filter_bundles(files);
+    let libfolder: LibFolder;
+    let folder = unsafe { CStr::from_ptr(folder) }.to_string_lossy();
+    match lib.get_folder(&folder) {
+        Some(lf) =>
+            libfolder = lf,
+        _ => {
+            if let Some(lf) = lib.add_folder(&folder) {
+                libfolder = lf.clone();
+                lib.notify(Box::new(LibNotification::AddedFolder(lf)));
+            } else {
+                return false;
+            }
+        }
+    }
+    let folder_id = libfolder.id();
+    for bundle in bundles {
+        lib.add_bundle(folder_id, &bundle, manage.clone());
+    }
+    lib.notify(Box::new(LibNotification::AddedFiles));
+    true
+}
+
+#[no_mangle]
+pub fn cmd_request_metadata(lib: &Library, file_id: LibraryId) -> bool {
+    if let Some(lm) = lib.get_metadata(file_id) {
+        lib.notify(Box::new(LibNotification::MetadataQueried(lm)));
+        return true;
+    }
+    false
+}
+
+#[no_mangle]
+pub fn cmd_query_folder_content(lib: &Library, folder_id: LibraryId) -> bool {
+    let fl = lib.get_folder_content(folder_id);
+    let mut value = Box::new(
+        LibNotification::FolderContentQueried(unsafe { Content::new(folder_id) }));
+    if let LibNotification::FolderContentQueried(ref mut content) = *value {
+        for f in fl {
+            unsafe { content.push(Box::into_raw(Box::new(f)) as *mut CLibFile) };
+        }
+    }
+    lib.notify(value);
+    true
+}
+
+#[no_mangle]
+pub fn cmd_set_metadata(lib: &Library, id: LibraryId, meta: Np,
+                        value: &PropertyValue) -> bool {
+    lib.set_metadata(id, meta, value);
+    lib.notify(Box::new(LibNotification::MetadataChanged(
+        MetadataChange{id: id, meta: meta as u32, value: value.clone()})));
+    true
+}
+
+#[no_mangle]
+pub fn cmd_count_folder(lib: &Library, folder_id: LibraryId) -> bool {
+    let count = lib.count_folder(folder_id);
+    lib.notify(Box::new(LibNotification::FolderCounted(
+        FolderCount{folder: folder_id, count: count})));
+    true
+}
+
+#[no_mangle]
+pub fn cmd_query_keyword_content(lib: &Library, keyword_id: LibraryId) -> bool {
+    let fl = lib.get_keyword_content(keyword_id);
+    let mut content = unsafe { Content::new(keyword_id) };
+    for f in fl {
+        unsafe { content.push(Box::into_raw(Box::new(f)) as *mut CLibFile) };
+    }
+    lib.notify(Box::new(LibNotification::KeywordContentQueried(content)));
+    true
+}
+
+#[no_mangle]
+pub fn cmd_write_metadata(lib: &Library, file_id: LibraryId) -> bool {
+    lib.write_metadata(file_id)
+}
+
+#[no_mangle]
+pub fn cmd_move_file_to_folder(lib: &Library, file_id: LibraryId, from: LibraryId,
+                               to: LibraryId) -> bool {
+
+    if lib.move_file_to_folder(file_id, to) {
+        lib.notify(Box::new(LibNotification::FileMoved(
+            FileMove{file: file_id, from: from, to: to})));
+        lib.notify(Box::new(LibNotification::FolderCountChanged(
+            FolderCount{folder: from, count: -1})));
+        lib.notify(Box::new(LibNotification::FolderCountChanged(
+            FolderCount{folder: to, count: 1})));
+        return true;
+    }
+    false
+}
+
+#[no_mangle]
+pub fn cmd_list_all_labels(lib: &Library) -> bool {
+    let l = lib.get_all_labels();
+    // XXX change this notification type
+    for label in l {
+        lib.notify(Box::new(LibNotification::AddedLabel(label)));
+    }
+    true
+}
+
+#[no_mangle]
+pub fn cmd_create_label(lib: &Library, name: *const c_char, colour: *const c_char) -> bool {
+    let name = unsafe { CStr::from_ptr(name) }.to_string_lossy();
+    let colour = unsafe { CStr::from_ptr(colour) }.to_string_lossy();
+    let id = lib.add_label(&name, &colour);
+    if id != -1 {
+        let l = Label::new(id, &name, &colour);
+        lib.notify(Box::new(LibNotification::AddedLabel(l)));
+    }
+    true
+}
+
+#[no_mangle]
+pub fn cmd_delete_label(lib: &Library, label_id: LibraryId) -> bool {
+    lib.delete_label(label_id);
+    lib.notify(Box::new(LibNotification::LabelDeleted(label_id)));
+    true
+}
+
+#[no_mangle]
+pub fn cmd_update_label(lib: &Library, label_id: LibraryId, name: *const c_char,
+                        colour: *const c_char) -> bool {
+    let name = unsafe { CStr::from_ptr(name) }.to_string_lossy();
+    let colour = unsafe { CStr::from_ptr(colour) }.to_string_lossy();
+    lib.update_label(label_id, &name, &colour);
+    let label = Label::new(label_id, &name, &colour);
+    lib.notify(Box::new(LibNotification::LabelChanged(label)));
+    true
+}
+
+#[no_mangle]
+pub fn cmd_process_xmp_update_queue(lib: &Library, write_xmp: bool) -> bool {
+    lib.process_xmp_update_queue(write_xmp)
+}
diff --git a/src/engine/library/mod.rs b/src/engine/library/mod.rs
new file mode 100644
index 0000000..d36c58e
--- /dev/null
+++ b/src/engine/library/mod.rs
@@ -0,0 +1,4 @@
+
+
+pub mod commands;
+pub mod notification;
diff --git a/src/engine/library/notification.cpp b/src/engine/library/notification.cpp
index 4444a98..5c13b14 100644
--- a/src/engine/library/notification.cpp
+++ b/src/engine/library/notification.cpp
@@ -18,55 +18,39 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "fwk/base/debug.hpp"
-#include "niepce/notifications.hpp"
 #include "engine/library/notification.hpp"
 #include "engine/db/keyword.hpp"
+#include "fwk/base/debug.hpp"
 #include "fwk/toolkit/notificationcenter.hpp"
+#include "niepce/notifications.hpp"
 
 namespace {
 
-void notify(uint64_t notif_id, eng::LibNotification&& ln)
+/**
+ * Wrap a pointer (from Rust) into a shared_ptr<>
+ */
+eng::LibNotificationPtr
+engine_library_notification_wrap(eng::LibNotification* n)
+{
+    return eng::LibNotificationPtr(n, &engine_library_notification_delete);
+}
+}
+
+extern "C" void
+engine_library_notify(uint64_t notif_id,
+                      eng::LibNotification* notification)
 {
+    auto ln = engine_library_notification_wrap(notification);
     auto wnc = fwk::NotificationCenter::get_nc(notif_id);
     auto nc = wnc.lock();
     if (nc) {
         DBG_OUT("notif");
         // pass the notification
-        fwk::Notification::Ptr n(new fwk::Notification(niepce::NOTIFICATION_LIB));
+        fwk::Notification::Ptr n(
+            new fwk::Notification(niepce::NOTIFICATION_LIB));
         n->setData(boost::any(ln));
         nc->post(std::move(n));
     } else {
         DBG_OUT("try to send a notification without notification center");
     }
 }
-
-}
-
-// Rust glue
-extern "C" {
-
-void lib_notification_notify_new_lib_created(uint64_t notif_id)
-{
-    eng::LibNotification ln =
-        eng::LibNotification::make<eng::LibNotification::Type::NEW_LIBRARY_CREATED>({});
-    notify(notif_id, std::move(ln));
-}
-
-void lib_notification_notify_xmp_needs_update(uint64_t notif_id)
-{
-    eng::LibNotification ln =
-        eng::LibNotification::make<eng::LibNotification::Type::XMP_NEEDS_UPDATE>({});
-    notify(notif_id, std::move(ln));
-}
-
-void lib_notification_notify_kw_added(uint64_t notif_id, eng::Keyword* keyword)
-{
-    eng::KeywordPtr kw = eng::keyword_wrap(keyword);
-    eng::LibNotification ln =
-        eng::LibNotification::make<eng::LibNotification::Type::ADDED_KEYWORD>({kw});
-    notify(notif_id, std::move(ln));
-}
-
-
-}
diff --git a/src/engine/library/notification.hpp b/src/engine/library/notification.hpp
index f124c66..256af83 100644
--- a/src/engine/library/notification.hpp
+++ b/src/engine/library/notification.hpp
@@ -20,190 +20,95 @@
 
 #pragma once
 
-#include <boost/variant.hpp>
+#include <memory>
 
-#include "engine/library/clienttypes.hpp"
-#include "engine/db/libfolder.hpp"
 #include "engine/db/libfile.hpp"
-#include "engine/db/libmetadata.hpp"
-#include "engine/db/keyword.hpp"
-#include "engine/db/label.hpp"
+#include "engine/db/librarytypes.hpp"
+#include "engine/library/clienttypes.hpp"
 
 namespace eng {
-
-class LibNotification
-{
-public:
-    enum class Type {
-        NONE = 0,
-        NEW_LIBRARY_CREATED,
-        ADDED_FOLDERS,
-        ADDED_FILES,
-        ADDED_KEYWORDS,
-        ADDED_KEYWORD,
-        ADDED_LABELS,
-        FOLDER_CONTENT_QUERIED,
-        KEYWORD_CONTENT_QUERIED,
-        METADATA_QUERIED,
-        METADATA_CHANGED,
-        LABEL_CHANGED,
-        LABEL_DELETED,
-        XMP_NEEDS_UPDATE,
-        FOLDER_COUNTED,
-        FOLDER_COUNT_CHANGE,
-        FILE_MOVED
-    };
-
-    struct None {
-    };
-
-    struct Id {
-        library_id_t id;
-    };
-
-    struct AddedFolders {
-        LibFolderListPtr folders;
-    };
-
-    struct AddedKeyword {
-        KeywordPtr keyword;
-    };
-
-    struct AddedKeywords {
-        KeywordListPtr keywords;
-    };
-
-    struct AddedLabels {
-        Label::ListPtr labels;
-    };
-
-    struct FileMoved {
-        library_id_t file;
-        library_id_t from;
-        library_id_t to;
-    };
-
-    struct QueriedContent {
-        library_id_t container;
-        LibFileListPtr files;
-    };
-
-    struct MetadataContent {
-        library_id_t file;
-        LibMetadata::Ptr metadata;
-    };
-
-    typedef metadata_desc_t MetadataChange;
-
-    struct FolderCount {
-        library_id_t folder;
-        int32_t count;
-    };
-
-    struct LabelChange {
-        eng::Label::Ptr label;
-    };
-
-    // specialise this class template to map the notification parameter type
-    // with the notification type.
-    // By default type is None.
-    template<Type t> struct ParamType {
-        typedef None Type;
-    };
-
-    typedef boost::variant<None, Id, AddedFolders,
-                           AddedLabels, AddedKeyword, AddedKeywords,
-                           QueriedContent,
-                           MetadataContent, MetadataChange,
-                           LabelChange,
-                           FileMoved, FolderCount> Param;
-
-    Type type;
-
-    // Instanciate a notification. Will use the ParamType template to enforce the
-    // parameter type.
-    template<LibNotification::Type t>
-    static LibNotification make(typename LibNotification::ParamType<t>::Type&& p)
-        {
-            return LibNotification(t, p);
-        }
-    // Extract the parameter.
-    template<LibNotification::Type t>
-    const typename LibNotification::ParamType<t>::Type& get() const
-        {
-            return boost::get<typename LibNotification::ParamType<t>::Type>(param);
-        }
-private:
-    Param param;
-
-    LibNotification(Type t, Param&& p)
-        : type(t), param(p) {}
+class Label;
+class LibMetadata;
+class LibFolder;
+class Keyword;
+
+class LibNotification;
+typedef std::shared_ptr<LibNotification> LibNotificationPtr;
+
+enum class LibNotificationType {
+    NONE = 0,
+    NEW_LIBRARY_CREATED,
+    ADDED_FOLDER,
+    ADDED_FILE,
+    ADDED_FILES,
+    ADDED_KEYWORD,
+    ADDED_LABEL,
+    FOLDER_CONTENT_QUERIED,
+    KEYWORD_CONTENT_QUERIED,
+    METADATA_QUERIED,
+    METADATA_CHANGED,
+    LABEL_CHANGED,
+    LABEL_DELETED,
+    XMP_NEEDS_UPDATE,
+    FOLDER_COUNTED,
+    FOLDER_COUNT_CHANGE,
+    FILE_MOVED
 };
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::ADDED_FOLDERS> {
-    typedef AddedFolders Type;
+struct LnFileMove {
+    library_id_t file;
+    library_id_t from;
+    library_id_t to;
 };
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::ADDED_KEYWORDS> {
-    typedef AddedKeywords Type;
+struct LnFolderCount {
+    library_id_t folder;
+    int64_t count;
 };
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::ADDED_KEYWORD> {
-    typedef AddedKeyword Type;
-};
+struct QueriedContent {
+    library_id_t container;
+    LibFileListPtr files;
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::ADDED_LABELS> {
-    typedef AddedLabels Type;
+    QueriedContent(library_id_t container);
+    void push(LibFile*);
 };
+}
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::FOLDER_CONTENT_QUERIED> {
-    typedef QueriedContent Type;
-};
+extern "C" {
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::KEYWORD_CONTENT_QUERIED> {
-    typedef QueriedContent Type;
-};
+eng::LibNotificationType
+engine_library_notification_type(const eng::LibNotification* n);
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::METADATA_QUERIED> {
-    typedef MetadataContent Type;
-};
+// if METADATA_CHANGE return the inner id. otherwise directly attached id.
+eng::library_id_t
+engine_library_notification_get_id(const eng::LibNotification* n);
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::METADATA_CHANGED> {
-    typedef MetadataChange Type;
-};
+const eng::Label*
+engine_library_notification_get_label(const eng::LibNotification* n);
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::LABEL_CHANGED> {
-    typedef LabelChange Type;
-};
+const eng::LnFileMove*
+engine_library_notification_get_filemoved(const eng::LibNotification* n);
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::LABEL_DELETED> {
-    typedef Id Type;
-};
+const eng::LibMetadata*
+engine_library_notification_get_libmetadata(const eng::LibNotification* n);
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::FOLDER_COUNTED> {
-    typedef FolderCount Type;
-};
+const eng::LnFolderCount*
+engine_library_notification_get_folder_count(const eng::LibNotification* n);
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::FOLDER_COUNT_CHANGE> {
-    typedef FolderCount Type;
-};
+const eng::metadata_desc_t*
+engine_library_notification_get_metadatachange(const eng::LibNotification* n);
 
-template<>
-struct LibNotification::ParamType<LibNotification::Type::FILE_MOVED> {
-    typedef FileMoved Type;
-};
+const eng::LibFolder*
+engine_library_notification_get_libfolder(const eng::LibNotification* n);
+
+const eng::Keyword*
+engine_library_notification_get_keyword(const eng::LibNotification* n);
+
+const eng::QueriedContent*
+engine_library_notification_get_content(const eng::LibNotification* n);
 
+void engine_library_notification_delete(eng::LibNotification* n);
 
+void engine_library_notify(uint64_t notify_id, eng::LibNotification* n);
 }
diff --git a/src/engine/library/notification.rs b/src/engine/library/notification.rs
new file mode 100644
index 0000000..f211497
--- /dev/null
+++ b/src/engine/library/notification.rs
@@ -0,0 +1,203 @@
+/*
+ * niepce - engine/library/notification.rs
+ *
+ * Copyright (C) 2017 Hubert Figuière
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+use libc::c_void;
+use engine::db::LibraryId;
+use engine::db::label::Label;
+use engine::db::libfolder::LibFolder;
+use engine::db::libmetadata::LibMetadata;
+use engine::db::keyword::Keyword;
+
+use root::eng::LibNotificationType as NotificationType;
+pub use root::eng::LnFileMove as FileMove;
+pub use root::eng::LnFolderCount as FolderCount;
+pub use root::eng::QueriedContent as Content;
+pub use root::eng::metadata_desc_t as MetadataChange;
+
+pub enum Notification {
+    AddedFile,
+    AddedFiles,
+    AddedFolder(LibFolder),
+    AddedKeyword(Keyword),
+    AddedLabel(Label),
+    FileMoved(FileMove),
+    FolderContentQueried(Content),
+    FolderCounted(FolderCount),
+    FolderCountChanged(FolderCount),
+    KeywordContentQueried(Content),
+    LabelChanged(Label),
+    LabelDeleted(LibraryId),
+    LibCreated,
+    MetadataChanged(MetadataChange),
+    MetadataQueried(LibMetadata),
+    XmpNeedsUpdate,
+}
+
+#[cfg(not(test))]
+extern "C" {
+    // actually a *mut Notification
+    pub fn engine_library_notify(notif_id: u64, n: *mut c_void);
+}
+
+#[cfg(test)]
+#[no_mangle]
+pub fn engine_library_notify(notif_id: u64, n: *mut c_void) {
+    // stub for tests
+}
+
+#[no_mangle]
+pub fn engine_library_notification_delete(n: *mut Notification) {
+    unsafe { Box::from_raw(n); }
+}
+
+
+#[no_mangle]
+pub fn engine_library_notification_type(n: &Notification) -> NotificationType {
+    match *n {
+        Notification::AddedFile => NotificationType::ADDED_FILE,
+        Notification::AddedFiles => NotificationType::ADDED_FILES,
+        Notification::AddedFolder(_) => NotificationType::ADDED_FOLDER,
+        Notification::AddedKeyword(_) => NotificationType::ADDED_KEYWORD,
+        Notification::AddedLabel(_) => NotificationType::ADDED_LABEL,
+        Notification::FileMoved(_) => NotificationType::FILE_MOVED,
+        Notification::FolderContentQueried(_) => NotificationType::FOLDER_CONTENT_QUERIED,
+        Notification::FolderCounted(_) => NotificationType::FOLDER_COUNTED,
+        Notification::FolderCountChanged(_) => NotificationType::FOLDER_COUNT_CHANGE,
+        Notification::KeywordContentQueried(_) => NotificationType::KEYWORD_CONTENT_QUERIED,
+        Notification::LabelChanged(_) => NotificationType::LABEL_CHANGED,
+        Notification::LabelDeleted(_) => NotificationType::LABEL_DELETED,
+        Notification::LibCreated => NotificationType::NEW_LIBRARY_CREATED,
+        Notification::MetadataChanged(_) => NotificationType::METADATA_CHANGED,
+        Notification::MetadataQueried(_) => NotificationType::METADATA_QUERIED,
+        Notification::XmpNeedsUpdate => NotificationType::XMP_NEEDS_UPDATE,
+    }
+}
+
+#[no_mangle]
+pub fn engine_library_notification_get_id(n: &Notification) -> LibraryId {
+    match *n {
+        Notification::MetadataChanged(ref changed) => {
+            changed.id
+        },
+        Notification::LabelDeleted(id) => {
+            id
+        },
+        _ => {
+            unreachable!()
+        }
+    }
+}
+
+#[no_mangle]
+pub fn engine_library_notification_get_label(n: &Notification) -> *const Label {
+    match *n {
+        Notification::AddedLabel(ref l) => {
+            l
+        },
+        _ => {
+            unreachable!()
+        }
+    }
+}
+
+#[no_mangle]
+pub fn engine_library_notification_get_filemoved(n: &Notification) -> *const FileMove {
+    match *n {
+        Notification::FileMoved(ref m) => {
+            m
+        },
+        _ => {
+            unreachable!()
+        }
+    }
+}
+
+#[no_mangle]
+pub fn engine_library_notification_get_libmetadata(n: &Notification) -> *const LibMetadata {
+    match *n {
+        Notification::MetadataQueried(ref m) => {
+            m
+        },
+        _ => {
+            unreachable!()
+        }
+    }
+}
+
+#[no_mangle]
+pub fn engine_library_notification_get_folder_count(n: &Notification) -> *const FolderCount {
+    match *n {
+        Notification::FolderCountChanged(ref c) |
+        Notification::FolderCounted(ref c) => {
+            c
+        },
+        _ => {
+            unreachable!()
+        }
+    }
+}
+
+#[no_mangle]
+pub fn engine_library_notification_get_metadatachange(n: &Notification) -> *const MetadataChange {
+    match *n {
+        Notification::MetadataChanged(ref c) => {
+            c
+        },
+        _ => {
+            unreachable!()
+        }
+    }
+}
+
+#[no_mangle]
+pub fn engine_library_notification_get_libfolder(n: &Notification) -> *const LibFolder {
+    match *n {
+        Notification::AddedFolder(ref f) => {
+            f
+        },
+        _ => {
+            unreachable!()
+        }
+    }
+}
+
+#[no_mangle]
+pub fn engine_library_notification_get_keyword(n: &Notification) -> *const Keyword {
+    match *n {
+        Notification::AddedKeyword(ref f) => {
+            f
+        },
+        _ => {
+            unreachable!()
+        }
+    }
+}
+
+#[no_mangle]
+pub fn engine_library_notification_get_content(n: &Notification) -> *const Content {
+    match *n {
+        Notification::FolderContentQueried(ref c) |
+        Notification::KeywordContentQueried(ref c) => {
+            c
+        },
+        _ => {
+            unreachable!()
+        }
+    }
+}
diff --git a/src/engine/library/op.cpp b/src/engine/library/op.cpp
index d3afaa1..578afe6 100644
--- a/src/engine/library/op.cpp
+++ b/src/engine/library/op.cpp
@@ -23,13 +23,13 @@
 
 namespace eng {
 
-Op::Op(tid_t _id, const function_t & func)
+Op::Op(tid_t _id, const Function & func)
     : m_id(_id),
       m_function(func)
 {
 }
 
-void Op::operator() (const Library::Ptr &l)
+void Op::operator() (Library* l)
 {
     if(m_function) {
         m_function(l);
diff --git a/src/engine/library/op.hpp b/src/engine/library/op.hpp
index 6767422..e66b4ec 100644
--- a/src/engine/library/op.hpp
+++ b/src/engine/library/op.hpp
@@ -34,20 +34,20 @@ class Op
 {
 public:
     typedef std::unique_ptr< Op > Ptr;
-    typedef std::function<void (const Library::Ptr &)> function_t;
+    typedef std::function<bool (Library*)> Function;
 
-    Op(tid_t id, const function_t & func);
+    Op(tid_t id, const Function & func);
 
     tid_t id() const
         { return m_id; }
 
-    void operator() (const Library::Ptr &);
-    const function_t & fn() const
+    void operator() (Library*);
+    const Function & fn() const
         { return m_function; }
 protected:
 private:
     tid_t   m_id;
-    function_t m_function;
+    Function m_function;
 };
 
 }
diff --git a/src/engine/library/test_opqueue.cpp b/src/engine/library/test_opqueue.cpp
index 8bc7e1e..f0010d6 100644
--- a/src/engine/library/test_opqueue.cpp
+++ b/src/engine/library/test_opqueue.cpp
@@ -30,7 +30,7 @@ int test_main(int, char *[])
 {
        OpQueue q;
 
-       Op::Ptr p(new Op(1, [](const Library::Ptr &){}));
+       Op::Ptr p(new Op(1, [](Library*){ return false; }));
 
        BOOST_CHECK(q.empty());
 
diff --git a/src/engine/library/thumbnailcache.cpp b/src/engine/library/thumbnailcache.cpp
index 0309be7..cdddd1e 100644
--- a/src/engine/library/thumbnailcache.cpp
+++ b/src/engine/library/thumbnailcache.cpp
@@ -44,10 +44,10 @@ ThumbnailCache::~ThumbnailCache()
 {
 }
 
-void ThumbnailCache::request(const LibFileListPtr & fl)
+void ThumbnailCache::request(const LibFileList& fl)
 {
     clear();
-    std::for_each(fl->begin(), fl->end(),
+    std::for_each(fl.begin(), fl.end(),
                   [this] (const auto& f) {
                       ThumbnailTask::Ptr task(new ThumbnailTask(f, 160, 160));
                       this->schedule(task);
diff --git a/src/engine/library/thumbnailcache.hpp b/src/engine/library/thumbnailcache.hpp
index ab73e84..14a2e85 100644
--- a/src/engine/library/thumbnailcache.hpp
+++ b/src/engine/library/thumbnailcache.hpp
@@ -58,7 +58,7 @@ public:
     ThumbnailCache(const std::string & dir, uint64_t notif_id);
     ~ThumbnailCache();
 
-    void request(const LibFileListPtr & fl);
+    void request(const LibFileList& fl);
 
     static bool is_thumbnail_cached(const std::string & file, const std::string & thumb);
 
diff --git a/src/engine/mod.rs b/src/engine/mod.rs
index 8f653fc..dffa614 100644
--- a/src/engine/mod.rs
+++ b/src/engine/mod.rs
@@ -18,4 +18,4 @@
  */
 
 pub mod db;
-
+pub mod library;
diff --git a/src/fwk/base/colour.cpp b/src/fwk/base/colour.cpp
index ba2e6cf..f91d846 100644
--- a/src/fwk/base/colour.cpp
+++ b/src/fwk/base/colour.cpp
@@ -17,53 +17,48 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
-#include <vector>
-
-#include <boost/algorithm/string/classification.hpp>
-#include <boost/algorithm/string/split.hpp>
-#include <boost/format.hpp>
-#include <boost/lexical_cast.hpp>
-
 #include "fwk/base/colour.hpp"
+#include "fwk/base/rust.hpp"
 
-namespace fwk {
-
+// Rust glue.
 
-  RgbColour::RgbColour(value_type r, value_type g, value_type b)
-  {
-    at(0) = r;
-    at(1) = g;
-    at(2) = b;
-  }
+extern "C" {
 
+fwk::RgbColour* fwk_rgbcolour_new(uint16_t r, uint16_t g, uint16_t b);
+fwk::RgbColour* fwk_rgbcolour_clone(const fwk::RgbColour*);
+void fwk_rgbcolour_delete(fwk::RgbColour*);
+}
 
-  RgbColour::RgbColour(const std::string & s)
-  {
-    std::vector<std::string> components;
-    boost::split(components, s, boost::is_any_of(" "));
-    if(components.size() >= 3) {
-      try {
-        for(int i = 0; i < 3; ++i) {
-          at(i) = boost::lexical_cast<value_type>(components[i]);
-        }
-        return;
-      }
-      catch(...) {
+namespace fwk {
 
-      }
-    }
-    // fallback in case of failure
-    at(0) = 0;
-    at(1) = 0;
-    at(2) = 0;
-  }
+RgbColourPtr rgbcolour_new(uint16_t r, uint16_t g, uint16_t b)
+{
+    return rgbcolour_wrap(fwk_rgbcolour_new(r, g, b));
+}
 
+RgbColourPtr rgbcolour_clone(const RgbColour* c)
+{
+    return rgbcolour_wrap(fwk_rgbcolour_clone(c));
+}
 
-  std::string RgbColour::to_string() const
-  {
-    return str(boost::format("%1% %2% %3%") % at(0) % at(1) % at(2));
-  }
+RgbColourPtr rgbcolour_wrap(RgbColour* c)
+{
+    return RgbColourPtr(c, &fwk_rgbcolour_delete);
+}
 
+std::string rgbcolour_to_string(uint16_t r, uint16_t g, uint16_t b)
+{
+    RgbColour* colour = fwk_rgbcolour_new(r, g, b);
+    std::string s = rgbcolour_to_string(colour);
+    fwk_rgbcolour_delete(colour);
+    return s;
+}
 
+std::string rgbcolour_to_string(const RgbColour* c)
+{
+    char* p = fwk_rgbcolour_to_string(c);
+    std::string s(p);
+    rust_cstring_delete(p);
+    return s;
+}
 }
diff --git a/src/fwk/base/colour.hpp b/src/fwk/base/colour.hpp
index ecb6fed..e758969 100644
--- a/src/fwk/base/colour.hpp
+++ b/src/fwk/base/colour.hpp
@@ -1,7 +1,7 @@
 /*
  * niepce - fwk/base/colour.hpp
  *
- * Copyright (C) 2009-2013 Hubert Figuiere
+ * Copyright (C) 2009-2017 Hubert Figuiere
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,32 +17,25 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#pragma once
 
-
-#ifndef __FWK_BASE_COLOUR_HPP_
-#define __FWK_BASE_COLOUR_HPP_
-
-#include <stdint.h>
-
+#include <memory>
 #include <string>
-#include <array>
 
 namespace fwk {
 
-  /** A RgbColour tuple (3 components, 16bpp)
-   *  To be used only for UI.
-   */
-  class RgbColour
-    : public std::array<uint16_t, 3>
-  {
-  public:
-    RgbColour(value_type r = 0, value_type g = 0, value_type b = 0);
-    explicit RgbColour(const std::string & );
+class RgbColour;
+typedef std::shared_ptr<RgbColour> RgbColourPtr;
 
-    std::string to_string() const;
-  };
+RgbColourPtr rgbcolour_new(uint16_t r, uint16_t g, uint16_t b);
+RgbColourPtr rgbcolour_clone(const RgbColour*);
+RgbColourPtr rgbcolour_wrap(RgbColour*);
 
+std::string rgbcolour_to_string(uint16_t r, uint16_t g, uint16_t b);
+std::string rgbcolour_to_string(const RgbColour*);
 }
 
-
-#endif
+extern "C" {
+uint16_t fwk_rgbcolour_component(const fwk::RgbColour*, int32_t idx);
+char* fwk_rgbcolour_to_string(const fwk::RgbColour*);
+}
diff --git a/src/fwk/base/date.cpp b/src/fwk/base/date.cpp
index 2cc2d20..a481561 100644
--- a/src/fwk/base/date.cpp
+++ b/src/fwk/base/date.cpp
@@ -22,9 +22,34 @@
 
 #include "date.hpp"
 #include "debug.hpp"
+#include "rust.hpp"
+
+// rust glue
+extern "C" {
+void fwk_date_delete(fwk::Date*);
+char* fwk_date_to_string(const fwk::Date*);
+}
 
 namespace fwk {
 
+DatePtr date_wrap(fwk::Date* date)
+{
+    return DatePtr(date, fwk_date_delete);
+}
+
+std::string date_to_string(const Date* d)
+{
+    DBG_ASSERT(d, "d is nullptr");
+    if (!d) {
+        return "";
+    }
+    char* p = fwk_date_to_string(d);
+    std::string s(p);
+    rust_cstring_delete(p);
+    return s;
+}
+
+#if 0
 time_t make_time_value(const Date & d)
 {
     time_t date = 0;
@@ -46,6 +71,7 @@ time_t make_time_value(const Date & d)
 
     return date;
 }
+#endif
 
 bool make_xmp_date_time(time_t t, XmpDateTime& xmp_dt)
 {
@@ -71,6 +97,7 @@ bool make_xmp_date_time(time_t t, XmpDateTime& xmp_dt)
     return true;
 }
 
+#if 0
 Date::Date(const XmpDateTime& dt, const Timezone* tz)
     : m_datetime(dt)
     , m_tz(tz)
@@ -96,7 +123,7 @@ std::string Date::to_string() const
 
     return buffer;
 }
-
+#endif
 }
 
 /*
diff --git a/src/fwk/base/date.hpp b/src/fwk/base/date.hpp
index 89481b3..4d41668 100644
--- a/src/fwk/base/date.hpp
+++ b/src/fwk/base/date.hpp
@@ -1,7 +1,7 @@
 /*
  * niepce - fwk/base/date.hpp
  *
- * Copyright (C) 2012-2013 Hubert Figuiere
+ * Copyright (C) 2012-2017 Hubert Figuiere
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,9 +17,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef __FWK_BASE_DATE_HPP
-#define __FWK_BASE_DATE_HPP
+#pragma once
 
+#include <memory>
 #include <string>
 
 #include <exempi/xmp.h>
@@ -34,28 +34,11 @@ class Timezone;
  */
 bool make_xmp_date_time(time_t t, XmpDateTime& xmp_dt);
 
-/**
- * Class to deal with ISO8601 string dates as used by XMP.
- * Bonus: with a timezone.
- */
-class Date
-{
-public:
-    Date(const XmpDateTime& dt, const Timezone* tz = nullptr);
-    Date(const time_t dt, const Timezone* tz = nullptr);
-
-    std::string to_string() const;
-    const XmpDateTime& xmp_date() const
-        { return m_datetime; }
-
-private:
-    XmpDateTime m_datetime;
-    // weak pointer.
-    const Timezone* m_tz;
-};
-
-time_t make_time_value(const Date &);
+class Date;
 
+typedef std::shared_ptr<Date> DatePtr;
+DatePtr date_wrap(Date*);
+std::string date_to_string(const Date*);
 }
 
 /*
@@ -67,4 +50,3 @@ time_t make_time_value(const Date &);
   fill-column:99
   End:
 */
-#endif
diff --git a/src/fwk/base/date.rs b/src/fwk/base/date.rs
index 5d45555..e252332 100644
--- a/src/fwk/base/date.rs
+++ b/src/fwk/base/date.rs
@@ -19,6 +19,7 @@
 
 use exempi;
 use libc;
+use std::ffi::CString;
 use std::ptr;
 use std::time::{
     SystemTime,
@@ -129,6 +130,16 @@ pub fn make_xmp_date_time(t: Time, xmp_dt: &mut exempi::DateTime) -> bool {
     return true;
 }
 
+#[no_mangle]
+pub fn fwk_date_delete(date: *mut Date) {
+    unsafe { Box::from_raw(date); }
+}
+
+#[no_mangle]
+pub fn fwk_date_to_string(date: &Date) -> *mut libc::c_char {
+    CString::new(date.to_string().as_bytes()).unwrap().into_raw()
+}
+
 #[cfg(test)]
 mod test {
     use super::Date;
diff --git a/src/fwk/base/propertybag.cpp b/src/fwk/base/propertybag.cpp
index 64b6072..36b3ae5 100644
--- a/src/fwk/base/propertybag.cpp
+++ b/src/fwk/base/propertybag.cpp
@@ -38,16 +38,52 @@ bool is_string(const PropertyValue & v)
     return v.type() == typeid(std::string);
 }
 
+bool is_string_array(const PropertyValue & v)
+{
+    return v.type() == typeid(StringArray);
+}
+
+bool is_date(const PropertyValue & v)
+{
+    return v.type() == typeid(DatePtr);
+}
+
 int get_integer(const PropertyValue & v)
 {
     return is_empty(v) ? 0 : boost::get<int>(v);
 }
 
+const Date* get_date(const PropertyValue & v)
+{
+    return boost::get<DatePtr>(v).get();
+}
 const std::string & get_string(const PropertyValue & v)
 {
     return boost::get<std::string>(v);
 }
 
+const char* get_string_cstr(const PropertyValue & v)
+{
+    return boost::get<std::string>(v).c_str();
+}
+
+// Rust glue
+const fwk::StringArray & get_string_array(const PropertyValue & v)
+{
+    return boost::get<fwk::StringArray>(v);
+}
+
+size_t string_array_len(const fwk::StringArray &v)
+{
+    return v.size();
+}
+
+const char* string_array_at_cstr(const fwk::StringArray &v, size_t i)
+{
+    return v[i].c_str();
+}
+// end
+
 bool PropertyBag::set_value_for_property(PropertyIndex idx, const PropertyValue & value)
 {
     bool removed = (m_bag.erase(idx) == 1);
diff --git a/src/fwk/base/propertybag.hpp b/src/fwk/base/propertybag.hpp
index 111689a..8f56cf1 100644
--- a/src/fwk/base/propertybag.hpp
+++ b/src/fwk/base/propertybag.hpp
@@ -38,7 +38,39 @@ typedef uint32_t PropertyIndex;
 typedef boost::blank EmptyValue;
 typedef std::vector<std::string> StringArray;
 /** EmptyValue will be the default type */
-typedef boost::variant<EmptyValue, int, std::string, StringArray, Date> PropertyValue;
+class PropertyValue
+    : private boost::variant<EmptyValue, int, std::string, StringArray, DatePtr>
+{
+public:
+#if !RUST_BINDGEN
+    typedef boost::variant<EmptyValue, int, std::string, StringArray, DatePtr> _inner;
+
+    PropertyValue()
+        : _inner()
+        {
+        }
+    template<class T>
+    PropertyValue(const T& data)
+        : _inner(data)
+        {
+        }
+
+    _inner& get_variant()
+        { return *this; }
+    const _inner& get_variant() const
+        { return *this; }
+#endif
+    friend bool is_empty(const PropertyValue & v);
+    friend bool is_integer(const PropertyValue & v);
+    friend bool is_string(const PropertyValue & v);
+    friend bool is_string_array(const PropertyValue & v);
+    friend bool is_date(const PropertyValue & v);
+    friend int get_integer(const PropertyValue & v);
+    friend const Date* get_date(const PropertyValue & v);
+    friend const std::string & get_string(const PropertyValue & v);
+    friend const char* get_string_cstr(const PropertyValue & v);
+    friend const fwk::StringArray & get_string_array(const PropertyValue & v);
+};
 
 typedef std::set<PropertyIndex> PropertySet;
 
@@ -47,10 +79,19 @@ bool is_empty(const PropertyValue & v);
 /** Return if it is an integer */
 bool is_integer(const PropertyValue & v);
 bool is_string(const PropertyValue & v);
+bool is_string_array(const PropertyValue & v);
+bool is_date(const PropertyValue & v);
+const Date* get_date(const PropertyValue & v);
 /** Return the integer value (or 0 if empty) */
 int get_integer(const PropertyValue & v);
 /** Return the string value */
 const std::string & get_string(const PropertyValue & v);
+const char* get_string_cstr(const PropertyValue & v);
+
+// Rust glue
+const fwk::StringArray & get_string_array(const PropertyValue & v);
+size_t string_array_len(const fwk::StringArray &);
+const char* string_array_at_cstr(const fwk::StringArray &, size_t);
 
 /** a property bag
  * It is important that the values for PropertyIndex be properly name spaced
diff --git a/src/fwk/base/rgbcolour.rs b/src/fwk/base/rgbcolour.rs
index 0812dce..56aed43 100644
--- a/src/fwk/base/rgbcolour.rs
+++ b/src/fwk/base/rgbcolour.rs
@@ -1,14 +1,33 @@
+/*
+ * niepce - fwk/base/rgbcolour.rs
+ *
+ * Copyright (C) 2017 Hubert Figuière
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
 
-
+use libc::c_char;
+use std::ffi::CString;
 use std::num::ParseIntError;
 use std::str::FromStr;
 
 #[repr(C)]
 #[derive(Clone,Default)]
 pub struct RgbColour {
-    r: u16,
-    g: u16,
-    b: u16
+    pub r: u16,
+    pub g: u16,
+    pub b: u16
 }
 
 #[derive(Debug)]
@@ -22,7 +41,7 @@ pub enum ColourParseError {
 }
 
 impl From<ParseIntError> for ColourParseError {
-    fn from(e: ParseIntError) -> ColourParseError {
+    fn from(_: ParseIntError) -> ColourParseError {
         ColourParseError::ParseIntError
     }
 }
@@ -49,3 +68,39 @@ impl FromStr for RgbColour {
         Ok(RgbColour::new(r, g, b))
     }
 }
+
+impl ToString for RgbColour {
+    fn to_string(&self) -> String {
+        format!("{} {} {}", self.r, self.g, self.b)
+    }
+}
+
+#[no_mangle]
+pub fn fwk_rgbcolour_to_string(c: &RgbColour) -> *mut c_char {
+    CString::new(c.to_string().as_bytes()).unwrap().into_raw()
+}
+
+#[no_mangle]
+pub fn fwk_rgbcolour_delete(c: *mut RgbColour) {
+    unsafe { Box::from_raw(c); }
+}
+
+#[no_mangle]
+pub fn fwk_rgbcolour_component(c: &RgbColour, idx: i32) -> u16 {
+    match idx {
+        0 => c.r,
+        1 => c.g,
+        2 => c.b,
+        _ => unreachable!()
+    }
+}
+
+#[no_mangle]
+pub fn fwk_rgbcolour_new(r: u16, g: u16, b: u16) -> *mut RgbColour {
+    Box::into_raw(Box::new(RgbColour::new(r, g, b)))
+}
+
+#[no_mangle]
+pub fn fwk_rgbcolour_clone(c: &RgbColour) -> *mut RgbColour {
+    Box::into_raw(Box::new(c.clone()))
+}
diff --git a/src/fwk/base/rust.hpp b/src/fwk/base/rust.hpp
new file mode 100644
index 0000000..6beb923
--- /dev/null
+++ b/src/fwk/base/rust.hpp
@@ -0,0 +1,12 @@
+
+
+#pragma once
+
+#include <memory>
+
+namespace rust {
+
+
+}
+
+extern "C" void rust_cstring_delete(char*);
diff --git a/src/fwk/base/t/testpropertybag.cpp b/src/fwk/base/t/testpropertybag.cpp
index c653c11..a516de7 100644
--- a/src/fwk/base/t/testpropertybag.cpp
+++ b/src/fwk/base/t/testpropertybag.cpp
@@ -27,7 +27,7 @@ int test_main( int, char *[] )             // note the name!
 {
        fwk::PropertyValue v;
 
-       BOOST_CHECK(v.type() == typeid(fwk::EmptyValue));
+       BOOST_CHECK(v.get_variant().type() == typeid(fwk::EmptyValue));
 
        BOOST_CHECK(is_empty(v));
        BOOST_CHECK(get_integer(v) == 0);
diff --git a/src/fwk/toolkit/gdkutils.cpp b/src/fwk/toolkit/gdkutils.cpp
index 67f06d4..780e55b 100644
--- a/src/fwk/toolkit/gdkutils.cpp
+++ b/src/fwk/toolkit/gdkutils.cpp
@@ -83,15 +83,16 @@ namespace fwk {
   Gdk::RGBA rgbcolour_to_gdkcolor(const fwk::RgbColour & colour)
   {
     Gdk::RGBA gdkcolour;
-    gdkcolour.set_rgba_u(colour[0], colour[1], colour[2]);
+    gdkcolour.set_rgba_u(fwk_rgbcolour_component(&colour, 0),
+                         fwk_rgbcolour_component(&colour, 1),
+                         fwk_rgbcolour_component(&colour, 2));
     return gdkcolour;
   }
 
 
-  fwk::RgbColour gdkcolor_to_rgbcolour(const Gdk::RGBA & colour)
+  fwk::RgbColourPtr gdkcolor_to_rgbcolour(const Gdk::RGBA & colour)
   {
-    fwk::RgbColour rgbcolour(colour.get_red_u(), colour.get_green_u(), colour.get_blue_u());
-    return rgbcolour;
+    return fwk::rgbcolour_new(colour.get_red_u(), colour.get_green_u(), colour.get_blue_u());
   }
 
 
diff --git a/src/fwk/toolkit/gdkutils.hpp b/src/fwk/toolkit/gdkutils.hpp
index 092ca85..a1b1f62 100644
--- a/src/fwk/toolkit/gdkutils.hpp
+++ b/src/fwk/toolkit/gdkutils.hpp
@@ -38,7 +38,7 @@ namespace fwk {
                                                                                                        int 
exif_orientation);
 
   Gdk::RGBA rgbcolour_to_gdkcolor(const fwk::RgbColour & colour);
-  fwk::RgbColour gdkcolor_to_rgbcolour(const Gdk::RGBA & colour);
+  fwk::RgbColourPtr gdkcolor_to_rgbcolour(const Gdk::RGBA & colour);
 }
 
 #endif
diff --git a/src/fwk/toolkit/metadatawidget.cpp b/src/fwk/toolkit/metadatawidget.cpp
index ff2fe48..d4db88b 100644
--- a/src/fwk/toolkit/metadatawidget.cpp
+++ b/src/fwk/toolkit/metadatawidget.cpp
@@ -325,7 +325,7 @@ bool MetaDataWidget::set_string_array_data(Gtk::Widget* w, const PropertyValue &
 {
     try {
         AutoFlag flag(m_update);
-        fwk::StringArray tokens = boost::get<fwk::StringArray>(value);
+        fwk::StringArray tokens = boost::get<fwk::StringArray>(value.get_variant());
 
         static_cast<fwk::TokenTextView*>(w)->set_tokens(tokens);
     }
@@ -383,10 +383,10 @@ bool MetaDataWidget::set_date_data(Gtk::Widget* w, const PropertyValue & value)
 {
     try {
         AutoFlag flag(m_update);
-        fwk::Date date = boost::get<fwk::Date>(value);
-        static_cast<Gtk::Label*>(w)->set_text(date.to_string());
+        fwk::DatePtr date = boost::get<fwk::DatePtr>(value.get_variant());
+        static_cast<Gtk::Label*>(w)->set_text(fwk::date_to_string(date.get()));
 
-        DBG_OUT("setting date data %s", date.to_string().c_str());
+        DBG_OUT("setting date data %s", fwk::date_to_string(date.get()).c_str());
     }
     catch(...) {
         return false;
@@ -452,7 +452,7 @@ bool MetaDataWidget::on_text_changed(GdkEventFocus*,
     if(m_update) {
         return true;
     }
-    emit_metadata_changed(prop, 
+    emit_metadata_changed(prop,
                           fwk::PropertyValue(b->get_text()));
     return true;
 }
diff --git a/src/fwk/utils/Makefile.am b/src/fwk/utils/Makefile.am
index 6e38035..b88dc9f 100644
--- a/src/fwk/utils/Makefile.am
+++ b/src/fwk/utils/Makefile.am
@@ -1,18 +1,16 @@
 
-DIST_SUBDIRS = db
-
 AM_CPPFLAGS = -I$(top_srcdir)/src/ \
        @FRAMEWORK_CFLAGS@
 
 TESTS = testfiles testxmp \
        testpathutils \
-       teststringutils test_db test_db2 test_db3 test_db4 testufrawmeta
+       teststringutils testufrawmeta
 
 EXTRA_DIST = test.xmp test2.ufraw MODULES_HOWTO
 
 check_PROGRAMS = testfiles testxmp \
        testpathutils \
-       teststringutils test_db test_db2 test_db3 test_db4 testufrawmeta
+       teststringutils testufrawmeta
 
 
 testfiles_SOURCES = testfiles.cpp
@@ -39,27 +37,6 @@ testpathutils_SOURCES = t/testpathutils.cpp
 testpathutils_LDADD = libniepceutils.a \
        @FRAMEWORK_LIBS@
 
-
-test_db_SOURCES = db/test_db.cpp
-test_db_LDADD = libniepceutils.a \
-       ../base/libfwkbase.a \
-       @FRAMEWORK_LIBS@
-
-test_db2_SOURCES = db/test_db2.cpp
-test_db2_LDADD = libniepceutils.a \
-       ../base/libfwkbase.a \
-       @FRAMEWORK_LIBS@
-
-test_db3_SOURCES = db/test_db3.cpp
-test_db3_LDADD = libniepceutils.a \
-       ../base/libfwkbase.a \
-       @FRAMEWORK_LIBS@
-
-test_db4_SOURCES = db/test_db4.cpp
-test_db4_LDADD = libniepceutils.a \
-       ../base/libfwkbase.a \
-       @FRAMEWORK_LIBS@
-
 noinst_LIBRARIES = libniepceutils.a
 
 libniepceutils_a_SOURCES = \
@@ -75,11 +52,6 @@ libniepceutils_a_SOURCES = \
        thread.hpp thread.cpp worker.hpp \
        pathutils.hpp pathutils.cpp \
        databinder.hpp databinder.cpp \
-       db/iconnectiondriver.hpp db/iconnectionmanagerdriver.hpp \
-       db/insertstatement.cpp db/insertstatement.hpp \
-       db/sqlstatement.cpp db/sqlstatement.hpp \
-       db/sqlite/sqlitecnxdrv.cpp db/sqlite/sqlitecnxdrv.hpp \
-       db/sqlite/sqlitecnxmgrdrv.cpp db/sqlite/sqlitecnxmgrdrv.hpp \
        dynamicmodule.hpp dynamicmodule.cpp \
        modulefactory.hpp \
        modulemanager.hpp modulemanager.cpp \
diff --git a/src/fwk/utils/exempi.cpp b/src/fwk/utils/exempi.cpp
index 12a808a..8ccd79a 100644
--- a/src/fwk/utils/exempi.cpp
+++ b/src/fwk/utils/exempi.cpp
@@ -17,239 +17,45 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <string.h>
 #include <time.h>
 
 #include <boost/lexical_cast.hpp>
 
-#include <glib.h>
 #include <giomm/file.h>
+#include <glib.h>
 
 #include <exempi/xmp.h>
 #include <exempi/xmpconsts.h>
 
-#include "fwk/base/debug.hpp"
-#include "fwk/base/date.hpp"
 #include "exempi.hpp"
+#include "fwk/base/date.hpp"
+#include "fwk/base/debug.hpp"
 #include "pathutils.hpp"
 
 namespace xmp {
 
-const char * NIEPCE_XMP_NAMESPACE = "http://xmlns.figuiere.net/ns/niepce/1.0";;
-const char * NIEPCE_XMP_NS_PREFIX = "niepce";
-
-const char * UFRAW_INTEROP_NAMESPACE = "http://xmlns.figuiere.net/ns/ufraw_interop/1.0";;
-const char * UFRAW_INTEROP_NS_PREFIX = "ufrint";
-
-}
-
-namespace fwk {
-
-ExempiManager::ExempiManager(const ns_defs_t* namespaces)
-{
-    if(xmp_init()) {
-        xmp_register_namespace(xmp::UFRAW_INTEROP_NAMESPACE,
-                               xmp::UFRAW_INTEROP_NS_PREFIX, nullptr);
-        xmp_register_namespace(xmp::NIEPCE_XMP_NAMESPACE,
-                               xmp::NIEPCE_XMP_NS_PREFIX, nullptr);
-
-        if(namespaces != nullptr) {
-            for(auto iter = namespaces; iter->ns != nullptr; iter++) {
-                // TODO check the return code
-                xmp_register_namespace(iter->ns,
-                                       iter->prefix, nullptr);
-            }
-        }
-    }
-}
-
-
-ExempiManager::~ExempiManager()
-{
-    xmp_terminate();
-}
-
-
-XmpMeta::XmpMeta()
-    : m_xmp(),
-      m_keyword_fetched(false)
-{
-    m_xmp = xmp_new_empty();
-}
-
-/** @param file the path to the file to open
- * @param sidecar_only we only want the sidecar.
- * It will locate the XMP sidecar for the file.
- */
-XmpMeta::XmpMeta(const std::string & file, bool sidecar_only)
-    : m_xmp(nullptr),
-      m_keyword_fetched(false)
-{
-    if(!sidecar_only) {
-        DBG_OUT("trying to load the XMP from the file");
-        xmp::ScopedPtr<XmpFilePtr>
-            xmpfile(xmp_files_open_new(file.c_str(), XMP_OPEN_READ));
-        if(xmpfile != nullptr) {
-            m_xmp = xmp_files_get_new_xmp(xmpfile);
-            if(xmpfile == nullptr) {
-                ERR_OUT("xmpfile is nullptr");
-            }
-        }
-    }
-
-    if(m_xmp == nullptr) {
-        gsize len = 0;
-        char * buffer = nullptr;
-        std::string sidecar = fwk::path_replace_extension(file, ".xmp");
-
-        try {
-            DBG_OUT("creating xmpmeta from %s", sidecar.c_str());
-            Glib::RefPtr<Gio::File> f = Gio::File::create_for_path(sidecar);
-            std::string etag_out;
-            f->load_contents(buffer, len, etag_out);
-        }
-        catch(const Glib::Exception & e) {
-            ERR_OUT("loading XMP failed: %s", e.what().c_str());
-        }
-        if(buffer) {
-            m_xmp = xmp_new_empty();
-            if(!xmp_parse(m_xmp, buffer, len)) {
-                xmp_free(m_xmp);
-                m_xmp = nullptr;
-            }
-            g_free(buffer);
-        }
-    }
-}
-
-XmpMeta::~XmpMeta()
-{
-    if(m_xmp) {
-        xmp_free(m_xmp);
-    }
-}
-
-std::string XmpMeta::serialize_inline() const
-{
-    std::string buf;
-    xmp::ScopedPtr<XmpStringPtr> output(xmp_string_new());
-    if(xmp_serialize_and_format(m_xmp, output,
-                                XMP_SERIAL_OMITPACKETWRAPPER | XMP_SERIAL_OMITALLFORMATTING,
-                                0, "", "", 0)) {
-        buf = xmp_string_cstr(output);
-    }
-    return buf;
-}
-
-std::string XmpMeta::serialize() const
-{
-    std::string buf;
-    xmp::ScopedPtr<XmpStringPtr> output(xmp_string_new());
-    if(xmp_serialize_and_format(m_xmp, output,
-                                XMP_SERIAL_OMITPACKETWRAPPER,
-                                0, "\n", " ", 0)) {
-        buf = xmp_string_cstr(output);
-    }
-    return buf;
-}
-
-void XmpMeta::unserialize(const char * buffer)
-{
-    if(!buffer)
-        return;
-    xmp_parse(m_xmp, buffer, strlen(buffer));
-}
-
-
-int32_t XmpMeta::orientation() const
-{
-    int32_t _orientation = 0;
-    int err = 0;
-    if(!xmp_get_property_int32(m_xmp, NS_TIFF, "Orientation",
-                               &_orientation, nullptr)
-       && ((err = xmp_get_error()) != 0)) {
-        ERR_OUT("get \"Orientation\" property failed: %d", xmp_get_error());
-    }
-    return _orientation;
-}
-
+const char* NIEPCE_XMP_NAMESPACE = "http://xmlns.figuiere.net/ns/niepce/1.0";;
+const char* NIEPCE_XMP_NS_PREFIX = "niepce";
 
-std::string XmpMeta::label() const
-{
-    std::string _label;
-    xmp::ScopedPtr<XmpStringPtr> value(xmp_string_new());
-    if(xmp_get_property(m_xmp, NS_XAP, "Label", value, nullptr)) {
-        _label = xmp_string_cstr(value);
-    }
-    return _label;
+const char* UFRAW_INTEROP_NAMESPACE =
+    "http://xmlns.figuiere.net/ns/ufraw_interop/1.0";;
+const char* UFRAW_INTEROP_NS_PREFIX = "ufrint";
 }
 
+extern "C" {
 
-int32_t XmpMeta::rating() const
-{
-    int32_t _rating = -1;
-    int err = 0;
-    if(!xmp_get_property_int32(m_xmp, NS_XAP, "Rating", &_rating, nullptr)
-       && ((err = xmp_get_error()) != 0)) {
-        ERR_OUT("get \"Rating\" property failed: %d", err);
-    }
-    return _rating;
-}
-
-int32_t XmpMeta::flag() const
-{
-    int32_t _flag = 0;
-    int err = 0;
-    if(!xmp_get_property_int32(m_xmp, xmp::NIEPCE_XMP_NAMESPACE, "Flag",
-                               &_flag, nullptr)
-       && ((err = xmp_get_error()) != 0)) {
-        ERR_OUT("get \"Flag\" property failed: %d", err);
-    }
-    return _flag;
-}
-
-fwk::Date XmpMeta::creation_date() const
-{
-    XmpDateTime value;
-    int err = 0;
-    if(xmp_get_property_date(m_xmp, NS_EXIF, "DateTimeOriginal",
-                             &value, nullptr)) {
-        return fwk::Date(value);
-    }
-    else if((err = xmp_get_error()) != 0) {
-        ERR_OUT("get \"DateTimeOriginal\" property failed: %d", err);
-    }
-    return Date(0);
-}
-
-std::string XmpMeta::creation_date_str() const
-{
-    std::string s;
-    xmp::ScopedPtr<XmpStringPtr> value(xmp_string_new());
-    if(xmp_get_property(m_xmp, NS_EXIF, "DateTimeOriginal",
-                        value, nullptr)) {
-        s = xmp_string_cstr(value);
-    }
-    return s;
+fwk::ExempiManager* fwk_exempi_manager_new();
+void fwk_exempi_manager_delete(fwk::ExempiManager*);
 }
 
+namespace fwk {
 
-const std::vector< std::string > & XmpMeta::keywords() const
+ExempiManagerPtr exempi_manager_new()
 {
-    if(!m_keyword_fetched) {
-        xmp::ScopedPtr<XmpIteratorPtr>
-            iter(xmp_iterator_new(m_xmp, NS_DC, "subject",
-                                  XMP_ITER_JUSTLEAFNODES));
-        xmp::ScopedPtr<XmpStringPtr> value(xmp_string_new());
-        while(xmp_iterator_next(iter, nullptr, nullptr, value, nullptr)) {
-            m_keywords.push_back(xmp_string_cstr(value));
-        }
-        m_keyword_fetched = true;
-    }
-    return m_keywords;
+    return ExempiManagerPtr(fwk_exempi_manager_new(),
+                            &fwk_exempi_manager_delete);
 }
-
 }
 
 /*
diff --git a/src/fwk/utils/exempi.hpp b/src/fwk/utils/exempi.hpp
index 2bd9190..327a7f9 100644
--- a/src/fwk/utils/exempi.hpp
+++ b/src/fwk/utils/exempi.hpp
@@ -87,65 +87,24 @@ namespace fwk {
 
 class Date;
 
-class ExempiManager
-{
-public:
-    NON_COPYABLE(ExempiManager);
-
-    struct ns_defs_t {
-        const char *ns;
-        const char *prefix;
-    };
-    /** construct with namespaces to initialize */
-    ExempiManager(const ns_defs_t * namespaces = 0);
-    ~ExempiManager();
-};
+class XmpMeta;
+class ExempiManager;
+typedef std::shared_ptr<ExempiManager> ExempiManagerPtr;
 
-/** a high-level wrapper for xmp */
-class XmpMeta
-{
-public:
-    NON_COPYABLE(XmpMeta);
-
-    XmpMeta();
-    XmpMeta(const std::string& for_file, bool sidecar_only);
-    virtual ~XmpMeta();
-
-    bool isOk() const
-        { return m_xmp != nullptr; }
-    XmpPtr xmp() const
-        { return m_xmp; }
-    /** serialize the XMP inline */
-    std::string serialize_inline() const;
-    /** serialize the XMP (for the sidecar) */
-    std::string serialize() const;
-    /** load the XMP from the unserialized buffer
-     * (NUL terminated)
-     */
-    void unserialize(const char *);
-
-    int32_t orientation() const;
-    std::string label() const;
-    /** return the rating, -1 is not found (not set) */
-    int32_t rating() const;
-    int32_t flag() const;
-    fwk::Date  creation_date() const;
-    std::string creation_date_str() const;
-    const std::vector< std::string > & keywords() const;
+ExempiManagerPtr exempi_manager_new();
 
-private:
+}
 
-    XmpPtr m_xmp;
-    // caches
-    mutable bool m_keyword_fetched;
-    mutable std::vector< std::string > m_keywords;
-};
 
+extern "C" {
+    double fwk_gps_coord_from_xmp(const char* value);
+    int32_t fwk_xmp_meta_get_orientation(const fwk::XmpMeta*);
+    int32_t fwk_xmp_meta_get_rating(const fwk::XmpMeta*);
+    // the pointer must be released by calling rust_cstring_delete()
+    char* fwk_xmp_meta_get_label(const fwk::XmpMeta*);
+    fwk::Date* fwk_xmp_meta_get_creation_date(const fwk::XmpMeta*);
 }
 
-// implemented in Rust
-extern "C" double fwk_gps_coord_from_xmp(const char* value);
-
 /*
   Local Variables:
   mode:c++
diff --git a/src/fwk/utils/exempi.rs b/src/fwk/utils/exempi.rs
index 2f53046..8cd688e 100644
--- a/src/fwk/utils/exempi.rs
+++ b/src/fwk/utils/exempi.rs
@@ -17,9 +17,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+use libc::c_char;
+use std::ffi::CString;
 use std::fs::File;
 use std::io::prelude::*;
 use std::path::Path;
+use std::ptr;
 use exempi;
 use exempi::Xmp;
 use fwk::Date;
@@ -31,10 +34,10 @@ static UFRAW_INTEROP_NAMESPACE: &'static str = "http://xmlns.figuiere.net/ns/ufr
 static UFRAW_INTEROP_NS_PREFIX: &'static str = "ufrint";
 
 
-static NS_TIFF: &'static str = "http://ns.adobe.com/tiff/1.0/";;
-static NS_XAP: &'static str = "http://ns.adobe.com/xap/1.0/";;
-static NS_EXIF: &'static str = "http://ns.adobe.com/exif/1.0/";;
-static NS_DC: &'static str = "http://purl.org/dc/elements/1.1/";;
+pub static NS_TIFF: &'static str = "http://ns.adobe.com/tiff/1.0/";;
+pub static NS_XAP: &'static str = "http://ns.adobe.com/xap/1.0/";;
+pub static NS_EXIF: &'static str = "http://ns.adobe.com/exif/1.0/";;
+pub static NS_DC: &'static str = "http://purl.org/dc/elements/1.1/";;
 
 pub struct NsDef {
     ns: String,
@@ -68,7 +71,7 @@ impl Drop for ExempiManager {
 
 
 pub struct XmpMeta {
-    xmp: Xmp,
+    pub xmp: Xmp,
     keywords: Vec<String>,
     keywords_fetched: bool,
 }
@@ -146,7 +149,7 @@ impl XmpMeta {
     }
 
     pub fn label(&self) -> Option<String> {
-        let mut flags: exempi::PropFlags = exempi::PropFlags::empty();
+        let mut flags: exempi::PropFlags = exempi::PROP_NONE;
         if let Some(xmpstring) = self.xmp.get_property(NS_XAP, "Label", &mut flags) {
             return Some(String::from(xmpstring.to_str()));
         }
@@ -154,7 +157,7 @@ impl XmpMeta {
     }
 
     pub fn rating(&self) -> Option<i32> {
-        let mut flags: exempi::PropFlags = exempi::PropFlags::empty();
+        let mut flags: exempi::PropFlags = exempi::PROP_NONE;
         return self.xmp.get_property_i32(NS_XAP, "Rating", &mut flags);
     }
 
@@ -268,6 +271,48 @@ pub fn gps_coord_from_xmp(xmps: &str) -> Option<f64> {
     None
 }
 
+#[no_mangle]
+pub fn fwk_exempi_manager_new() -> *mut ExempiManager {
+    return Box::into_raw(Box::new(ExempiManager::new(None)))
+}
+
+#[no_mangle]
+pub fn fwk_exempi_manager_delete(em: *mut ExempiManager) {
+    unsafe { Box::from_raw(em); }
+}
+
+#[no_mangle]
+pub fn fwk_xmp_meta_get_orientation(xmp: &XmpMeta) -> i32 {
+    if let Some(o) = xmp.orientation() {
+        return o;
+    }
+    0
+}
+
+#[no_mangle]
+pub fn fwk_xmp_meta_get_rating(xmp: &XmpMeta) -> i32 {
+    if let Some(r) = xmp.rating() {
+        return r;
+    }
+    0
+}
+
+
+#[no_mangle]
+pub fn fwk_xmp_meta_get_label(xmp: &XmpMeta) -> *mut c_char {
+    if let Some(s) = xmp.label() {
+        return CString::new(s.as_bytes()).unwrap().into_raw()
+    }
+    ptr::null_mut()
+}
+
+#[no_mangle]
+pub fn fwk_xmp_meta_get_creation_date(xmp: &XmpMeta) -> *mut Date {
+    if let Some(d) = xmp.creation_date() {
+        return Box::into_raw(Box::new(d));
+    }
+    ptr::null_mut()
+}
 
 #[cfg(test)]
 mod tests {
diff --git a/src/fwk/utils/files.cpp b/src/fwk/utils/files.cpp
index 6e787f1..c136b0f 100644
--- a/src/fwk/utils/files.cpp
+++ b/src/fwk/utils/files.cpp
@@ -73,6 +73,22 @@ FileList::FileList( const _impltype_t & v )
 {
 }
 
+const FileList::value_type::value_type*
+FileList::at_cstr(size_type index) const
+{
+    return _impltype_t::at(index).c_str();
+}
+
+FileList::size_type FileList::size() const
+{
+    return _impltype_t::size();
+}
+
+void FileList::sort()
+{
+    std::sort(_impltype_t::begin(), _impltype_t::end());
+}
+
 FileList::Ptr FileList::getFilesFromDirectory(const FileList::value_type& p, std::function<bool (const 
Glib::RefPtr<Gio::FileInfo>&)> filter)
 {
 //             if(!exists( p ) ) {
diff --git a/src/fwk/utils/files.hpp b/src/fwk/utils/files.hpp
index ca36219..9b999b4 100644
--- a/src/fwk/utils/files.hpp
+++ b/src/fwk/utils/files.hpp
@@ -21,16 +21,20 @@
 #ifndef __UTILS_FILES_H__
 #define __UTILS_FILES_H__
 
-#include <list>
+#include <algorithm>
+#include <vector>
 #include <string>
 #include <memory>
 
 #include <functional>
 
+#if !RUST_BINDGEN
 #include <giomm/fileinfo.h>
+#endif
 
 namespace fwk {
 
+#if !RUST_BINDGEN
 /** wrapper around g_dir_make_tmp() */
 std::string make_tmp_dir(const std::string& base);
 
@@ -38,14 +42,18 @@ bool filter_none(const Glib::RefPtr<Gio::FileInfo> & file);
 bool filter_ext(const Glib::RefPtr<Gio::FileInfo> & file,
                 const std::string & ext);
 bool filter_xmp_out(const Glib::RefPtr<Gio::FileInfo> & file);
+#endif
+
+class FileList;
+typedef std::shared_ptr<FileList> FileListPtr;
 
 class FileList
-       : private std::list<std::string>
+       : private std::vector<std::string>
 {
 public:
-    typedef std::shared_ptr< FileList > Ptr;
+    typedef FileListPtr Ptr;
 
-    typedef std::list< std::string >    _impltype_t;
+    typedef std::vector<std::string>    _impltype_t;
     typedef _impltype_t::value_type       value_type;
     typedef _impltype_t::iterator         iterator;
     typedef _impltype_t::const_iterator   const_iterator;
@@ -55,9 +63,14 @@ public:
         {}
     FileList(const _impltype_t&);
 
+#if !RUST_BINDGEN
     static Ptr getFilesFromDirectory(const value_type& dir,
                                      std::function<bool (const Glib::RefPtr<Gio::FileInfo>&)> filter);
+#endif
 
+    value_type at(size_type index) const
+        { return _impltype_t::at(index); }
+    const value_type::value_type* at_cstr(size_type index) const;
     const_iterator begin() const
         { return _impltype_t::cbegin(); }
     const_iterator end() const
@@ -66,10 +79,8 @@ public:
         { return _impltype_t::cbegin(); }
     const_iterator cend() const
         { return _impltype_t::cend(); }
-    size_type size() const
-        { return _impltype_t::size(); }
-    void sort()
-        { _impltype_t::sort(); }
+    size_type size() const;
+    void sort();
     void push_back(const value_type & v)
         { _impltype_t::push_back(v); }
 };
diff --git a/src/lib.rs b/src/lib.rs
index d495a93..b50bfe3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -29,3 +29,11 @@ pub mod fwk;
 pub mod engine;
 
 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+
+use libc::c_char;
+use std::ffi::CString;
+
+#[no_mangle]
+pub fn rust_cstring_delete(string: *mut c_char) {
+    unsafe { CString::from_raw(string); }
+}
diff --git a/src/libraryclient/Makefile.am b/src/libraryclient/Makefile.am
index 44d23a6..f9c2877 100644
--- a/src/libraryclient/Makefile.am
+++ b/src/libraryclient/Makefile.am
@@ -9,10 +9,11 @@ TESTS = test_worker
 TEST_LIBS =  \
        liblibraryclient.a \
        $(top_builddir)/src/engine/libniepceengine.a \
+       $(top_builddir)/target/debug/libniepce_rust.a \
+       $(top_builddir)/src/engine/libniepceengineglue.a \
         $(top_builddir)/src/fwk/utils/libniepceutils.a \
        $(top_builddir)/src/fwk/toolkit/libniepceframework.a \
        $(top_builddir)/src/fwk/base/libfwkbase.a \
-       $(top_builddir)/target/debug/libniepce_rust.a \
         @BOOST_UNIT_TEST_FRAMEWORK_LIBS@ \
        @FRAMEWORK_LIBS@ \
        @OPENRAW_LIBS@ \
diff --git a/src/libraryclient/clientimpl.cpp b/src/libraryclient/clientimpl.cpp
index df41836..23238ec 100644
--- a/src/libraryclient/clientimpl.cpp
+++ b/src/libraryclient/clientimpl.cpp
@@ -25,9 +25,8 @@
 #include "clientimpl.hpp"
 #include "locallibraryserver.hpp"
 
-using fwk::FileList;
+using fwk::FileListPtr;
 using eng::Op;
-using eng::Commands;
 using eng::LibraryManaged;
 using eng::tid_t;
 
@@ -61,7 +60,7 @@ bool ClientImpl::ok() const
    @param func the function to schedule
    @return the tid
  */
-tid_t ClientImpl::schedule_op(const Op::function_t & func)
+tid_t ClientImpl::schedule_op(const Op::Function & func)
 {
     tid_t id = LibraryClient::newTid();
     Op::Ptr op(new Op(id, func));
@@ -71,44 +70,44 @@ tid_t ClientImpl::schedule_op(const Op::function_t & func)
 
 tid_t ClientImpl::getAllKeywords()
 {
-    return schedule_op(&Commands::cmdListAllKeywords);
+    return schedule_op(&cmd_list_all_keywords);
 }
 
 
 tid_t ClientImpl::getAllFolders()
 {
-    return schedule_op(&Commands::cmdListAllFolders);
+    return schedule_op(&cmd_list_all_folders);
 }
 
 tid_t ClientImpl::queryFolderContent(eng::library_id_t folder_id)
 {
     return schedule_op([folder_id](const auto& lib) {
-        Commands::cmdQueryFolderContent(lib, folder_id);
-    });
+            return cmd_query_folder_content(lib, folder_id);
+        });
 }
 
 
 tid_t ClientImpl::countFolder(eng::library_id_t folder_id)
 {
     return schedule_op([folder_id](const auto& lib) {
-        Commands::cmdCountFolder(lib, folder_id);
-    });
+            return cmd_count_folder(lib, folder_id);
+        });
 }
 
 
 tid_t ClientImpl::queryKeywordContent(eng::library_id_t keyword_id)
 {
     return schedule_op([keyword_id](const auto& lib) {
-        Commands::cmdQueryKeywordContent(lib, keyword_id);
-    });
+            return cmd_query_keyword_content(lib, keyword_id);
+        });
 }
 
 
 tid_t ClientImpl::requestMetadata(eng::library_id_t file_id)
 {
     return schedule_op([file_id](const auto& lib) {
-        Commands::cmdRequestMetadata(lib, file_id);
-    });
+           return cmd_request_metadata(lib, file_id);
+        });
 }
 
 
@@ -116,15 +115,15 @@ tid_t ClientImpl::setMetadata(eng::library_id_t file_id, int meta,
                               const fwk::PropertyValue & value)
 {
     return schedule_op([file_id, meta, value](const auto& lib) {
-        Commands::cmdSetMetadata(lib, file_id, meta, value);
-    });
+            return cmd_set_metadata(lib, file_id, meta, &value);
+        });
 }
 
 tid_t ClientImpl::write_metadata(eng::library_id_t file_id)
 {
     return schedule_op([file_id](const auto& lib) {
-        Commands::cmdWriteMetadata(lib, file_id);
-    });
+            return cmd_write_metadata(lib, file_id);
+        });
 }
 
 tid_t ClientImpl::moveFileToFolder(eng::library_id_t file_id,
@@ -132,29 +131,29 @@ tid_t ClientImpl::moveFileToFolder(eng::library_id_t file_id,
                                    eng::library_id_t to_folder_id)
 {
     return schedule_op([file_id, from_folder_id, to_folder_id](const auto& lib) {
-        Commands::cmdMoveFileToFolder(lib, file_id, from_folder_id, to_folder_id);
-    });
+            return cmd_move_file_to_folder(lib, file_id, from_folder_id, to_folder_id);
+        });
 }
 
 
 tid_t ClientImpl::getAllLabels()
 {
-    return schedule_op(&Commands::cmdListAllLabels);
+    return schedule_op(&cmd_list_all_labels);
 }
 
 
 tid_t ClientImpl::createLabel(const std::string & s, const std::string & colour)
 {
     return schedule_op([s, colour](const auto& lib) {
-        Commands::cmdCreateLabel(lib, s, colour);
-    });
+            return cmd_create_label(lib, s.c_str(), colour.c_str());
+        });
 }
 
 tid_t ClientImpl::deleteLabel(int label_id)
 {
     return schedule_op([label_id](const auto& lib) {
-        Commands::cmdDeleteLabel(lib, label_id);
-    });
+            return cmd_delete_label(lib, label_id);
+        });
 }
 
 tid_t ClientImpl::updateLabel(eng::library_id_t label_id,
@@ -162,34 +161,34 @@ tid_t ClientImpl::updateLabel(eng::library_id_t label_id,
                               const std::string & new_colour)
 {
     return schedule_op([label_id, new_name, new_colour](const auto& lib) {
-        Commands::cmdUpdateLabel(lib, label_id, new_name, new_colour);
-    });
+            return cmd_update_label(lib, label_id, new_name.c_str(), new_colour.c_str());
+        });
 }
 
 
 tid_t ClientImpl::processXmpUpdateQueue(bool write_xmp)
 {
     return schedule_op([write_xmp](const auto& lib) {
-        Commands::cmdProcessXmpUpdateQueue(lib, write_xmp);
-    });
+            return cmd_process_xmp_update_queue(lib, write_xmp);
+        });
 }
 
 tid_t ClientImpl::importFile(const std::string & path, LibraryManaged manage)
 {
     return schedule_op([path, manage](const auto& lib) {
-        Commands::cmdImportFile(lib, path, manage);
-    });
+            return cmd_import_file(lib, path.c_str(), manage);
+        });
 }
 
 tid_t ClientImpl::importFromDirectory(const std::string & dir, LibraryManaged manage)
 {
-    FileList::Ptr files;
+    FileListPtr files;
 
-    files = FileList::getFilesFromDirectory(dir, &fwk::filter_none);
+    files = fwk::FileList::getFilesFromDirectory(dir, &fwk::filter_none);
 
     return schedule_op([dir, files, manage](const auto& lib) {
-        Commands::cmdImportFiles(lib, dir, files, manage);
-    });
+            return cmd_import_files(lib, dir.c_str(), files.get(), manage);
+        });
 }
 
 }
diff --git a/src/libraryclient/clientimpl.hpp b/src/libraryclient/clientimpl.hpp
index 69a7863..e068a3c 100644
--- a/src/libraryclient/clientimpl.hpp
+++ b/src/libraryclient/clientimpl.hpp
@@ -72,7 +72,7 @@ protected:
     std::unique_ptr<LocalLibraryServer> m_localLibrary;
 private:
     /** do the dirty work of scheduling the op */
-    eng::tid_t schedule_op(const eng::Op::function_t & func);
+    eng::tid_t schedule_op(const eng::Op::Function & func);
 };
 
 }
diff --git a/src/libraryclient/locallibraryserver.cpp b/src/libraryclient/locallibraryserver.cpp
index 9e449cf..f468ae6 100644
--- a/src/libraryclient/locallibraryserver.cpp
+++ b/src/libraryclient/locallibraryserver.cpp
@@ -27,6 +27,7 @@ namespace libraryclient {
 
 void LocalLibraryServer::execute(const Op::Ptr& _op)
 {
-    (*_op)(m_library);
+  (*_op)(m_library.get());
 }
+
 }
diff --git a/src/libraryclient/locallibraryserver.hpp b/src/libraryclient/locallibraryserver.hpp
index fbafb46..8897c0c 100644
--- a/src/libraryclient/locallibraryserver.hpp
+++ b/src/libraryclient/locallibraryserver.hpp
@@ -1,7 +1,7 @@
 /*
- * niepce - libraryclient/locallibraryserver.h
+ * niepce - libraryclient/locallibraryserver.hpp
  *
- * Copyright (C) 2007-2013 Hubert Figuiere
+ * Copyright (C) 2007-2017 Hubert Figuière
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,21 +29,21 @@ namespace libraryclient {
 
 class LocalLibraryServer : public fwk::Worker<eng::Op> {
 public:
-    /** create the local server for the library whose dir is specified */
-    LocalLibraryServer(const std::string& dir,
-                       uint64_t notif_id)
-        : fwk::Worker<eng::Op>()
-        , m_library(eng::Library::Ptr(new eng::Library(dir, notif_id)))
+  /** create the local server for the library whose dir is specified */
+  LocalLibraryServer(const std::string& dir, uint64_t notif_id)
+    : fwk::Worker<eng::Op>()
+    , m_library(eng::library_new(dir.c_str(), notif_id))
     {
     }
-    bool ok() const { return m_library && m_library->ok(); }
+  bool ok() const { return m_library && engine_db_library_ok(m_library.get()); }
 
 protected:
-    virtual void execute(const ptr_t& _op) override;
+  virtual void execute(const ptr_t& _op) override;
 
 private:
-    eng::Library::Ptr m_library;
+  eng::LibraryPtr m_library;
 };
+
 }
 
 #endif
diff --git a/src/libraryclient/test_worker.cpp b/src/libraryclient/test_worker.cpp
index e942738..fcea80a 100644
--- a/src/libraryclient/test_worker.cpp
+++ b/src/libraryclient/test_worker.cpp
@@ -40,11 +40,11 @@ int test_main(int, char *[])
        BOOST_CHECK(ptempl);
        {
                fwk::DirectoryDisposer d(ptempl);
-               LocalLibraryServer w(std::string("") + ptempl, fwk::NotificationCenter::Ptr());
+               LocalLibraryServer w(std::string("") + ptempl, 0);
 
                BOOST_CHECK(w._tasks().empty());
 
-               Op::Ptr p(new Op(0, [](const Library::Ptr &){}));
+               Op::Ptr p(new Op(0, [](Library*){ return false; }));
                w.schedule(p);
        }
     return 0;
diff --git a/src/libraryclient/uidataprovider.cpp b/src/libraryclient/uidataprovider.cpp
index 466df21..68757ba 100644
--- a/src/libraryclient/uidataprovider.cpp
+++ b/src/libraryclient/uidataprovider.cpp
@@ -28,23 +28,20 @@
 
 namespace libraryclient {
 
-void UIDataProvider::updateLabel(const eng::Label::Ptr & l)
+void UIDataProvider::updateLabel(const eng::Label & l)
 {
     // TODO: will work as long as we have 5 labels or something.
-    for(auto iter : m_labels) {
-        if(iter->id() == l->id()) {
-            iter->set_label(l->label());
-            iter->set_colour(l->colour());
+    for (auto & label : m_labels) {
+        if (engine_db_label_id(label.get()) == engine_db_label_id(&l)) {
+            label = eng::label_clone(&l);
         }
     }
 }
 
 
-void UIDataProvider::addLabels(const eng::Label::ListPtr & l)
+void UIDataProvider::addLabel(const eng::Label & l)
 {
-    for(auto iter : *l) {
-        m_labels.push_back(eng::Label::Ptr(new eng::Label(*iter)));
-    }
+    m_labels.push_back(eng::label_clone(&l));
 }
 
 
@@ -54,7 +51,7 @@ void UIDataProvider::deleteLabel(int id)
     for(auto iter = m_labels.begin();
         iter != m_labels.end(); ++iter) {
 
-        if((*iter)->id() == id) {
+        if (engine_db_label_id(iter->get()) == id) {
             DBG_OUT("remove label %d", id);
             iter = m_labels.erase(iter);
             break;
@@ -62,14 +59,15 @@ void UIDataProvider::deleteLabel(int id)
     }
 }
 
-fwk::Option<fwk::RgbColour> UIDataProvider::colourForLabel(int id) const
+fwk::Option<fwk::RgbColourPtr> UIDataProvider::colourForLabel(int id) const
 {
-    for(auto iter : m_labels) {
-        if(iter->id() == id) {
-            return fwk::Option<fwk::RgbColour>(iter->colour());
+    for(auto label : m_labels) {
+        if (engine_db_label_id(label.get()) == id) {
+            return fwk::Option<fwk::RgbColourPtr>(
+                fwk::rgbcolour_clone(engine_db_label_colour(label.get())));
         }
     }
-    return fwk::Option<fwk::RgbColour>();
+    return fwk::Option<fwk::RgbColourPtr>();
 }
 
 
diff --git a/src/libraryclient/uidataprovider.hpp b/src/libraryclient/uidataprovider.hpp
index 4d85dfd..19f479d 100644
--- a/src/libraryclient/uidataprovider.hpp
+++ b/src/libraryclient/uidataprovider.hpp
@@ -23,12 +23,9 @@
 #include <stdint.h>
 
 #include "fwk/base/option.hpp"
+#include "fwk/base/colour.hpp"
 #include "engine/db/label.hpp"
 
-namespace fwk {
-class RgbColour;
-}
-
 namespace libraryclient {
 
 class UIDataProvider
@@ -36,14 +33,14 @@ class UIDataProvider
 public:
     // label management
 
-    void updateLabel(const eng::Label::Ptr &);
-    void addLabels(const eng::Label::ListPtr & l);
+    void updateLabel(const eng::Label &);
+    void addLabel(const eng::Label & l);
     void deleteLabel(int id);
-    fwk::Option<fwk::RgbColour> colourForLabel(int id) const;
-    const eng::Label::List & getLabels() const
+    fwk::Option<fwk::RgbColourPtr> colourForLabel(int id) const;
+    const eng::LabelList & getLabels() const
         { return m_labels; }
 private:
-    eng::Label::List m_labels;
+    eng::LabelList m_labels;
 };
 
 }
diff --git a/src/niepce/Makefile.am b/src/niepce/Makefile.am
index 1729e56..cb1a261 100644
--- a/src/niepce/Makefile.am
+++ b/src/niepce/Makefile.am
@@ -24,6 +24,7 @@ niepce_LDADD = \
        $(top_builddir)/target/debug/libniepce_rust.a \
        $(top_builddir)/src/engine/libniepceengine.a \
        $(top_builddir)/src/fwk/utils/libniepceutils.a \
+       $(top_builddir)/src/engine/libniepceengineglue.a \
        @FRAMEWORK_LIBS@ \
        @GPHOTO_LIBS@ \
        @BABL_LIBS@ \
diff --git a/src/niepce/main.cpp b/src/niepce/main.cpp
index 663e6c6..5dc7195 100644
--- a/src/niepce/main.cpp
+++ b/src/niepce/main.cpp
@@ -38,7 +38,7 @@ int main(int argc, char ** argv)
 
   fwk::utils::init();
 
-  fwk::ExempiManager ex_manager(nullptr);
+  fwk::ExempiManagerPtr manager = fwk::exempi_manager_new();
 
   fwk::Application::Ptr app = ui::NiepceApplication::create(argc, argv);
   return fwk::Application::main(app,
diff --git a/src/niepce/modules/map/mapmodule.cpp b/src/niepce/modules/map/mapmodule.cpp
index 43896d2..56b6fc5 100644
--- a/src/niepce/modules/map/mapmodule.cpp
+++ b/src/niepce/modules/map/mapmodule.cpp
@@ -20,8 +20,10 @@
 #include <gtkmm/box.h>
 
 #include "fwk/base/debug.hpp"
+#include "fwk/utils/exempi.hpp"
 #include "fwk/toolkit/application.hpp"
 #include "engine/db/properties.hpp"
+#include "engine/db/libmetadata.hpp"
 #include "mapmodule.hpp"
 
 namespace mapm {
@@ -69,17 +71,17 @@ MapModule::on_lib_notification(const eng::LibNotification &ln)
     if (!m_active) {
         return;
     }
-    switch(ln.type) {
-    case eng::LibNotification::Type::METADATA_QUERIED:
+    switch(engine_library_notification_type(&ln)) {
+    case eng::LibNotificationType::METADATA_QUERIED:
     {
-        auto lm = ln.get<eng::LibNotification::Type::METADATA_QUERIED>().metadata;
+        auto lm = engine_library_notification_get_libmetadata(&ln);
         DBG_OUT("received metadata in MapModule");
 
         if (lm) {
             fwk::PropertyBag properties;
             const fwk::PropertySet propset = { eng::NpExifGpsLongProp,
                                                eng::NpExifGpsLatProp };
-            lm->to_properties(propset, properties);
+            eng::libmetadata_to_properties(lm, propset, properties);
             double latitude, longitude;
             latitude = longitude = NAN;
             auto result = properties.get_value_for_property(eng::NpExifGpsLongProp);
diff --git a/src/niepce/notificationcenter.cpp b/src/niepce/notificationcenter.cpp
index 264bc1c..77128a8 100644
--- a/src/niepce/notificationcenter.cpp
+++ b/src/niepce/notificationcenter.cpp
@@ -20,6 +20,7 @@
 #include <boost/any.hpp>
 
 #include "fwk/base/debug.hpp"
+#include "engine/library/notification.hpp"
 #include "niepce/notifications.hpp"
 #include "niepce/notificationcenter.hpp"
 
@@ -41,16 +42,15 @@ void NotificationCenter::dispatch_notification(const fwk::Notification::Ptr &n)
         switch(n->type()) {
         case NOTIFICATION_LIB:
         {
-            eng::LibNotification ln
-                = boost::any_cast<eng::LibNotification>(n->data());
-            signal_lib_notification (ln);
+            auto ln = boost::any_cast<eng::LibNotificationPtr>(n->data());
+            signal_lib_notification(*ln);
             break;
         }
         case NOTIFICATION_THUMBNAIL:
         {
             eng::ThumbnailNotification tn
                 = boost::any_cast<eng::ThumbnailNotification>(n->data());
-            signal_thumbnail_notification (tn);
+            signal_thumbnail_notification(tn);
             break;
         }
         default:
diff --git a/src/niepce/notificationcenter.hpp b/src/niepce/notificationcenter.hpp
index 46713d8..7afe4c8 100644
--- a/src/niepce/notificationcenter.hpp
+++ b/src/niepce/notificationcenter.hpp
@@ -26,6 +26,7 @@
 
 #include "fwk/toolkit/notificationcenter.hpp"
 #include "engine/db/library.hpp"
+#include "engine/library/notification.hpp"
 #include "engine/library/thumbnailnotification.hpp"
 
 
diff --git a/src/niepce/ui/dialogs/editlabels.cpp b/src/niepce/ui/dialogs/editlabels.cpp
index 74a5e0d..4e20050 100644
--- a/src/niepce/ui/dialogs/editlabels.cpp
+++ b/src/niepce/ui/dialogs/editlabels.cpp
@@ -69,9 +69,10 @@ void EditLabels::setup_widget()
         m_entries[i] = labelentry;
 
         if(has_label) {
-            Gdk::RGBA colour = fwk::rgbcolour_to_gdkcolor(m_labels[i]->colour());
+            Gdk::RGBA colour = fwk::rgbcolour_to_gdkcolor(
+                *engine_db_label_colour(m_labels[i].get()));
             colourbutton->set_rgba(colour);
-            labelentry->set_text(m_labels[i]->label());
+            labelentry->set_text(engine_db_label_label(m_labels[i].get()));
         }
         colourbutton->signal_color_set().connect(
             sigc::bind(sigc::mem_fun(*this, &EditLabels::label_colour_changed), i));
@@ -107,16 +108,18 @@ void EditLabels::update_labels(int /*response*/)
                 continue;
             }
             std::string new_colour
-                = fwk::gdkcolor_to_rgbcolour(m_colours[i]->get_rgba()).to_string();
+                = fwk::rgbcolour_to_string(
+                    fwk::gdkcolor_to_rgbcolour(m_colours[i]->get_rgba()).get());
             if(!undo) {
                 undo = fwk::Application::app()->begin_undo(_("Change Labels"));
             }
 
             auto libclient = m_lib_client;
             if(has_label) {
-                std::string current_name = m_labels[i]->label();
-                std::string current_colour = m_labels[i]->colour().to_string();
-                auto label_id = m_labels[i]->id();
+                std::string current_name = engine_db_label_label(m_labels[i].get());
+                std::string current_colour =
+                    fwk::rgbcolour_to_string(engine_db_label_colour(m_labels[i].get()));
+                auto label_id = engine_db_label_id(m_labels[i].get());
 
                 undo->new_command<void>(
                     [libclient, new_name, new_colour, label_id] () {
diff --git a/src/niepce/ui/dialogs/editlabels.hpp b/src/niepce/ui/dialogs/editlabels.hpp
index dac3f61..2a3cd0e 100644
--- a/src/niepce/ui/dialogs/editlabels.hpp
+++ b/src/niepce/ui/dialogs/editlabels.hpp
@@ -46,7 +46,7 @@ private:
     void label_name_changed(size_t idx);
     void label_colour_changed(size_t idx);
     void update_labels(int /*response*/);
-    const eng::Label::List& m_labels;
+    const eng::LabelList& m_labels;
     std::array<Gtk::ColorButton*, 5> m_colours;
     std::array<Gtk::Entry*, 5> m_entries;
     std::array<bool, 5> m_status;
diff --git a/src/niepce/ui/gridviewmodule.cpp b/src/niepce/ui/gridviewmodule.cpp
index ac299a0..5ffd0a6 100644
--- a/src/niepce/ui/gridviewmodule.cpp
+++ b/src/niepce/ui/gridviewmodule.cpp
@@ -54,21 +54,25 @@ GridViewModule::~GridViewModule()
 void
 GridViewModule::on_lib_notification(const eng::LibNotification &ln)
 {
-    switch(ln.type) {
-    case eng::LibNotification::Type::METADATA_QUERIED:
+    switch(engine_library_notification_type(&ln)) {
+    case eng::LibNotificationType::METADATA_QUERIED:
     {
-        auto lm = ln.get<eng::LibNotification::Type::METADATA_QUERIED>();
+        auto lm = engine_library_notification_get_libmetadata(&ln);
         DBG_OUT("received metadata");
-        m_metapanecontroller->display(lm.file, lm.metadata);
+        if (lm) {
+            m_metapanecontroller->display(engine_libmetadata_get_id(lm), lm);
+        } else {
+            ERR_OUT("Invalid LibMetadata (nullptr)");
+        }
         break;
     }
-    case eng::LibNotification::Type::METADATA_CHANGED:
+    case eng::LibNotificationType::METADATA_CHANGED:
     {
         DBG_OUT("metadata changed");
-        auto m = ln.get<eng::LibNotification::Type::METADATA_CHANGED>();
-        if(m.id == m_metapanecontroller->displayed_file()) {
+        auto id = engine_library_notification_get_id(&ln);
+        if(id && id == m_metapanecontroller->displayed_file()) {
             // FIXME: actually just update the metadata
-          m_shell.getLibraryClient()->requestMetadata(m.id);
+          m_shell.getLibraryClient()->requestMetadata(id);
         }
         break;
     }
@@ -80,7 +84,7 @@ GridViewModule::on_lib_notification(const eng::LibNotification &ln)
 
 void GridViewModule::display_none()
 {
-    m_metapanecontroller->display(0, eng::LibMetadata::Ptr());
+    m_metapanecontroller->display(0, nullptr);
 }
 
 
diff --git a/src/niepce/ui/imageliststore.cpp b/src/niepce/ui/imageliststore.cpp
index d4a82b2..b14bb32 100644
--- a/src/niepce/ui/imageliststore.cpp
+++ b/src/niepce/ui/imageliststore.cpp
@@ -90,24 +90,23 @@ Gtk::TreePath ImageListStore::get_path_from_id(eng::library_id_t id)
 
 void ImageListStore::on_lib_notification(const eng::LibNotification &ln)
 {
-    switch (ln.type) {
-    case eng::LibNotification::Type::FOLDER_CONTENT_QUERIED:
-    case eng::LibNotification::Type::KEYWORD_CONTENT_QUERIED:
+    switch (engine_library_notification_type(&ln)) {
+    case eng::LibNotificationType::FOLDER_CONTENT_QUERIED:
+    case eng::LibNotificationType::KEYWORD_CONTENT_QUERIED:
     {
-        auto param = ln.get<eng::LibNotification::Type::FOLDER_CONTENT_QUERIED>();
-        auto l = param.files;
-        if (ln.type == eng::LibNotification::Type::FOLDER_CONTENT_QUERIED) {
-            m_current_folder = param.container;
+        auto param = engine_library_notification_get_content(&ln);
+        const auto& l = param->files;
+        if (engine_library_notification_type(&ln) == eng::LibNotificationType::FOLDER_CONTENT_QUERIED) {
+            m_current_folder = param->container;
             m_current_keyword = 0;
-        } else if (ln.type == eng::LibNotification::Type::KEYWORD_CONTENT_QUERIED) {
+        } else if (engine_library_notification_type(&ln) == 
eng::LibNotificationType::KEYWORD_CONTENT_QUERIED) {
             m_current_folder = 0;
-            m_current_keyword = param.container;
+            m_current_keyword = param->container;
         }
-        DBG_OUT("received folder content file # %lu", l->size());
+        DBG_OUT("received folder content file # %lu %p %p", l->size());
         // clear the map before the list.
         m_idmap.clear();
         clear();
-        eng::LibFileList::const_iterator iter = l->begin();
         for_each(l->begin(), l->end(),
                  [this] (const eng::LibFilePtr & f) {
                      Gtk::TreeModel::iterator riter = append();
@@ -120,46 +119,49 @@ void ImageListStore::on_lib_notification(const eng::LibNotification &ln)
                      m_idmap[engine_db_libfile_id(f.get())] = riter;
                  });
         // at that point clear the cache because the icon view is populated.
-        getLibraryClient()->thumbnailCache().request(l);
+        getLibraryClient()->thumbnailCache().request(*l);
         break;
     }
-    case eng::LibNotification::Type::FILE_MOVED:
+    case eng::LibNotificationType::FILE_MOVED:
     {
         DBG_OUT("File moved. Current folder %ld", m_current_folder);
-        auto param = ln.get<eng::LibNotification::Type::FILE_MOVED>();
+        auto param = engine_library_notification_get_filemoved(&ln);
         if (m_current_folder == 0) {
             return;
         }
-        if (param.from == m_current_folder) {
+        if (param->from == m_current_folder) {
             // remove from list
             DBG_OUT("from this folder");
-            auto iter = get_iter_from_id(param.file);
+            auto iter = get_iter_from_id(param->file);
             if (iter) {
                 iter = erase(iter);
             }
-        } else if (param.to == m_current_folder) {
+        } else if (param->to == m_current_folder) {
             // XXX add to list. but this isn't likely to happen atm.
         }
+        break;
     }
-    case eng::LibNotification::Type::METADATA_CHANGED:
+    case eng::LibNotificationType::METADATA_CHANGED:
     {
-        auto m = ln.get<eng::LibNotification::Type::METADATA_CHANGED>();
-        fwk::PropertyIndex prop = m.meta;
+        auto m = engine_library_notification_get_metadatachange(&ln);
+        const fwk::PropertyIndex& prop = m->meta;
         DBG_OUT("metadata changed %s", eng::_propertyName(prop));
         // only interested in a few props
         if(is_property_interesting(prop)) {
-            std::map<eng::library_id_t, Gtk::TreeIter>::const_iterator iter = m_idmap.find(m.id);
+            std::map<eng::library_id_t, Gtk::TreeIter>::const_iterator iter =
+                m_idmap.find(m->id);
             if(iter != m_idmap.end()) {
                 Gtk::TreeRow row = *(iter->second);
                 //
                 eng::LibFilePtr file = row[m_columns.m_libfile];
-                engine_db_libfile_set_property(file.get(), prop, boost::get<int32_t>(m.value));
+                engine_db_libfile_set_property(
+                    file.get(), prop, boost::get<int32_t>(m->value.get_variant()));
                 row[m_columns.m_libfile] = file;
             }
         }
         break;
     }
-    case eng::LibNotification::Type::XMP_NEEDS_UPDATE:
+    case eng::LibNotificationType::XMP_NEEDS_UPDATE:
     {
         fwk::Configuration & cfg = fwk::Application::app()->config();
         int write_xmp = false;
diff --git a/src/niepce/ui/imageliststore.hpp b/src/niepce/ui/imageliststore.hpp
index a3d889f..396d712 100644
--- a/src/niepce/ui/imageliststore.hpp
+++ b/src/niepce/ui/imageliststore.hpp
@@ -29,6 +29,7 @@
 #include "fwk/toolkit/controller.hpp"
 #include "engine/db/libfile.hpp"
 #include "engine/db/library.hpp"
+#include "engine/library/notification.hpp"
 #include "engine/library/thumbnailnotification.hpp"
 #include "libraryclient/libraryclient.hpp"
 
diff --git a/src/niepce/ui/librarycellrenderer.cpp b/src/niepce/ui/librarycellrenderer.cpp
index 11f41ee..3535f28 100644
--- a/src/niepce/ui/librarycellrenderer.cpp
+++ b/src/niepce/ui/librarycellrenderer.cpp
@@ -276,7 +276,7 @@ LibraryCellRenderer::render_vfunc(const Cairo::RefPtr<Cairo::Context>& cr,
                 auto result = m_shell.get_ui_data_provider()->colourForLabel(label_id);
                 DBG_ASSERT(!result.empty(), "colour not found");
                 if (!result.empty()) {
-                    drawLabel(cr, left, result.unwrap(), r);
+                    drawLabel(cr, left, *result.unwrap(), r);
                 }
             }
         }
diff --git a/src/niepce/ui/metadatapanecontroller.cpp b/src/niepce/ui/metadatapanecontroller.cpp
index d5d9b6f..a44e679 100644
--- a/src/niepce/ui/metadatapanecontroller.cpp
+++ b/src/niepce/ui/metadatapanecontroller.cpp
@@ -143,14 +143,14 @@ void MetaDataPaneController::on_metadata_changed(const fwk::PropertyBag & props,
 }
 
 
-void MetaDataPaneController::display(eng::library_id_t file_id, const eng::LibMetadata::Ptr & meta)
+void MetaDataPaneController::display(eng::library_id_t file_id, const eng::LibMetadata* meta)
 {
     m_fileid = file_id;
     DBG_OUT("displaying metadata");
     fwk::PropertyBag properties;
     if(meta) {
         const fwk::PropertySet & propset = get_property_set();
-        meta->to_properties(propset, properties);
+        libmetadata_to_properties(meta, propset, properties);
     }
     std::for_each(m_widgets.begin(), m_widgets.end(),
                   [properties] (auto w) {
diff --git a/src/niepce/ui/metadatapanecontroller.hpp b/src/niepce/ui/metadatapanecontroller.hpp
index 936557d..a9c3afe 100644
--- a/src/niepce/ui/metadatapanecontroller.hpp
+++ b/src/niepce/ui/metadatapanecontroller.hpp
@@ -44,7 +44,7 @@ public:
     MetaDataPaneController();
     ~MetaDataPaneController();
     virtual Gtk::Widget * buildWidget() override;
-    void display(eng::library_id_t file_id, const eng::LibMetadata::Ptr & meta);
+    void display(eng::library_id_t file_id, const eng::LibMetadata* meta);
     eng::library_id_t displayed_file() const
         { return m_fileid; }
 
diff --git a/src/niepce/ui/niepcewindow.cpp b/src/niepce/ui/niepcewindow.cpp
index 664b619..5bcfdf3 100644
--- a/src/niepce/ui/niepcewindow.cpp
+++ b/src/niepce/ui/niepcewindow.cpp
@@ -326,36 +326,48 @@ void NiepceWindow::on_open_library()
 void NiepceWindow::create_initial_labels()
 {
     // TODO make this parametric from resources
-    m_libClient->createLabel(_("Label 1"), fwk::RgbColour(55769, 9509, 4369).to_string()); /* 217, 37, 17 */
-    m_libClient->createLabel(_("Label 2"), fwk::RgbColour(24929, 55769, 4369).to_string()); /* 97, 217, 17 */
-    m_libClient->createLabel(_("Label 3"), fwk::RgbColour(4369, 50629, 55769).to_string()); /* 17, 197, 217 
*/
-    m_libClient->createLabel(_("Label 4"), fwk::RgbColour(35209, 4369, 55769).to_string()); /* 137, 17, 217 
*/
-    m_libClient->createLabel(_("Label 5"), fwk::RgbColour(55769, 35209, 4369).to_string()); /* 217, 137, 17 
*/
+    m_libClient->createLabel(_("Label 1"), fwk::rgbcolour_to_string(55769, 9509, 4369)); /* 217, 37, 17 */
+    m_libClient->createLabel(_("Label 2"), fwk::rgbcolour_to_string(24929, 55769, 4369)); /* 97, 217, 17 */
+    m_libClient->createLabel(_("Label 3"), fwk::rgbcolour_to_string(4369, 50629, 55769)); /* 17, 197, 217 */
+    m_libClient->createLabel(_("Label 4"), fwk::rgbcolour_to_string(35209, 4369, 55769)); /* 137, 17, 217 */
+    m_libClient->createLabel(_("Label 5"), fwk::rgbcolour_to_string(55769, 35209, 4369)); /* 217, 137, 17 */
 }
 
 
-void NiepceWindow::on_lib_notification(const eng::LibNotification & ln)
+void NiepceWindow::on_lib_notification(const eng::LibNotification& ln)
 {
-    switch(ln.type) {
-    case eng::LibNotification::Type::NEW_LIBRARY_CREATED:
+    switch(engine_library_notification_type(&ln)) {
+    case eng::LibNotificationType::NEW_LIBRARY_CREATED:
         create_initial_labels();
         break;
-    case eng::LibNotification::Type::ADDED_LABELS:
+    case eng::LibNotificationType::ADDED_LABEL:
     {
-        auto l = ln.get<eng::LibNotification::Type::ADDED_LABELS>();
-        m_libClient->getDataProvider()->addLabels(l.labels);
+        auto l = engine_library_notification_get_label(&ln);
+        if (l) {
+            m_libClient->getDataProvider()->addLabel(*l);
+        } else {
+            ERR_OUT("Invalid label (nullptr)");
+        }
         break;
     }
-    case eng::LibNotification::Type::LABEL_CHANGED:
+    case eng::LibNotificationType::LABEL_CHANGED:
     {
-        auto l = ln.get<eng::LibNotification::Type::LABEL_CHANGED>();
-        m_libClient->getDataProvider()->updateLabel(l.label);
+        auto l = engine_library_notification_get_label(&ln);
+        if (l) {
+            m_libClient->getDataProvider()->updateLabel(*l);
+        } else {
+            ERR_OUT("Invalid label (nullptr)");
+        }
         break;
     }
-    case eng::LibNotification::Type::LABEL_DELETED:
+    case eng::LibNotificationType::LABEL_DELETED:
     {
-        auto id = ln.get<eng::LibNotification::Type::LABEL_DELETED>();
-        m_libClient->getDataProvider()->deleteLabel(id.id);
+        auto id = engine_library_notification_get_id(&ln);
+        if (id) {
+            m_libClient->getDataProvider()->deleteLabel(id);
+        } else {
+            ERR_OUT("Invalid ID");
+        }
         break;
     }
     default:
diff --git a/src/niepce/ui/niepcewindow.hpp b/src/niepce/ui/niepcewindow.hpp
index 8454ae7..cb022d5 100644
--- a/src/niepce/ui/niepcewindow.hpp
+++ b/src/niepce/ui/niepcewindow.hpp
@@ -32,6 +32,7 @@
 #include "fwk/toolkit/appframe.hpp"
 #include "fwk/toolkit/configdatabinder.hpp"
 #include "engine/db/label.hpp"
+#include "engine/library/notification.hpp"
 #include "libraryclient/libraryclient.hpp"
 #include "ui/moduleshell.hpp"
 #include "ui/workspacecontroller.hpp"
diff --git a/src/niepce/ui/selectioncontroller.cpp b/src/niepce/ui/selectioncontroller.cpp
index 98ebc2c..9fb9d28 100644
--- a/src/niepce/ui/selectioncontroller.cpp
+++ b/src/niepce/ui/selectioncontroller.cpp
@@ -224,8 +224,8 @@ bool SelectionController::_set_metadata(const std::string & undo_label,
 
         if (!result.empty()) {
             value = result.unwrap();
-            if(value.type() != typeid(fwk::EmptyValue)) {
-                DBG_ASSERT(value.type() == iter.second.type(),
+            if(value.get_variant().type() != typeid(fwk::EmptyValue)) {
+                DBG_ASSERT(value.get_variant().type() == iter.second.get_variant().type(),
                            "Value type mismatch");
             }
         }
diff --git a/src/niepce/ui/workspacecontroller.cpp b/src/niepce/ui/workspacecontroller.cpp
index 0fe83bf..a6d637e 100644
--- a/src/niepce/ui/workspacecontroller.cpp
+++ b/src/niepce/ui/workspacecontroller.cpp
@@ -27,6 +27,7 @@
 #include "fwk/base/debug.hpp"
 #include "niepce/notifications.hpp"
 #include "engine/db/librarytypes.hpp"
+#include "engine/library/notification.hpp"
 #include "libraryclient/libraryclient.hpp"
 #include "fwk/toolkit/application.hpp"
 #include "niepcewindow.hpp"
@@ -83,57 +84,43 @@ fwk::Configuration::Ptr WorkspaceController::getLibraryConfig() const
 void WorkspaceController::on_lib_notification(const eng::LibNotification &ln)
 {
     DBG_OUT("notification for workspace");
-    switch(ln.type) {
-    case eng::LibNotification::Type::ADDED_FOLDERS:
+    switch(engine_library_notification_type(&ln)) {
+    case eng::LibNotificationType::ADDED_FOLDER:
     {
-        auto l = ln.get<eng::LibNotification::Type::ADDED_FOLDERS>().folders;
-        DBG_OUT("received added folders # %lu", l->size());
-        for_each(l->cbegin(), l->cend(),
-                 [this] (const eng::LibFolderPtr& f) {
-                     this->add_folder_item(f);
-                 });
+        auto f = engine_library_notification_get_libfolder(&ln);
+        this->add_folder_item(f);
         break;
     }
-    case eng::LibNotification::Type::ADDED_KEYWORD:
+    case eng::LibNotificationType::ADDED_KEYWORD:
     {
-        auto k = ln.get<eng::LibNotification::Type::ADDED_KEYWORD>().keyword;
-        DBG_ASSERT(static_cast<bool>(k), "keyword must not be NULL");
+        auto k = engine_library_notification_get_keyword(&ln);
+        DBG_ASSERT(k, "keyword must not be NULL");
         add_keyword_item(k);
         break;
     }
-    case eng::LibNotification::Type::ADDED_KEYWORDS:
+    case eng::LibNotificationType::FOLDER_COUNTED:
     {
-        auto l = ln.get<eng::LibNotification::Type::ADDED_KEYWORDS>().keywords;
-        DBG_ASSERT(static_cast<bool>(l), "keyword list must not be NULL");
-        for_each(l->cbegin(), l->cend(),
-                 [this] (const eng::KeywordPtr& k) {
-                     this->add_keyword_item(k);
-                 });
-        break;
-    }
-    case eng::LibNotification::Type::FOLDER_COUNTED:
-    {
-        auto count = ln.get<eng::LibNotification::Type::FOLDER_COUNTED>();
-        DBG_OUT("count for folder %Ld is %d", (long long)count.folder, count.count);
+        auto count = engine_library_notification_get_folder_count(&ln);
+        DBG_OUT("count for folder %Ld is %d", (long long)count->folder, count->count);
         std::map<eng::library_id_t, Gtk::TreeIter>::const_iterator iter
-            = m_folderidmap.find(count.folder);
+            = m_folderidmap.find(count->folder);
         if(iter != m_folderidmap.cend()) {
             Gtk::TreeRow row = *(iter->second);
-            row[m_librarycolumns.m_count_n] = count.count;
-            row[m_librarycolumns.m_count] = std::to_string(count.count);
+            row[m_librarycolumns.m_count_n] = count->count;
+            row[m_librarycolumns.m_count] = std::to_string(count->count);
         }
 
         break;
     }
-    case eng::LibNotification::Type::FOLDER_COUNT_CHANGE:
+    case eng::LibNotificationType::FOLDER_COUNT_CHANGE:
     {
-        auto count = ln.get<eng::LibNotification::Type::FOLDER_COUNT_CHANGE>();
-        DBG_OUT("count change for folder %Ld is %d", (long long)count.folder, count.count);
+        auto count = engine_library_notification_get_folder_count(&ln);
+        DBG_OUT("count change for folder %Ld is %d", (long long)count->folder, count->count);
         std::map<eng::library_id_t, Gtk::TreeIter>::const_iterator iter
-            = m_folderidmap.find(count.folder);
+            = m_folderidmap.find(count->folder);
         if(iter != m_folderidmap.cend()) {
             Gtk::TreeRow row = *(iter->second);
-            int new_count = row[m_librarycolumns.m_count_n] + count.count;
+            int new_count = row[m_librarycolumns.m_count_n] + count->count;
             row[m_librarycolumns.m_count_n] = new_count;
             row[m_librarycolumns.m_count] = std::to_string(new_count);
         }
@@ -209,39 +196,39 @@ void WorkspaceController::on_row_collapsed(const Gtk::TreeIter& iter,
 }
 
 
-void WorkspaceController::add_keyword_item(const eng::KeywordPtr & k)
+void WorkspaceController::add_keyword_item(const eng::Keyword* k)
 {
     auto children = m_keywordsNode->children();
     bool was_empty = children.empty();
     auto iter = add_item(m_treestore, children,
                          m_icons[ICON_KEYWORD],
-                         engine_db_keyword_keyword(k.get()),
-                         engine_db_keyword_id(k.get()), KEYWORD_ITEM);
+                         engine_db_keyword_keyword(k),
+                         engine_db_keyword_id(k), KEYWORD_ITEM);
 //             getLibraryClient()->countKeyword(f->id());
-    m_keywordsidmap[engine_db_keyword_id(k.get())] = iter;
+    m_keywordsidmap[engine_db_keyword_id(k)] = iter;
     if(was_empty) {
         expand_from_cfg("workspace_keywords_expanded", m_keywordsNode);
     }
 }
 
-void WorkspaceController::add_folder_item(const eng::LibFolderPtr & f)
+void WorkspaceController::add_folder_item(const eng::LibFolder* f)
 {
     int icon_idx = ICON_ROLL;
-    if(engine_db_libfolder_virtual_type(f.get()) == (int32_t)eng::LibFolderVirtualType::TRASH) {
+    if(engine_db_libfolder_virtual_type(f) == (int32_t)eng::LibFolderVirtualType::TRASH) {
         icon_idx = ICON_TRASH;
-        getLibraryClient()->set_trash_id(engine_db_libfolder_id(f.get()));
+        getLibraryClient()->set_trash_id(engine_db_libfolder_id(f));
     }
     auto children = m_folderNode->children();
     bool was_empty = children.empty();
     auto iter = add_item(m_treestore, children,
                          m_icons[icon_idx],
-                         engine_db_libfolder_name(f.get()),
-                         engine_db_libfolder_id(f.get()), FOLDER_ITEM);
-    if(engine_db_libfolder_expanded(f.get())) {
+                         engine_db_libfolder_name(f),
+                         engine_db_libfolder_id(f), FOLDER_ITEM);
+    if(engine_db_libfolder_expanded(f)) {
         m_librarytree.expand_row(m_treestore->get_path(iter), false);
     }
-    getLibraryClient()->countFolder(engine_db_libfolder_id(f.get()));
-    m_folderidmap[engine_db_libfolder_id(f.get())] = iter;
+    getLibraryClient()->countFolder(engine_db_libfolder_id(f));
+    m_folderidmap[engine_db_libfolder_id(f)] = iter;
     // expand if needed. Because Gtk is stupid and doesn't expand empty
     if(was_empty) {
         expand_from_cfg("workspace_folders_expanded", m_folderNode);
diff --git a/src/niepce/ui/workspacecontroller.hpp b/src/niepce/ui/workspacecontroller.hpp
index 3430a6f..b2a2733 100644
--- a/src/niepce/ui/workspacecontroller.hpp
+++ b/src/niepce/ui/workspacecontroller.hpp
@@ -98,9 +98,9 @@ private:
     fwk::Configuration::Ptr getLibraryConfig() const;
 
     /** add a folder item to the treeview */
-    void add_folder_item(const eng::LibFolderPtr & f);
+    void add_folder_item(const eng::LibFolder* f);
     /** add a keyword item to the treeview */
-    void add_keyword_item(const eng::KeywordPtr & k);
+    void add_keyword_item(const eng::Keyword* k);
     /** add a tree item in the treeview
      * @param treestore the treestore to add to
      * @param childrens the children subtree to add to


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]