polodb_core 2.0.0

PoloDB is an embedded JSON-based database
Documentation
use std::path::{Path, PathBuf};
use std::rc::Rc;
use polodb_bson::Document;
use crate::context::DbContext;
use crate::{DbResult, Config, SerializeType, TransactionType, Database};

fn mk_old_db_path(db_path: &Path) -> PathBuf {
    let mut buf = db_path.to_path_buf();
    let filename = buf.file_name().unwrap().to_str().unwrap();
    let new_filename = String::from(filename) + ".old";
    buf.set_file_name(new_filename);
    buf
}

fn mk_new_db_path(db_path: &Path) -> PathBuf {
    let mut buf = db_path.to_path_buf();
    let filename = buf.file_name().unwrap().to_str().unwrap();
    let new_filename = String::from(filename) + ".new";
    buf.set_file_name(new_filename);
    buf
}

fn find_all(db_ctx: &mut DbContext, col_id: u32, meta_version: u32) -> DbResult<Vec<Rc<Document>>> {
    let mut result = vec![];

    let mut handle = db_ctx.find(col_id, meta_version, None)?;

    handle.step()?;

    while handle.has_row() {
        let doc = handle.get().unwrap_document();
        result.push(doc.clone());

        handle.step()?;
    }

    Ok(result)
}

fn do_transfer(db_ctx: &mut DbContext, new_db: &mut Database) -> DbResult<()> {
    let vec = db_ctx.query_all_meta()?;

    println!("size: {}", vec.len());

    for item in vec {
        println!("hello: {}", item);
        let id = item.get("_id").unwrap().unwrap_int() as u32;
        let name = item.get("name").expect("not a valid db").unwrap_string();

        let data = find_all(db_ctx, id, db_ctx.meta_version).unwrap();

        let mut new_collection = new_db.create_collection(name).unwrap();

        for item in data {
            let mut doc = item.as_ref().clone();
            new_collection.insert(&mut doc).unwrap();
        }
    }

    Ok(())
}

pub(crate) fn v1_to_v2(path: &Path) -> DbResult<()> {
    let new_db_path = mk_new_db_path(path);
    let old_db_path = mk_old_db_path(path);

    let result = {
        let mut new_db = Database::open_file(&new_db_path)?;

        new_db.start_transaction(Some(TransactionType::Write))?;

        let mut config = Config::default();
        config.serialize_type = SerializeType::Legacy;
        let mut db_ctx= crate::context::DbContext::open_file(&path, config)?;

        db_ctx.start_transaction(Some(TransactionType::Read))?;

        let result = do_transfer(&mut db_ctx, &mut new_db);

        new_db.commit()?;
        db_ctx.commit()?;

        result
    };

    if result.is_ok()  {
        std::fs::rename(path, old_db_path)?;
        std::fs::rename(&new_db_path, path)?;
    } else {
        let _ = std::fs::remove_file(&new_db_path);
    }

    return result;
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;
    use crate::Database;
    use crate::migration::v1_to_v2;

    fn mk_db_path(db_name: &str) -> PathBuf {
        let mut db_path = std::env::temp_dir();
        let db_filename = String::from(db_name) + ".db";
        db_path.push(db_filename);
        db_path
    }

    #[test]
    fn test_meta_information() {
        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        d.pop();
        d.pop();
        d.push("fixtures/test-collection.db");

        let test_path = mk_db_path("test-migration");
        let _ = std::fs::remove_file(&test_path);

        std::fs::copy(&d, &test_path).unwrap();

        println!("path: {}", d.to_str().unwrap());

        v1_to_v2(&test_path).unwrap();

        let mut new_db = Database::open_file(&test_path).unwrap();
        let meta = new_db.query_all_meta().unwrap();
        assert!(meta.len()> 0);
    }

}