archivist 0.4.4

Store files in a time or index based directory hierarchy, automatically deleting the oldest files if the size limit is reached
Documentation
use anyhow::Result;
use archivist::{
    store::GzStore,
    tree::{CountingTree, DateTimeDirTree},
    Archivist,
};
use chrono::Local;
use std::{
    fs,
    io::Cursor,
    path::Path,
    sync::{Arc, Once},
};
use tempdir::TempDir;

const DATA_DIR: &str = "./tests/data";
static INIT: Once = Once::new();

fn setup() {
    INIT.call_once(|| {
        tracing_subscriber::fmt::init();
    });
}

#[tokio::test(flavor = "multi_thread")]
async fn add_raw() -> Result<()> {
    setup();
    let image_path = Path::new(DATA_DIR).join("words.txt");
    let dir = TempDir::new("archivist").expect("Create tmp dir");
    let a = Archivist::new(dir.path().to_str().unwrap(), DateTimeDirTree::default(), 10).await?;
    let s = fs::read_to_string(image_path.clone()).unwrap();

    for _ in 0..100 {
        let res = a.add("string.txt", &s).await;
        assert!(res.is_ok(), "{:?}", res);
    }

    Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn multitasking() -> Result<()> {
    setup();
    let image_path = Path::new(DATA_DIR).join("words.txt");
    let dir = TempDir::new("archivist").expect("Create tmp dir");
    let a = Arc::new(
        Archivist::new(dir.path().to_str().unwrap(), DateTimeDirTree::default(), 10).await?,
    );
    let s = fs::read_to_string(image_path.clone()).unwrap();

    let t1 = tokio::spawn({
        let a = a.clone();
        let s = s.clone();
        async move {
            for _ in 0..100 {
                let res = a.add("string.txt", &s).await;
                assert!(res.is_ok(), "{:?}", res);
            }
        }
    });

    let t2 = tokio::spawn(async move {
        for _ in 0..100 {
            let res = a.add("string.txt", &s).await;
            assert!(res.is_ok(), "{:?}", res);
        }
    });

    tokio::try_join!(t1, t2)?;

    Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn add_raw_same_timestamp() -> Result<()> {
    setup();
    let image_path = Path::new(DATA_DIR).join("words.txt");
    let dir = TempDir::new("archivist").expect("Create tmp dir");
    let a = Archivist::new(dir.path().to_str().unwrap(), DateTimeDirTree::default(), 10).await?;
    let s = fs::read_to_string(image_path.clone()).unwrap();

    // add 10 groups of files
    for _ in 0..10 {
        a.tree().await.set_timestamp(Local::now());
        // add 10 files with the same timestamp but different suffix to group files together.
        for i in 0..10 {
            let res = a.add(&format!("string_{i}.txt"), &s).await;
            assert!(res.is_ok(), "{:?}", res);
        }
    }

    Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn add_raw_counting_tree() -> Result<()> {
    setup();
    let image_path = Path::new(DATA_DIR).join("words.txt");
    let dir = TempDir::new("archivist").expect("Create tmp dir");
    let a = Archivist::new(dir.path().to_str().unwrap(), CountingTree::new(10), 10).await?;
    let s = fs::read_to_string(image_path.clone()).unwrap();

    let ids = &["XXX", "YYY", "ZZZ"];

    for (_, id) in (0..100).zip(ids.iter().cycle()) {
        a.tree().await.id = id.to_string();
        let res = a.add("_string.txt", &s).await;
        assert!(res.is_ok(), "{:?}", res);
    }

    Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn add_gz() -> Result<()> {
    setup();
    let image_path = Path::new(DATA_DIR).join("words.txt");
    let dir = TempDir::new("archivist").expect("Create tmp dir");
    let a = Archivist::new(dir.path().to_str().unwrap(), DateTimeDirTree::default(), 10).await?;
    let s = fs::read_to_string(image_path.clone()).unwrap();

    for _ in 0..100 {
        let ss = GzStore::new(Cursor::new(s.clone()));
        let res = a.add("words.txt.gz", &ss).await;
        assert!(res.is_ok(), "{:?}", res);
    }

    Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn add_image() -> Result<()> {
    setup();
    let image_path = Path::new(DATA_DIR).join("lena.tiff");
    let dir = TempDir::new("archivist").expect("Create tmp dir");
    let a = Archivist::new(dir.path().to_str().unwrap(), DateTimeDirTree::default(), 10).await?;
    let image = image::open(image_path).unwrap();

    for _ in 0..100 {
        let res = a.add("image.jpg", &image).await;
        assert!(res.is_ok(), "{:?}", res);
    }

    Ok(())
}