kcode-rust-libs 0.2.3

Manage, validate, document, and publish small agent-authored Rust crates
Documentation
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::path::PathBuf;

use crate::CheckResult;

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

/// An error that prevented a Rust library operation, validation run, or publication.
#[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),
    VersionMismatch {
        version_file: String,
        cargo_manifest: 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::VersionMismatch {
                version_file,
                cargo_manifest,
            } => write!(
                f,
                "version {version_file} does not match Cargo.toml package version {cargo_manifest}"
            ),
            Self::InvalidRegistryToken => f.write_str("crates.io registry token is empty"),
            Self::CheckFailed(result) => {
                if let Some(stage) = result.failure() {
                    write!(
                        f,
                        "Rust library cannot be published because its {} check failed",
                        stage.stage
                    )
                } else {
                    f.write_str(
                        "Rust library cannot be published because its full check did not pass",
                    )
                }
            }
            Self::Io {
                action,
                path,
                source,
            } => write!(f, "failed to {action} {}: {source}", path.display()),
            Self::Sandbox { stage, message } => {
                write!(f, "Podman failed during {stage}: {message}")
            }
            Self::Publish(message) => write!(f, "crates.io publication failed: {message}"),
        }
    }
}

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