vv-agent 0.4.8

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use std::path::{Path, PathBuf};

pub(crate) fn resolve_workspace_path_checked(
    workspace: &Path,
    raw_path: &str,
    allow_outside_workspace_paths: bool,
) -> Result<PathBuf, String> {
    let base = workspace
        .canonicalize()
        .unwrap_or_else(|_| absolutize_without_canonicalizing(workspace));
    let candidate = crate::workspace::expand_home_path(raw_path);
    let target = if candidate.is_absolute() {
        absolutize_without_canonicalizing(&candidate)
    } else {
        absolutize_without_canonicalizing(&base.join(&candidate))
    };
    let normalized = normalize_path(target);
    if !allow_outside_workspace_paths && normalized != base && !normalized.starts_with(&base) {
        return Err(format!("Path escapes workspace: {raw_path}"));
    }
    Ok(normalized)
}

fn absolutize_without_canonicalizing(path: &Path) -> PathBuf {
    if path.is_absolute() {
        path.to_path_buf()
    } else {
        std::env::current_dir()
            .unwrap_or_else(|_| PathBuf::from("."))
            .join(path)
    }
}

fn normalize_path(path: PathBuf) -> PathBuf {
    let mut normalized = PathBuf::new();
    for component in path.components() {
        match component {
            std::path::Component::CurDir => {}
            std::path::Component::ParentDir => {
                normalized.pop();
            }
            other => normalized.push(other.as_os_str()),
        }
    }
    normalized
}