semora-cli 0.4.1

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;

/// 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()
}

/// 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
}

/// 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()
}