hdiff-update-core 0.2.0

Core library for signed, transactional HDiffPatch directory updates.
Documentation
use std::{path::PathBuf, process::ExitStatus};

use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
    #[error("I/O error at {path}: {source}")]
    IoWithPath {
        path: PathBuf,
        #[source]
        source: std::io::Error,
    },
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),
    #[error("HTTP error: {0}")]
    Http(#[from] reqwest::Error),
    #[error("unexpected HTTP status: {status}")]
    UnexpectedHttpStatus { status: u16 },
    #[error("URL error: {0}")]
    Url(#[from] url::ParseError),
    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),
    #[error("tool not found: {tool}")]
    ToolNotFound { tool: String },
    #[error("process failed: {program} {args:?}, status={status}, stderr={stderr}")]
    ProcessFailed {
        program: PathBuf,
        args: Vec<String>,
        status: ExitStatus,
        stdout: String,
        stderr: String,
    },
    #[error("SHA-256 mismatch for {path}: expected {expected}, got {actual}")]
    HashMismatch {
        path: PathBuf,
        expected: String,
        actual: String,
    },
    #[error("size mismatch for {path}: expected {expected}, got {actual}")]
    SizeMismatch {
        path: PathBuf,
        expected: u64,
        actual: u64,
    },
    #[error("download exceeded byte limit: limit={limit}, attempted={attempted}")]
    DownloadLimitExceeded { limit: u64, attempted: u64 },
    #[error(
        "insufficient disk space at {path}: required={required} bytes, available={available} bytes"
    )]
    InsufficientDiskSpace {
        path: PathBuf,
        required: u64,
        available: u64,
    },
    #[error("update manifest signature is required but missing")]
    SignatureMissing,
    #[error("update manifest signature public key is required")]
    SignaturePublicKeyMissing,
    #[error("update manifest signature verification failed")]
    SignatureVerificationFailed,
    #[error("invalid {kind}: {message}")]
    InvalidKeyMaterial { kind: String, message: String },
    #[error("unsupported patch algorithm: {0}")]
    UnsupportedAlgorithm(String),
    #[error("unexpected update version: expected {expected}, got {actual}")]
    UnexpectedVersion { expected: String, actual: String },
    #[error("invalid semantic version {value}: {message}")]
    InvalidVersion { value: String, message: String },
    #[error("target version must be newer than current: current={current}, target={target}")]
    NonUpgradeVersion { current: String, target: String },
    #[error("manifest has no platform entry for {platform}")]
    MissingPlatform { platform: String },
    #[error(
        "no matching directory delta for platform={platform}, current_version={current_version}"
    )]
    NoMatchingDelta {
        platform: String,
        current_version: String,
    },
    #[error("unsupported URL or path: {0}")]
    UnsupportedUrl(String),
    #[error("{label} must use HTTPS in production, got {scheme}")]
    InsecureTransport { label: String, scheme: String },
    #[error("{0}")]
    Message(String),
}

pub type Result<T> = std::result::Result<T, Error>;

pub(crate) fn io_path(path: impl Into<PathBuf>, source: std::io::Error) -> Error {
    Error::IoWithPath {
        path: path.into(),
        source,
    }
}