liteboxfs 0.2.0

A modern POSIX filesystem in a SQLite database
Documentation
mod testing;

use std::io::{Read, Seek, SeekFrom, Write};

use xpct::{be_err, be_ok, eq_diff, equal, expect, match_pattern, pattern};

use liteboxfs::{
    Connection, CreateOptions, Error, FileBy, FileOrigin,
    metadata::{FileKind, Owner},
};
use testing::{Case, random_string};

#[test]
fn setting_file_to_current_length_is_nop() -> liteboxfs::Result<()> {
    let mut conn = Connection::open_in_memory(&CreateOptions::new())?;

    let buf = random_string(1024, Case::Lower);
    let len = buf.len() as u64;

    conn.exec(|fs| {
        let mut file = fs.create("test.txt", FileKind::Regular, Owner::ROOT)?;

        file.write_all(buf.as_bytes())?;
        file.set_len(len)?;
        expect!(file.len()).to(be_ok()).to(equal(len));

        let mut actual = String::with_capacity(buf.len());

        file.seek(SeekFrom::Start(0))?;
        file.read_to_string(&mut actual)?;
        expect!(actual).to(eq_diff(buf));

        liteboxfs::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn setting_file_to_smaller_length_truncates() -> liteboxfs::Result<()> {
    let mut conn = Connection::open_in_memory(&CreateOptions::new())?;

    let buf = random_string(1024, Case::Lower);
    let len = buf.len() as u64;
    let new_len = len / 2;

    conn.exec(|fs| {
        let mut file = fs.create("test.txt", FileKind::Regular, Owner::ROOT)?;

        file.write_all(buf.as_bytes())?;
        file.set_len(new_len)?;
        expect!(file.len()).to(be_ok()).to(equal(new_len));
        expect!(file.stream_position())
            .to(be_ok())
            .to(equal(new_len));

        let mut actual = String::with_capacity(buf.len());

        file.seek(SeekFrom::Start(0))?;
        file.read_to_string(&mut actual)?;
        expect!(actual).to(eq_diff(&buf[..new_len as usize]));

        liteboxfs::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn setting_file_to_greater_length_creates_hole() -> liteboxfs::Result<()> {
    let mut conn = Connection::open_in_memory(&CreateOptions::new())?;

    let buf = random_string(1024, Case::Lower);
    let len = buf.len() as u64;
    let new_len = len * 2;

    conn.exec(|fs| {
        let mut file = fs.create("test.txt", FileKind::Regular, Owner::ROOT)?;

        file.write_all(buf.as_bytes())?;
        file.set_len(new_len)?;
        expect!(file.len()).to(be_ok()).to(equal(new_len));
        expect!(file.stream_position()).to(be_ok()).to(equal(len));

        let mut actual = String::with_capacity(new_len as usize);
        let expected = [buf.as_bytes(), &vec![0u8; len as usize]].concat();

        file.seek(SeekFrom::Start(0))?;
        file.read_to_string(&mut actual)?;
        expect!(actual.as_bytes()).to(equal(expected));

        liteboxfs::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn len_on_directory_returns_not_a_regular_file() -> liteboxfs::Result<()> {
    let mut conn = Connection::open_in_memory(&CreateOptions::new())?;

    conn.exec(|fs| {
        let mut file = fs.create("dir", FileKind::Dir, Owner::ROOT)?;

        expect!(file.len()).to(be_err()).to(match_pattern(pattern!(
            Error::NotARegularFile { file: FileOrigin::Litebox { locator, .. }, .. } if locator == &FileBy::from(file.file_id())
        )));

        liteboxfs::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn is_empty_on_directory_returns_not_a_regular_file() -> liteboxfs::Result<()> {
    let mut conn = Connection::open_in_memory(&CreateOptions::new())?;

    conn.exec(|fs| {
        let mut file = fs.create("dir", FileKind::Dir, Owner::ROOT)?;

        expect!(file.is_empty())
            .to(be_err())
            .to(match_pattern(pattern!(
                Error::NotARegularFile { file: FileOrigin::Litebox { locator, .. }, .. } if locator == &FileBy::from(file.file_id())
            )));

        liteboxfs::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn set_len_on_directory_returns_not_a_regular_file() -> liteboxfs::Result<()> {
    let mut conn = Connection::open_in_memory(&CreateOptions::new())?;

    conn.exec(|fs| {
        let mut file = fs.create("dir", FileKind::Dir, Owner::ROOT)?;

        expect!(file.set_len(100)).to(be_err()).to(match_pattern(
            pattern!(Error::NotARegularFile { file: FileOrigin::Litebox { locator, .. }, .. } if locator == &FileBy::from(file.file_id())),
        ));

        liteboxfs::Result::Ok(())
    })?;

    Ok(())
}