use std::fmt;
pub const EXIT_OK: u8 = 0;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind {
Usage,
TooBig,
Input,
BackendUnavailable,
BackendFailed,
#[allow(dead_code)]
NoTty,
PasteUnsupported,
}
impl ErrorKind {
pub const fn code(self) -> u8 {
match self {
ErrorKind::Usage => 1,
ErrorKind::TooBig => 3,
ErrorKind::Input => 4,
ErrorKind::BackendUnavailable => 5,
ErrorKind::BackendFailed => 6,
ErrorKind::NoTty => 7,
ErrorKind::PasteUnsupported => 8,
}
}
pub fn json_name(self) -> &'static str {
match self {
ErrorKind::Usage => "usage",
ErrorKind::TooBig => "too_big",
ErrorKind::Input => "input",
ErrorKind::BackendUnavailable => "backend_unavailable",
ErrorKind::BackendFailed => "backend_failed",
ErrorKind::NoTty => "no_tty",
ErrorKind::PasteUnsupported => "paste_unsupported",
}
}
}
#[derive(Debug, Clone)]
pub struct ClipfError {
pub kind: ErrorKind,
pub message: String,
pub bytes: Option<usize>,
}
impl ClipfError {
pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
Self {
kind,
message: message.into(),
bytes: None,
}
}
pub fn code(&self) -> u8 {
self.kind.code()
}
}
impl fmt::Display for ClipfError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for ClipfError {}
macro_rules! ctor {
($name:ident, $kind:ident) => {
impl ClipfError {
pub fn $name(message: impl Into<String>) -> Self {
Self::new(ErrorKind::$kind, message)
}
}
};
}
ctor!(usage, Usage);
ctor!(input, Input);
ctor!(backend_unavailable, BackendUnavailable);
ctor!(backend_failed, BackendFailed);
ctor!(paste_unsupported, PasteUnsupported);
impl ClipfError {
pub fn too_big(message: impl Into<String>, bytes: usize) -> Self {
Self {
kind: ErrorKind::TooBig,
message: message.into(),
bytes: Some(bytes),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn code_table_matches_the_readme() {
assert_eq!(ErrorKind::Usage.code(), 1);
assert_eq!(ErrorKind::TooBig.code(), 3);
assert_eq!(ErrorKind::Input.code(), 4);
assert_eq!(ErrorKind::BackendUnavailable.code(), 5);
assert_eq!(ErrorKind::BackendFailed.code(), 6);
assert_eq!(ErrorKind::NoTty.code(), 7);
assert_eq!(ErrorKind::PasteUnsupported.code(), 8);
}
#[test]
fn json_names_are_snake_case_and_stable() {
for (kind, name) in [
(ErrorKind::Usage, "usage"),
(ErrorKind::TooBig, "too_big"),
(ErrorKind::Input, "input"),
(ErrorKind::BackendUnavailable, "backend_unavailable"),
(ErrorKind::BackendFailed, "backend_failed"),
(ErrorKind::NoTty, "no_tty"),
(ErrorKind::PasteUnsupported, "paste_unsupported"),
] {
assert_eq!(kind.json_name(), name);
}
}
#[test]
fn display_is_the_bare_message_with_no_added_prefix() {
let e = ClipfError::input("no such file: x.txt");
assert_eq!(e.to_string(), "no such file: x.txt");
assert_eq!(e.code(), 4);
assert_eq!(e.bytes, None);
}
#[test]
fn too_big_carries_the_byte_count() {
let e = ClipfError::too_big("134 bytes exceeds the guard", 134);
assert_eq!(e.code(), 3);
assert_eq!(e.bytes, Some(134));
}
}