Skip to main content

choreo_client_core/
error.rs

1use choreo_proto::ProtoError;
2use std::io;
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6pub enum ClientError {
7    #[error(transparent)]
8    Proto(#[from] ProtoError),
9    #[error(transparent)]
10    Io(#[from] io::Error),
11    #[error(transparent)]
12    Utf8(#[from] std::string::FromUtf8Error),
13
14    #[error(
15        "no unlock key available for {0}: add `unlock_key` (base64) to known_servers.toml, or run /unlock <base64 unlock-key>"
16    )]
17    NoUnlockKey(String),
18    #[error("failed to read private key: {0}")]
19    PrivateKeyRead(String),
20    #[error("invalid private key file: expected 32 bytes")]
21    PrivateKeyInvalid,
22    #[error("failed to read public key: {0}")]
23    PublicKeyRead(String),
24    #[error("invalid public key file")]
25    PublicKeyInvalid,
26    #[error("{0}")]
27    CredentialParse(String),
28    #[error("postcard serialization failed: {0}")]
29    Postcard(String),
30    #[error("encryption failed: {0}")]
31    Encryption(String),
32    /// Daemon autostart (the caller-provided `ensure_daemon` hook of
33    /// `run_daemon_connection_with_autostart`) failed. The string carries the
34    /// full anyhow cause chain so the TUI can surface the daemon's log path.
35    #[error("failed to start the daemon: {0}")]
36    DaemonStart(String),
37}
38
39/// Convert an mpsc send error (or any displayable error) into a
40/// `ClientError::Io(BrokenPipe)`.  This is the standard pattern when
41/// the daemon connection drops and we need to propagate the error
42/// through `client_tx.send(...).map_err(broken_pipe)?`.
43pub fn broken_pipe(err: impl std::fmt::Display) -> ClientError {
44    ClientError::Io(io::Error::new(io::ErrorKind::BrokenPipe, err.to_string()))
45}
46
47impl From<ClientError> for io::Error {
48    fn from(error: ClientError) -> Self {
49        match error {
50            ClientError::Proto(proto) => io::Error::from(proto),
51            ClientError::Io(io) => io,
52            ClientError::Utf8(e) => io::Error::new(io::ErrorKind::InvalidData, e),
53            ClientError::NoUnlockKey(_)
54            | ClientError::PrivateKeyRead(_)
55            | ClientError::PrivateKeyInvalid
56            | ClientError::PublicKeyRead(_)
57            | ClientError::PublicKeyInvalid
58            | ClientError::CredentialParse(_)
59            | ClientError::Postcard(_)
60            | ClientError::Encryption(_)
61            | ClientError::DaemonStart(_) => io::Error::new(io::ErrorKind::InvalidData, error),
62        }
63    }
64}