Skip to main content

vissue_core/
error.rs

1//! Matchable errors so callers do not parse [`Display`] text.
2
3use std::fmt;
4
5/// Recoverable catalog and mutation failures with a stable shape.
6#[derive(Debug)]
7pub enum Error {
8    IssueNotFound {
9        id: String,
10    },
11    ClaimConflict {
12        id: String,
13        holder: String,
14        claimed_at: Option<String>,
15    },
16    BlockerCycle {
17        blocker: String,
18        issue: String,
19    },
20    InvalidState {
21        id: String,
22        state: String,
23    },
24    Other(anyhow::Error),
25}
26
27impl fmt::Display for Error {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Error::IssueNotFound { id } => write!(f, "issue {id} not found"),
31            Error::ClaimConflict {
32                id,
33                holder,
34                claimed_at,
35            } => write!(
36                f,
37                "{id} is claimed by {holder} since {}; pass --force to take it over",
38                claimed_at.as_deref().unwrap_or("an unknown time")
39            ),
40            Error::BlockerCycle { blocker, issue } if blocker == issue => {
41                write!(f, "issue {issue} cannot block itself")
42            }
43            Error::BlockerCycle { blocker, issue } => {
44                write!(
45                    f,
46                    "adding {blocker} -> {issue} would create a blocker cycle"
47                )
48            }
49            Error::InvalidState { id, state } => {
50                write!(f, "{id} is already {state}; cannot claim")
51            }
52            Error::Other(err) => write!(f, "{err}"),
53        }
54    }
55}
56
57impl std::error::Error for Error {
58    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
59        match self {
60            Error::Other(err) => Some(err.as_ref()),
61            _ => None,
62        }
63    }
64}
65
66impl From<anyhow::Error> for Error {
67    fn from(err: anyhow::Error) -> Self {
68        match err.downcast::<Error>() {
69            Ok(typed) => typed,
70            Err(other) => Error::Other(other),
71        }
72    }
73}
74
75impl From<std::io::Error> for Error {
76    fn from(err: std::io::Error) -> Self {
77        Error::Other(err.into())
78    }
79}