Krolyxon
Commit 90075f3

Branches

Refs →

budctlcratesbudctl-coresrcerror.rs

100 lines · 3.7 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 sent something budctl cannot parse.
33 Protocol(String),
34 /// Any other operating-system error.
35 Os { context: &'static str, source: io::Error },
36}
37
38/// How a front end should treat an error.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum Kind {
41 /// The user asked for something invalid. (CLI exit code 1)
42 Usage,
43 /// The device was not found or is not reachable. (CLI exit code 2)
44 NotFound,
45 /// Another app holds the control channel. (CLI exit code 3)
46 Busy,
47 /// The buds rejected the command, timed out or sent garbage. (CLI exit code 4)
48 Failed,
49}
50
51impl Error {
52 pub fn kind(&self) -> Kind {
53 match self {
54 Error::Usage(_) | Error::Unsupported(_) => Kind::Usage,
55 Error::NoDevice(_)
56 | Error::NotConnected(_)
57 | Error::NoService(_)
58 | Error::WrongModel { .. }
59 | Error::Bluez(_)
60 | Error::Os { .. } => Kind::NotFound,
61 Error::ChannelBusy(_) => Kind::Busy,
62 Error::Timeout(_) | Error::Rejected { .. } | Error::Protocol(_) => Kind::Failed,
63 }
64 }
65}
66
67impl fmt::Display for Error {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 match self {
70 Error::Usage(msg) | Error::Unsupported(msg) | Error::NoDevice(msg) => f.write_str(msg),
71 Error::NotConnected(msg) => f.write_str(msg),
72 Error::NoService(address) => write!(
73 f,
74 "{address} offers no control service budctl knows (checked its SDP record)"
75 ),
76 Error::WrongModel { expected, found } => {
77 write!(f, "these buds report product id {found}, which is not a {expected}")
78 }
79 Error::Bluez(msg) => write!(f, "cannot reach BlueZ over D-Bus: {msg} (is bluetoothd running?)"),
80 Error::ChannelBusy(detail) => {
81 write!(f, "control channel busy — is another app connected? ({detail})")
82 }
83 Error::Timeout(what) => write!(f, "no answer from the buds: {what}"),
84 Error::Rejected { what, status } => {
85 write!(f, "the buds rejected {what} (status 0x{status:02X})")
86 }
87 Error::Protocol(msg) => write!(f, "unexpected data from the buds: {msg}"),
88 Error::Os { context, source } => write!(f, "{context}: {source}"),
89 }
90 }
91}
92
93impl std::error::Error for Error {
94 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
95 match self {
96 Error::Os { source, .. } => Some(source),
97 _ => None,
98 }
99 }
100}