synd-runtime 0.4.0-rc.1

Runtime session and singleton daemon lifecycle for syndicationd
use std::{path::PathBuf, process::ExitStatus};

use synd_protocol::CapabilitySet;

use thiserror::Error;

use crate::DaemonLaunchInfo;

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

#[derive(Debug, Error)]
pub enum Error {
    #[error(transparent)]
    Io(#[from] std::io::Error),

    #[error(transparent)]
    RegistryDb(#[from] synd_registry::RegistryDbError),

    #[error(transparent)]
    Migration(#[from] synd_persistence::sqlite::MigrationError),

    #[error(transparent)]
    Api(Box<synd_client::SyndApiError>),

    #[error(transparent)]
    RuntimeApi(Box<synd_api::Error>),

    #[error(transparent)]
    Json(#[from] serde_json::Error),

    #[error("{context} endpoint unavailable: {}", endpoint.display())]
    EndpointUnavailable {
        context: &'static str,
        endpoint: PathBuf,
    },

    #[error("{context} transport unsupported")]
    UnsupportedTransport { context: &'static str },

    #[error(
        "runtime daemon at {} is missing required session capabilities: {missing_capabilities}",
        endpoint.display()
    )]
    MissingSessionCapabilities {
        endpoint: PathBuf,
        missing_capabilities: CapabilitySet,
    },

    #[error("refusing to remove non-socket runtime endpoint {}", path.display())]
    NonSocketEndpoint { path: PathBuf },

    #[error(
        "daemon exited before endpoint became ready: {status}; daemon log: {}",
        launch.log().display()
    )]
    DaemonExitedBeforeReady {
        status: ExitStatus,
        launch: DaemonLaunchInfo,
    },

    #[error(
        "timed out waiting for launched daemon endpoint {} to become ready; daemon log: {}",
        endpoint.display(),
        launch.log().display()
    )]
    DaemonEndpointReadyTimeout {
        endpoint: PathBuf,
        launch: DaemonLaunchInfo,
    },

    #[error("timed out waiting for daemon endpoint {} to become ready", endpoint.display())]
    EndpointReadyTimeout { endpoint: PathBuf },

    #[error("timed out waiting for daemon endpoint {} to stop", endpoint.display())]
    EndpointStopTimeout { endpoint: PathBuf },

    #[error("daemon claim lock is already held at {}", path.display())]
    DaemonClaimLockAlreadyHeld { path: PathBuf },

    #[error("refusing force shutdown: {reason}")]
    ForceShutdownRefused { reason: String },

    #[error("timed out force stopping daemon process {pid}")]
    ForceShutdownTimeout { pid: u32 },

    #[error("runtime daemon is incompatible at {}; {suggestion}", endpoint.display())]
    IncompatibleRuntimeDaemon {
        endpoint: PathBuf,
        suggestion: String,
    },

    #[error("failed to stop incompatible runtime daemon at {}; {suggestion}", endpoint.display())]
    IncompatibleRuntimeDaemonShutdown {
        endpoint: PathBuf,
        suggestion: String,
        #[source]
        source: Box<synd_client::SyndApiError>,
    },

    #[error("timed out stopping incompatible runtime daemon at {}; {suggestion}", endpoint.display())]
    IncompatibleRuntimeDaemonStopTimeout {
        endpoint: PathBuf,
        suggestion: String,
    },
}

impl From<synd_client::SyndApiError> for Error {
    fn from(source: synd_client::SyndApiError) -> Self {
        Self::Api(Box::new(source))
    }
}

impl From<synd_api::Error> for Error {
    fn from(source: synd_api::Error) -> Self {
        Self::RuntimeApi(Box::new(source))
    }
}