Skip to main content

anybox/
error.rs

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        /// each box that answered, with what it couldn't do
33        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}