Krolyxon
Commit 684a13f

Branches

Refs →

budctlcratesbudctl-coresrcerror.rs

103 lines · 3.9 KB · Rust

1//! Every way budctl can fail, as one typed error.
2//!
3//! Front ends decide what to do with each [`Kind`]; the CLI maps them to its exit codes.
4
5use std::fmt;
6use std::io;
7
8pub type Result<T> = std::result::Result<T, Error>;
9
10#[derive(Debug)]
11pub enum Error {
12 /// The request makes no sense for this device (bad value, several devices and no choice made…).
13 Usage(String),
14 /// The buds do not offer this feature.
15 Unsupported(String),
16 /// No paired device matches, or it is not a model budctl supports.
17 NoDevice(String),
18 /// The buds are paired but not connected, or the link dropped.
19 NotConnected(String),
20 /// The buds' SDP record has none of the model's control services.
21 NoService(String),
22 /// The buds identified themselves as a different model.
23 WrongModel { expected: &'static str, found: String },
24 /// BlueZ could not be reached over D-Bus.
25 Bluez(String),
26 /// Another client holds the control channel.
27 ChannelBusy(String),
28 /// The buds did not answer in time.
29 Timeout(String),
30 /// The buds answered with a non-zero status byte.
31 Rejected { what: String, status: u8 },
32 /// The buds accepted a write, but reading back shows the old value.
33 NotApplied(String),
34 /// The buds sent something budctl cannot parse.
35 Protocol(String),
36 /// Any other operating-system error.
37 Os { context: &'static str, source: io::Error },
38}
39
40/// How a front end should treat an error.
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum Kind {
43 /// The user asked for something invalid. (CLI exit code 1)
44 Usage,
45 /// The device was not found or is not reachable. (CLI exit code 2)
46 NotFound,
47 /// Another app holds the control channel. (CLI exit code 3)
48 Busy,
49 /// The buds rejected the command, timed out or sent garbage. (CLI exit code 4)
50 Failed,
51}
52
53impl Error {
54 pub fn kind(&self) -> Kind {
55 match self {
56 Error::Usage(_) | Error::Unsupported(_) => Kind::Usage,
57 Error::NoDevice(_)
58 | Error::NotConnected(_)
59 | Error::NoService(_)
60 | Error::WrongModel { .. }
61 | Error::Bluez(_)
62 | Error::Os { .. } => Kind::NotFound,
63 Error::ChannelBusy(_) => Kind::Busy,
64 Error::Timeout(_) | Error::Rejected { .. } | Error::NotApplied(_) | Error::Protocol(_) => Kind::Failed,
65 }
66 }
67}
68
69impl fmt::Display for Error {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 match self {
72 Error::Usage(msg) | Error::Unsupported(msg) | Error::NoDevice(msg) => f.write_str(msg),
73 Error::NotConnected(msg) => f.write_str(msg),
74 Error::NoService(address) => write!(
75 f,
76 "{address} offers no control service budctl knows (checked its SDP record)"
77 ),
78 Error::WrongModel { expected, found } => {
79 write!(f, "these buds report product id {found}, which is not a {expected}")
80 }
81 Error::Bluez(msg) => write!(f, "cannot reach BlueZ over D-Bus: {msg} (is bluetoothd running?)"),
82 Error::ChannelBusy(detail) => {
83 write!(f, "control channel busy — is another app connected? ({detail})")
84 }
85 Error::Timeout(what) => write!(f, "no answer from the buds: {what}"),
86 Error::Rejected { what, status } => {
87 write!(f, "the buds rejected {what} (status 0x{status:02X})")
88 }
89 Error::NotApplied(msg) => f.write_str(msg),
90 Error::Protocol(msg) => write!(f, "unexpected data from the buds: {msg}"),
91 Error::Os { context, source } => write!(f, "{context}: {source}"),
92 }
93 }
94}
95
96impl std::error::Error for Error {
97 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
98 match self {
99 Error::Os { source, .. } => Some(source),
100 _ => None,
101 }
102 }
103}