1use std::fmt;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Debug)]
10pub enum Error {
11 IssueNotFound {
13 id: String,
15 },
16 ClaimConflict {
18 id: String,
20 holder: String,
22 claimed_at: Option<String>,
24 },
25 BlockerCycle {
27 blocker: String,
29 issue: String,
31 },
32 InvalidState {
34 id: String,
36 state: String,
38 },
39 Other(anyhow::Error),
41}
42
43impl fmt::Display for Error {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 match self {
46 Error::IssueNotFound { id } => write!(f, "issue {id} not found"),
47 Error::ClaimConflict {
48 id,
49 holder,
50 claimed_at,
51 } => write!(
52 f,
53 "{id} is claimed by {holder} since {}; pass --force to take it over",
54 claimed_at.as_deref().unwrap_or("an unknown time")
55 ),
56 Error::BlockerCycle { blocker, issue } if blocker == issue => {
57 write!(f, "issue {issue} cannot block itself")
58 }
59 Error::BlockerCycle { blocker, issue } => {
60 write!(
61 f,
62 "adding {blocker} -> {issue} would create a blocker cycle"
63 )
64 }
65 Error::InvalidState { id, state } => {
66 write!(f, "{id} is already {state}; cannot claim")
67 }
68 Error::Other(err) => write!(f, "{err}"),
69 }
70 }
71}
72
73impl std::error::Error for Error {
74 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
75 match self {
76 Error::Other(err) => Some(err.as_ref()),
77 _ => None,
78 }
79 }
80}
81
82impl From<anyhow::Error> for Error {
83 fn from(err: anyhow::Error) -> Self {
84 match err.downcast::<Error>() {
85 Ok(typed) => typed,
86 Err(other) => Error::Other(other),
87 }
88 }
89}
90
91impl From<std::io::Error> for Error {
92 fn from(err: std::io::Error) -> Self {
93 Error::Other(err.into())
94 }
95}
96
97impl From<fmt::Error> for Error {
98 fn from(err: fmt::Error) -> Self {
99 Error::Other(err.into())
100 }
101}
102
103impl From<serde_json::Error> for Error {
104 fn from(err: serde_json::Error) -> Self {
105 Error::Other(err.into())
106 }
107}