treeboot-core 0.8.0

Reusable worktree bootstrap engine for the treeboot CLI.
Documentation
use std::path::PathBuf;
use std::process::ExitStatus;

/// Error type for `treeboot-core` operations.
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// The current working directory could not be read.
    #[error("failed to read current directory: {source}")]
    CurrentDir {
        /// Source I/O error.
        #[source]
        source: std::io::Error,
    },

    /// A filesystem path could not be normalized.
    #[error("failed to normalize path {path:?}: {source}")]
    NormalizePath {
        /// Path that failed normalization.
        path: PathBuf,
        /// Source I/O error.
        #[source]
        source: std::io::Error,
    },

    /// The command was not run from a Git worktree.
    #[error("not inside a Git worktree")]
    NotGitWorktree,

    /// A Git command could not be spawned.
    #[error("failed to run {command}: {source}")]
    GitIo {
        /// Human-readable command label.
        command: String,
        /// Source I/O error.
        #[source]
        source: std::io::Error,
    },

    /// A Git command exited unsuccessfully.
    #[error("{command} failed: {stderr}")]
    GitFailed {
        /// Human-readable command label.
        command: String,
        /// Standard error from Git.
        stderr: String,
    },

    /// No root checkout path could be determined.
    #[error("could not determine root checkout path")]
    RootPathNotFound,

    /// A specifically requested config file does not exist.
    #[error("config file not found: {0:?}")]
    ConfigNotFound(PathBuf),

    /// A config file could not be read.
    #[error("failed to read config {path:?}: {source}")]
    ConfigIo {
        /// Config file path.
        path: PathBuf,
        /// Source I/O error.
        #[source]
        source: std::io::Error,
    },

    /// A config file contains invalid TOML.
    #[error("invalid config {path:?}: {message}")]
    ConfigParse {
        /// Config file path.
        path: PathBuf,
        /// Parse error message.
        message: String,
    },

    /// A config file contains unsupported or invalid declarations.
    #[error("invalid config {path:?}: {message}")]
    ConfigInvalid {
        /// Config file path.
        path: PathBuf,
        /// Validation error message.
        message: String,
    },

    /// A treeboot boolean environment variable has an unsupported value.
    #[error(
        "invalid boolean environment value for {name}: {value:?} \
         (expected one of 1, true, yes, on, 0, false, no, off)"
    )]
    InvalidBooleanEnv {
        /// Environment variable name.
        name: &'static str,
        /// Environment variable value.
        value: String,
    },

    /// No config was found while strict mode was enabled.
    #[error("no config detected")]
    NoConfigDetectedStrict,

    /// The command was run from the root checkout while strict mode was enabled.
    #[error("This is not a work tree")]
    RootWorktreeStrict,

    /// A configured command could not be spawned.
    #[error("failed to run command {label}: {source}")]
    CommandIo {
        /// Human-readable command label.
        label: String,
        /// Source I/O error.
        #[source]
        source: std::io::Error,
    },

    /// A configured command exited unsuccessfully.
    #[error("command {label} failed with {status}")]
    CommandFailed {
        /// Human-readable command label.
        label: String,
        /// Process exit status.
        status: ExitStatus,
    },

    /// A file operation encountered an unsupported conflict.
    #[error("{operation} file operation cannot use {path:?}: {message}")]
    FileOperationConflict {
        /// Operation being applied.
        operation: &'static str,
        /// Path involved in the conflict.
        path: PathBuf,
        /// Human-readable conflict detail.
        message: String,
    },

    /// A manual file operation request is invalid.
    #[error("invalid {operation} file operation: {message}")]
    FileOperationInvalid {
        /// Operation being planned.
        operation: &'static str,
        /// Human-readable validation detail.
        message: String,
    },

    /// A file operation failed while accessing the filesystem.
    #[error("{operation} file operation failed at {path:?}: {source}")]
    FileOperationIo {
        /// Operation being applied.
        operation: &'static str,
        /// Path being accessed.
        path: PathBuf,
        /// Source I/O error.
        #[source]
        source: std::io::Error,
    },

    /// An init script could not be spawned.
    #[error("failed to run init script {path:?}: {source}")]
    ScriptIo {
        /// Script path.
        path: PathBuf,
        /// Source I/O error.
        #[source]
        source: std::io::Error,
    },

    /// An init script exited unsuccessfully.
    #[error("init script {path:?} failed with {status}")]
    ScriptFailed {
        /// Script path.
        path: PathBuf,
        /// Script process status.
        status: ExitStatus,
    },

    /// Writing output failed.
    #[error("failed to write output: {source}")]
    Output {
        /// Source I/O error.
        #[source]
        source: std::io::Error,
    },

    /// Doctor diagnostics found fatal issues.
    #[error("doctor found fatal issues")]
    DoctorFailed,

    /// `treeboot init` refused to replace an existing target.
    #[error("init target already exists: {0:?}")]
    InitTargetExists(PathBuf),

    /// An init file could not be written.
    #[error("failed to write init target {path:?}: {source}")]
    InitIo {
        /// Init target path.
        path: PathBuf,
        /// Source I/O error.
        #[source]
        source: std::io::Error,
    },
}

impl Error {
    /// Returns the CLI exit code associated with this error.
    #[must_use]
    pub const fn exit_code(&self) -> u8 {
        1
    }
}