kcode-rust-libs-v2 0.1.0

Manage, validate, and publish complete agent-authored Rust library snapshots
Documentation
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::path::PathBuf;

/// The result type returned by this crate.
pub type Result<T> = std::result::Result<T, Error>;

/// A canonical UTF-8 path relative to a managed Rust-library root.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RustLibPath(String);

impl RustLibPath {
    /// Validates and constructs a managed-library path.
    pub fn new(path: impl Into<String>) -> Result<Self> {
        let path = path.into();
        validate_path(&path)?;
        Ok(Self(path))
    }

    /// Returns the canonical `/`-separated representation.
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for RustLibPath {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.0)
    }
}

fn validate_path(path: &str) -> Result<()> {
    let invalid = |reason| Error::InvalidRustLibPath {
        path: path.to_owned(),
        reason,
    };
    if path.is_empty() {
        return Err(invalid("path is empty"));
    }
    if path.starts_with('/') || path.ends_with('/') {
        return Err(invalid("path must not begin or end with '/'"));
    }
    if path.contains('\\') {
        return Err(invalid("backslashes are not allowed"));
    }
    if path.contains(':') {
        return Err(invalid("colons are not allowed"));
    }
    if path.contains('\0') {
        return Err(invalid("NUL is not allowed"));
    }
    if path
        .split('/')
        .any(|component| component.is_empty() || component == "." || component == "..")
    {
        return Err(invalid("empty, '.' and '..' components are not allowed"));
    }
    Ok(())
}

/// One complete UTF-8 file in a managed Rust library.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RustLibFile {
    pub path: RustLibPath,
    pub contents: String,
}

impl RustLibFile {
    pub fn new(path: RustLibPath, contents: impl Into<String>) -> Self {
        Self {
            path,
            contents: contents.into(),
        }
    }
}

/// One fixed stage in the managed validation pipeline.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CheckStage {
    Fetch,
    Format,
    Build,
    Clippy,
    Test,
    DocTest,
}

impl CheckStage {
    pub fn name(self) -> &'static str {
        match self {
            Self::Fetch => "fetch",
            Self::Format => "format",
            Self::Build => "build",
            Self::Clippy => "clippy",
            Self::Test => "test",
            Self::DocTest => "doc-test",
        }
    }
}

/// Captured output from one completed validation stage.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CheckStageResult {
    pub stage: CheckStage,
    pub command: String,
    pub exit_code: Option<i32>,
    pub stdout: String,
    pub stderr: String,
}

impl CheckStageResult {
    pub fn passed(&self) -> bool {
        self.exit_code == Some(0)
    }
}

/// Ordered results from a managed validation run.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CheckResult {
    pub stages: Vec<CheckStageResult>,
}

impl CheckResult {
    pub fn passed(&self) -> bool {
        self.stages.len() == 6 && self.stages.iter().all(CheckStageResult::passed)
    }

    pub fn failure(&self) -> Option<&CheckStageResult> {
        self.stages.iter().find(|stage| !stage.passed())
    }
}

/// An error that prevented a managed-library operation.
#[derive(Debug)]
pub enum Error {
    InvalidRustLibName(String),
    InvalidRustLibPath {
        path: String,
        reason: &'static str,
    },
    RustLibAlreadyExists(String),
    RustLibNotFound(String),
    RustLibIsNotDirectory(String),
    DuplicateWritePath(String),
    NonUtf8Path(PathBuf),
    NonUtf8File(PathBuf),
    SymlinkNotAllowed(PathBuf),
    UnsupportedFileType(PathBuf),
    RootsOverlap {
        rust_libs_root: PathBuf,
        work_root: PathBuf,
    },
    MissingRequiredFile(&'static str),
    InvalidVersion(String),
    InvalidCargoManifest(String),
    InvalidRegistryToken,
    CheckFailed(CheckResult),
    Io {
        action: &'static str,
        path: PathBuf,
        source: io::Error,
    },
    Sandbox {
        stage: String,
        message: String,
    },
    Publish(String),
}

impl Error {
    pub(crate) fn io(action: &'static str, path: impl Into<PathBuf>, source: io::Error) -> Self {
        Self::Io {
            action,
            path: path.into(),
            source,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::InvalidRustLibName(name) => write!(
                f,
                "invalid Rust library name {name:?}; use ASCII letters, digits, '-' or '_'"
            ),
            Self::InvalidRustLibPath { path, reason } => {
                write!(f, "invalid Rust library path {path:?}: {reason}")
            }
            Self::RustLibAlreadyExists(name) => {
                write!(f, "Rust library {name:?} already exists")
            }
            Self::RustLibNotFound(name) => write!(f, "Rust library {name:?} was not found"),
            Self::RustLibIsNotDirectory(name) => {
                write!(f, "Rust library {name:?} is not a directory")
            }
            Self::DuplicateWritePath(path) => {
                write!(f, "write batch contains duplicate path {path:?}")
            }
            Self::NonUtf8Path(path) => {
                write!(
                    f,
                    "Rust library contains a non-UTF-8 path: {}",
                    path.display()
                )
            }
            Self::NonUtf8File(path) => {
                write!(f, "Rust library file is not UTF-8: {}", path.display())
            }
            Self::SymlinkNotAllowed(path) => {
                write!(
                    f,
                    "Rust library symlinks are not allowed: {}",
                    path.display()
                )
            }
            Self::UnsupportedFileType(path) => write!(
                f,
                "Rust library entry is not a regular file or directory: {}",
                path.display()
            ),
            Self::RootsOverlap {
                rust_libs_root,
                work_root,
            } => write!(
                f,
                "Rust libraries root ({}) and work root ({}) must not overlap",
                rust_libs_root.display(),
                work_root.display()
            ),
            Self::MissingRequiredFile(path) => {
                write!(f, "Rust library is missing required file {path}")
            }
            Self::InvalidVersion(value) => write!(
                f,
                "invalid Cargo.toml [package].version {value:?}; expected canonical major.minor.patch"
            ),
            Self::InvalidCargoManifest(message) => {
                write!(f, "invalid Cargo.toml: {message}")
            }
            Self::InvalidRegistryToken => f.write_str("crates.io registry token is empty"),
            Self::CheckFailed(result) => {
                if let Some(stage) = result.failure() {
                    write!(f, "Rust library failed the {} stage", stage.stage.name())
                } else {
                    f.write_str("Rust library did not complete every check stage")
                }
            }
            Self::Io {
                action,
                path,
                source,
            } => write!(f, "could not {action} {}: {source}", path.display()),
            Self::Sandbox { stage, message } => {
                write!(f, "sandbox failure during {stage}: {message}")
            }
            Self::Publish(message) => write!(f, "publication failed: {message}"),
        }
    }
}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match self {
            Self::Io { source, .. } => Some(source),
            _ => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{CheckResult, CheckStage, CheckStageResult, RustLibPath};

    #[test]
    fn validates_canonical_relative_paths() {
        assert_eq!(
            RustLibPath::new("src/lib.rs").unwrap().as_str(),
            "src/lib.rs"
        );
        for path in ["", "/root", "tail/", "a//b", ".", "../x", "a\\b", "C:x"] {
            assert!(RustLibPath::new(path).is_err(), "accepted {path:?}");
        }
    }

    #[test]
    fn a_check_passes_only_after_all_six_stages() {
        let stages = [
            CheckStage::Fetch,
            CheckStage::Format,
            CheckStage::Build,
            CheckStage::Clippy,
            CheckStage::Test,
            CheckStage::DocTest,
        ]
        .into_iter()
        .map(|stage| CheckStageResult {
            stage,
            command: stage.name().to_owned(),
            exit_code: Some(0),
            stdout: String::new(),
            stderr: String::new(),
        })
        .collect();
        assert!(CheckResult { stages }.passed());
    }
}