Skip to main content

farhand_core/
error.rs

1use std::fmt;
2
3pub type Result<T> = std::result::Result<T, Error>;
4
5/// Every failure FarHand reports to a model or a user. `Denied` is the one
6/// that matters most: it means the secret guard or the local allowlist
7/// refused an operation, and the reason is safe to show.
8#[derive(Debug)]
9pub enum Error {
10    /// No configuration exists anywhere in the resolution order. Not a
11    /// broken config: for agents this simply means "not active here".
12    NoConfig,
13    Config(String),
14    Denied(String),
15    Ssh(String),
16    Io(std::io::Error),
17    Invalid(String),
18    Timeout {
19        secs: u64,
20    },
21}
22
23impl fmt::Display for Error {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            Error::NoConfig => write!(
27                f,
28                "no config found: create .farhand.toml in the project (`farhand init > \
29                 .farhand.toml`), or pass --config, set FARHAND_CONFIG, or write \
30                 ~/.config/farhand/config.toml"
31            ),
32            Error::Config(m) => write!(f, "config: {m}"),
33            Error::Denied(m) => write!(f, "denied: {m}"),
34            Error::Ssh(m) => write!(f, "ssh: {m}"),
35            Error::Io(e) => write!(f, "io: {e}"),
36            Error::Invalid(m) => write!(f, "invalid: {m}"),
37            Error::Timeout { secs } => write!(f, "timed out after {secs}s"),
38        }
39    }
40}
41
42impl std::error::Error for Error {}
43
44impl From<std::io::Error> for Error {
45    fn from(e: std::io::Error) -> Self {
46        Error::Io(e)
47    }
48}
49
50impl From<openssh::Error> for Error {
51    fn from(e: openssh::Error) -> Self {
52        Error::Ssh(e.to_string())
53    }
54}
55
56impl From<openssh_sftp_client::Error> for Error {
57    fn from(e: openssh_sftp_client::Error) -> Self {
58        Error::Ssh(format!("sftp: {e}"))
59    }
60}
61
62impl Error {
63    pub fn kind(&self) -> &'static str {
64        match self {
65            Error::NoConfig => "no-config",
66            Error::Config(_) => "config",
67            Error::Denied(_) => "denied",
68            Error::Ssh(_) => "ssh",
69            Error::Io(_) => "io",
70            Error::Invalid(_) => "invalid",
71            Error::Timeout { .. } => "timeout",
72        }
73    }
74}