semora-cli 0.7.0

Semora CLI: publish Markdown and HTML, and hear back from the page
Documentation
//! Exit codes. `wait` defines them in the spec; every command uses the same ones.

use std::fmt;

pub const OK: i32 = 0;
pub const ERROR: i32 = 1;
pub const TIMEOUT: i32 = 2;
pub const AUTH: i32 = 3;
pub const NOT_FOUND: i32 = 4;
/// A push refused because the server has a revision this checkout didn't start from.
pub const STALE: i32 = 5;

/// An error that carries its exit code.
#[derive(Debug)]
pub struct Exit {
    pub code: i32,
    pub message: String,
}

impl fmt::Display for Exit {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.message)
    }
}

impl std::error::Error for Exit {}

pub fn auth(message: impl Into<String>) -> anyhow::Error {
    Exit {
        code: AUTH,
        message: message.into(),
    }
    .into()
}

pub fn not_found(message: impl Into<String>) -> anyhow::Error {
    Exit {
        code: NOT_FOUND,
        message: message.into(),
    }
    .into()
}

pub fn timeout(message: impl Into<String>) -> anyhow::Error {
    Exit {
        code: TIMEOUT,
        message: message.into(),
    }
    .into()
}

pub fn stale(message: impl Into<String>) -> anyhow::Error {
    Exit {
        code: STALE,
        message: message.into(),
    }
    .into()
}

/// The exit code an error maps to.
pub fn code_of(err: &anyhow::Error) -> i32 {
    err.downcast_ref::<Exit>().map(|e| e.code).unwrap_or(ERROR)
}

pub fn is_not_found(err: &anyhow::Error) -> bool {
    code_of(err) == NOT_FOUND
}

pub fn is_stale(err: &anyhow::Error) -> bool {
    code_of(err) == STALE
}

/// The message of an [`Exit`], or the whole error.
pub fn message_of(err: &anyhow::Error) -> String {
    match err.downcast_ref::<Exit>() {
        Some(x) => x.message.clone(),
        None => format!("{err:#}"),
    }
}

/// The server answered 409.
#[derive(Debug)]
pub struct Conflict(pub String);

impl fmt::Display for Conflict {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.0)
    }
}

impl std::error::Error for Conflict {}

pub fn is_conflict(err: &anyhow::Error) -> bool {
    err.downcast_ref::<Conflict>().is_some()
}