relative-path-utils 0.3.1

Portable, relative paths for Rust.
Documentation
use std::fs;
use std::path::{Path, PathBuf};

use anyhow::Result;
use relative_path_utils::Root;

const PATH: &str = env!("CARGO_TARGET_TMPDIR");

fn make_path(path: &'static str) -> PathBuf {
    Path::new(PATH).join(path)
}

fn root(path: &'static str) -> Root {
    match Root::new(make_path(path)) {
        Ok(root) => root,
        Err(error) => panic!("Failed to open root: {path}: {error}"),
    }
}

fn files(list: &[(&'static str, Option<&'static str>)]) {
    for (item, content) in list {
        let path = make_path(item);

        if let Some(content) = content {
            if let Some(parent) = path.parent() {
                if let Err(error) = fs::create_dir_all(parent) {
                    panic!("Failed to create directory {}: {}", parent.display(), error);
                }
            }

            if let Err(error) = fs::write(&path, content) {
                panic!("Failed to create file {}: {}", path.display(), error);
            }
        } else if let Err(error) = fs::create_dir_all(&path) {
            panic!("Failed to create directory {}: {}", path.display(), error);
        }
    }
}

#[test]
fn relative_open() -> Result<()> {
    files(&[("relative_open/src/root/first", Some("first content"))]);

    let r1 = root("relative_open/src/root");
    assert_eq!(r1.read_to_string("first")?, "first content");

    let r2 = root("relative_open/src");
    assert_eq!(r2.read_to_string("root/first")?, "first content");
    Ok(())
}

#[test]
fn read_dir() -> Result<()> {
    files(&[("read_dir/src/root/first", Some("first content"))]);
    files(&[("read_dir/src/root/second", Some("second content"))]);

    let r1 = root(".");
    let d = r1.read_dir("read_dir/src/root")?;

    let mut values = Vec::new();

    for e in d {
        let e = e?;
        values.push(e.file_name().to_string_lossy().into_owned());
    }

    values.sort();

    assert_eq!(values, vec!["first", "second"]);
    Ok(())
}

#[test]
fn glob() -> Result<()> {
    files(&[("glob/src/root/first", Some("first content"))]);
    files(&[("glob/src/root/second", Some("second content"))]);

    let r1 = root("glob");
    let glob = r1.glob("**/root/*");

    let mut results = Vec::new();

    for e in glob.matcher() {
        results.push(e?);
    }

    results.sort();
    assert_eq!(results, vec!["src/root/first", "src/root/second"]);
    Ok(())
}

#[test]
fn read_root_dir() -> Result<()> {
    files(&[("read_root_dir/first", Some("first content"))]);

    let r1 = root("read_root_dir");

    let mut values = Vec::new();

    for e in r1.read_dir("")? {
        let e = e?;
        values.push(e.file_name().to_string_lossy().into_owned());
    }

    for e in r1.read_dir("")? {
        let e = e?;
        values.push(e.file_name().to_string_lossy().into_owned());
    }

    assert_eq!(values, vec!["first", "first"]);
    Ok(())
}

#[test]
fn test_parent_dir() -> Result<()> {
    files(&[("test_parent_dir/foo/bar/first", Some("first content"))]);
    files(&[("test_parent_dir/foo/second", Some("second content"))]);

    let r1 = root("test_parent_dir");
    assert_eq!(r1.read_to_string("foo/bar/../second")?, "second content");
    assert!(r1.read_to_string("../second").is_err());
    Ok(())
}