urma-node 0.6.0

Optional URMA archive node: Litecoin follower, record archive, libp2p overlay and loopback service
Documentation
use crate::{Error as NodeError, platform::Paths};
use std::{fmt, io, path::Path};

#[derive(Debug)]
pub enum Error {
    NoInit,
    Failed(String),
    Io(io::Error),
    Json(serde_json::Error),
    Command {
        program: String,
        args: Vec<String>,
        detail: String,
    },
    NotInstalled,
    Account(String),
    Node(NodeError),
    #[cfg(windows)]
    Windows {
        call: &'static str,
        error: windows_service::Error,
    },
}

impl Error {
    fn no_init(f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        let paths = Paths::linux_under(Path::new("/"));
        write!(
            f,
            "No supported init system found (systemd, OpenRC, runit). Configure your service \
             manager to run `urma-node service run` as user urma-node.\n  \
             binaries: /usr/bin/urma-node, /usr/bin/urma-node-control, /usr/bin/urma-node-app\n  \
             service settings: {}\n  archive config: {}\n  data: {}\n  \
             token: {} (0640, urma-node:urma-node)\n  peer key: {} (0600)",
            paths.settings.display(),
            paths.config.display(),
            paths.data.display(),
            paths.token.display(),
            paths.peer_key.display(),
        )
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        match self {
            Self::NoInit => Self::no_init(f),
            Self::Failed(message) => f.write_str(message),
            Self::Io(error) => write!(f, "service I/O: {error}"),
            Self::Json(error) => write!(f, "service status encoding: {error}"),
            Self::Command {
                program,
                args,
                detail,
            } => write!(f, "`{program} {}` failed: {detail}", args.join(" ")),
            Self::NotInstalled => {
                f.write_str("the service is not installed; run `urma-node service install`")
            }
            Self::Account(message) => write!(f, "service account: {message}"),
            Self::Node(error) => write!(f, "service defaults: {error}"),
            #[cfg(windows)]
            Self::Windows { call, error } => match error {
                windows_service::Error::Winapi(cause) => {
                    write!(f, "Windows service manager: {call}: {cause}")
                }
                other => write!(f, "Windows service manager: {call}: {other}"),
            },
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::Io(error) => Some(error),
            Self::Json(error) => Some(error),
            Self::Node(error) => Some(error),
            #[cfg(windows)]
            Self::Windows { error, .. } => Some(error),
            _ => None,
        }
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Self::Io(error)
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        Self::Json(error)
    }
}

impl From<NodeError> for Error {
    fn from(error: NodeError) -> Self {
        Self::Node(error)
    }
}