bijux-dag-app 0.4.1

Application orchestration and response-shaping layer for Bijux DAG command surfaces.
Documentation
use crate::ExitCode;
use std::path::Path;

fn has_unsafe_component(path: &Path) -> bool {
    path.components().any(|component| matches!(component, std::path::Component::ParentDir))
}

pub(crate) fn require_safe_path(path: &Path) -> Result<(), ExitCode> {
    if has_unsafe_component(path) {
        return Err(ExitCode::from(2));
    }
    Ok(())
}

pub(crate) fn require_existing_path(path: &Path) -> Result<(), ExitCode> {
    require_safe_path(path)?;
    if path.exists() {
        Ok(())
    } else {
        Err(ExitCode::from(3))
    }
}

pub(crate) fn require_file(path: &Path) -> Result<(), ExitCode> {
    require_safe_path(path)?;
    if path.is_file() {
        Ok(())
    } else {
        Err(ExitCode::from(3))
    }
}

pub(crate) fn require_directory(path: &Path) -> Result<(), ExitCode> {
    require_safe_path(path)?;
    if path.is_dir() {
        Ok(())
    } else {
        Err(ExitCode::from(3))
    }
}

pub(crate) fn require_run_directory(path: &Path) -> Result<(), ExitCode> {
    require_directory(path)?;
    if path.join("manifest.json").is_file() {
        Ok(())
    } else {
        Err(ExitCode::from(3))
    }
}

#[cfg(test)]
mod tests {
    use super::{
        require_directory, require_existing_path, require_file, require_run_directory,
        require_safe_path,
    };
    use std::path::Path;

    #[test]
    fn require_existing_path_rejects_missing() {
        let missing = std::path::Path::new("/definitely-missing-path-for-bijux");
        assert!(require_existing_path(missing).is_err());
    }

    #[test]
    fn require_file_rejects_directory() {
        let tmp = tempfile::tempdir().expect("tempdir");
        assert!(require_file(tmp.path()).is_err());
    }

    #[test]
    fn require_safe_path_rejects_parent_traversal() {
        let path = Path::new("../outside");
        assert!(require_safe_path(path).is_err());
    }

    #[test]
    fn require_directory_rejects_files() {
        let tmp = tempfile::tempdir().expect("tmp");
        let file = tmp.path().join("file.txt");
        std::fs::write(&file, b"x").expect("write");
        assert!(require_directory(&file).is_err());
    }

    #[test]
    fn require_run_directory_rejects_missing_manifest() {
        let tmp = tempfile::tempdir().expect("tmp");
        assert!(require_run_directory(tmp.path()).is_err());
    }
}