1use std::borrow::Cow;
2
3use crate::capability::{Action, Family, Firmware, StableId};
4
5pub type Result<T, E = Error> = std::result::Result<T, E>;
6
7#[derive(Debug, thiserror::Error)]
8#[non_exhaustive]
9pub enum Error {
10 #[error("no box answered{}, check it's plugged in and give it a few seconds if you just plugged it in. without a port, anybox only asks CH343 ports (1A86:55D3), so if your box sits behind another chip, name its port with Connect::port", tried_text(.tried))]
11 #[non_exhaustive]
12 NotFound { tried: Vec<String> },
13
14 #[error("this device was closed, open the box again if you need it")]
15 Closed,
16
17 #[error(
18 "{port} is already open in another program or another Device in this one, close it there first"
19 )]
20 #[non_exhaustive]
21 PortBusy { port: String },
22
23 #[error(
24 "you're not allowed to open {port}, on Linux that usually means adding yourself to the dialout group"
25 )]
26 #[non_exhaustive]
27 PermissionDenied { port: String },
28
29 #[error("{}, so plug in a box that can, or ask Connect::can for less", incapable_text(.boxes))]
30 #[non_exhaustive]
31 NoBoxCan {
32 boxes: Vec<(crate::connect::Found, Vec<Action>)>,
34 },
35
36 #[error("the box on {port} isn't a {expected}{}, pick the right family or leave it out and let anybox find it. if you just plugged it in, give it a couple seconds to start first", found_text(.found))]
37 #[non_exhaustive]
38 WrongFamily {
39 port: String,
40 expected: Family,
41 found: Option<Family>,
42 },
43
44 #[error("the box on {port} is {}, not {expected}, so use Connect::auto().id(..) and anybox finds it on whatever port it's on", found_id_text(.found))]
45 #[non_exhaustive]
46 WrongBox {
47 port: String,
48 expected: StableId,
49 found: Option<StableId>,
50 },
51
52 #[error(
53 "this {family} runs firmware {found} and anybox needs {needed} or newer, so update the box first"
54 )]
55 #[non_exhaustive]
56 FirmwareTooOld {
57 family: Family,
58 found: Firmware,
59 needed: Firmware,
60 },
61
62 #[error(
63 "this {family} runs firmware {found}, which is newer than this anybox knows, so update anybox"
64 )]
65 #[non_exhaustive]
66 LibraryTooOld { family: Family, found: Firmware },
67
68 #[error("{family} can't {action}, {reason}")]
69 #[non_exhaustive]
70 Unsupported {
71 action: Action,
72 family: Family,
73 reason: Cow<'static, str>,
74 },
75
76 #[error("can't {action} like that, {detail}")]
77 #[non_exhaustive]
78 InvalidArgument {
79 action: Action,
80 detail: Cow<'static, str>,
81 },
82
83 #[error("the box refused to {action}, {detail}")]
84 #[non_exhaustive]
85 Rejected { action: Action, detail: String },
86
87 #[error(
88 "the box didn't answer in time when asked to {action}, check it's still plugged in and nothing else is talking to it"
89 )]
90 #[non_exhaustive]
91 Timeout { action: Action },
92
93 #[error(
94 "the box is disconnected and anybox is already trying to reconnect, so try again once you get Reconnected"
95 )]
96 Disconnected,
97
98 #[error(
99 "couldn't {action} because the box reconnected or reset in the middle of it. anybox already let go of it, so do it again if you still want it"
100 )]
101 #[non_exhaustive]
102 Interrupted { action: Action },
103
104 #[error(transparent)]
105 Io(#[from] std::io::Error),
106}
107
108fn incapable_text(boxes: &[(crate::connect::Found, Vec<Action>)]) -> String {
109 let each: Vec<String> = boxes
110 .iter()
111 .map(|(found, missing)| {
112 let missing: Vec<String> = missing.iter().map(ToString::to_string).collect();
113 format!(
114 "the {} on {} can't {}",
115 found.info.family,
116 found.port,
117 missing.join(" or ")
118 )
119 })
120 .collect();
121 each.join(", ")
122}
123
124fn tried_text(tried: &[String]) -> String {
125 if tried.is_empty() {
126 String::new()
127 } else {
128 format!(" on {}", tried.join(", "))
129 }
130}
131
132fn found_id_text(found: &Option<StableId>) -> String {
133 match found {
134 Some(id) => id.to_string(),
135 None => "a box with no id".into(),
136 }
137}
138
139fn found_text(found: &Option<Family>) -> String {
140 match found {
141 Some(family) => format!(", it's a {family}"),
142 None => String::new(),
143 }
144}