ruroonga 0.6.0

A Rust language binding for Groonga.
Documentation
extern crate libc;
extern crate groonga_sys as ffi;
use std::mem;
use ffi::groonga;
use super::commandapi;

#[derive(Clone, Debug)]
pub struct LibGroonga {
    disposed: bool,
}

impl LibGroonga {
    pub fn new() -> Result<LibGroonga, String> {
        let rc = commandapi::groonga_init();
        if rc != groonga::grn_rc::GRN_SUCCESS {
            return Err("Couldn't initilize Groonga.".to_string());
        }
        Ok(LibGroonga { disposed: false })
    }

    fn close(&mut self) -> Result<(), String> {
        if self.disposed {
            return Ok(());
        }
        let rc = commandapi::groonga_fin();
        if rc != groonga::grn_rc::GRN_SUCCESS {
            return Err("Couldn't finalized Groonga.".to_string());
        }
        self.disposed = true;
        Ok(())
    }
}

impl Drop for LibGroonga {
    fn drop(&mut self) {
        self.close().unwrap();
    }
}

#[derive(Clone, Debug)]
pub struct Context {
    ctx: *mut groonga::grn_ctx,
    disposed: bool,
}

impl Context {
    pub fn new() -> Result<Context, String> {
        let ctx = commandapi::groonga_ctx_open(0);
        if ctx.is_null() {
            return Err("Couldn't create Groonga Context.".to_string());
        }
        Ok(Context {
            ctx: ctx,
            disposed: false,
        })
    }

    pub fn close(&mut self) -> Result<(), String> {
        if self.disposed {
            return Ok(());
        }
        let rc = commandapi::groonga_ctx_close(self.ctx);
        if rc != groonga::grn_rc::GRN_SUCCESS {
            return Err("Couldn't dispose Groonga Context.".to_string());
        }
        unsafe {
            self.ctx = mem::zeroed();
        }
        self.disposed = true;
        Ok(())
    }
}

#[derive(Clone, Debug)]
pub struct Database {
    ctx: *mut groonga::grn_ctx,
    disposed: bool,
}

impl Database {
    pub fn new(ctx: Context) -> Database {
        Database {
            ctx: ctx.ctx,
            disposed: false,
        }
    }

    pub fn uses(&mut self, dbpath: &str) {
        let _ = commandapi::groonga_db_use(self.ctx, dbpath);
    }

    fn close(&mut self) {
        if self.disposed {
            return;
        }
        unsafe {
            self.ctx = mem::zeroed();
        }
        self.disposed = true;
    }
}

impl Drop for Database {
    fn drop(&mut self) {
        self.close()
    }
}

#[derive(Clone, Debug)]
pub struct Command {
    ctx: *mut groonga::grn_ctx,
    disposed: bool,
}

impl Command {
    pub fn new(ctx: Context) -> Command {
        Command {
            ctx: ctx.ctx,
            disposed: false,
        }
    }

    pub fn groonga_version<'a>() -> &'a str {
        commandapi::get_groonga_version()
    }

    pub fn execute(&mut self, command: &str) -> String {
        let result = commandapi::groonga_execute_command(self.ctx, command);
        match result {
            Ok(v) => v,
            Err(_) => "".to_string(),
        }
    }

    fn close(&mut self) {
        if self.disposed {
            return;
        }
        unsafe {
            self.ctx = mem::zeroed();
        }
        self.disposed = true;
    }
}

impl Drop for Command {
    fn drop(&mut self) {
        self.close()
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    use super::*;
    use self::tempdir::TempDir;

    fn setup_dbpath() -> String {
        let dbdir = TempDir::new("ruroonga");
        let dbname = "test.db";
        let mut dbpath_buf = dbdir.unwrap().into_path();
        dbpath_buf.push(dbname);
        let dbpath = match dbpath_buf.to_str() {
            Some(v) => v,
            None => panic!("Could not get temporary database path."),
        };

        dbpath.to_owned()
    }

    #[test]
    fn integration_test() {
        let groonga = LibGroonga::new();
        assert!(groonga.is_ok());

        let context = Context::new();
        assert!(context.is_ok());

        let ctx = match context {
            Ok(v) => v,
            Err(_) => panic!("Could not get context."),
        };

        let dbpath = setup_dbpath();
        let _ = Database::new(ctx.clone()).uses(&*dbpath);

        let grn_command = "table_create Users TABLE_HASH_KEY ShortText";
        let mut command = Command::new(ctx.clone());
        let table_create_result = command.execute(grn_command.clone());
        assert_eq!("true", table_create_result);

        let dump = "dump";
        let result = command.execute(dump.clone());
        let dumped_command = "table_create Users TABLE_HASH_KEY ShortText\n";
        assert!(dumped_command == result || format!("{}\u{fffd}\u{7f}", dumped_command) == result);
    }
}