Skip to content

Commit a09e3a8

Browse files
committed
Re-work the serial error types to make the problem more clean, cleanup code
1 parent bfda278 commit a09e3a8

File tree

2 files changed

+70
-53
lines changed

2 files changed

+70
-53
lines changed

espflash/src/cli/serial.rs

Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crossterm::style::Stylize;
22
use dialoguer::{theme::ColorfulTheme, Confirm, Select};
33
use miette::{IntoDiagnostic, Result};
4-
use serialport::{available_ports, SerialPortInfo, SerialPortType};
4+
use serialport::{available_ports, SerialPortInfo, SerialPortType, UsbPortInfo};
55

66
use super::{config::Config, ConnectOpts};
77
use crate::{cli::config::UsbDevice, error::Error};
@@ -21,19 +21,23 @@ pub fn get_serial_port_info(
2121
// and PID match the configured values.
2222
let ports = detect_usb_serial_ports().unwrap_or_default();
2323

24-
let maybe_port = if let Some(serial) = &matches.serial {
25-
find_serial_port(&ports, serial.to_owned())
24+
if let Some(serial) = &matches.serial {
25+
find_serial_port(&ports, serial)
2626
} else if let Some(serial) = &config.connection.serial {
27-
find_serial_port(&ports, serial.to_owned())
28-
} else if !ports.is_empty() {
27+
find_serial_port(&ports, serial)
28+
} else {
2929
let (port, matches) = select_serial_port(ports, config)?;
30+
3031
match &port.port_type {
3132
SerialPortType::UsbPort(usb_info) if !matches => {
32-
if Confirm::with_theme(&ColorfulTheme::default())
33+
let remember = Confirm::with_theme(&ColorfulTheme::default())
3334
.with_prompt("Remember this serial port for future use?")
3435
.interact_opt()?
35-
.unwrap_or_default()
36-
{
36+
.unwrap_or_default();
37+
38+
if remember {
39+
// Allow this operation to fail without terminating the
40+
// application, but inform the user if something goes wrong.
3741
if let Err(e) = config.save_with(|config| {
3842
config.usb_device.push(UsbDevice {
3943
vid: usb_info.vid,
@@ -47,25 +51,22 @@ pub fn get_serial_port_info(
4751
_ => {}
4852
}
4953

50-
Some(port)
51-
} else {
52-
None
53-
};
54-
55-
if let Some(port_info) = maybe_port {
56-
Ok(port_info)
57-
} else {
58-
Err(Error::NoSerial)
54+
Ok(port)
5955
}
6056
}
6157

6258
/// Given a vector of `SerialPortInfo` structs, attempt to find and return one
6359
/// whose `port_name` field matches the provided `name` argument.
64-
fn find_serial_port(ports: &[SerialPortInfo], name: String) -> Option<SerialPortInfo> {
65-
ports
60+
fn find_serial_port(ports: &[SerialPortInfo], name: &str) -> Result<SerialPortInfo, Error> {
61+
let port_info = ports
6662
.iter()
67-
.find(|port| port.port_name.to_lowercase() == name.to_lowercase())
68-
.map(|port| port.to_owned())
63+
.find(|port| port.port_name.to_lowercase() == name.to_lowercase());
64+
65+
if let Some(port) = port_info {
66+
Ok(port.to_owned())
67+
} else {
68+
Err(Error::SerialNotFound(name.to_owned()))
69+
}
6970
}
7071

7172
/// serialport's autodetect doesn't provide any port information when using musl
@@ -78,19 +79,19 @@ fn detect_usb_serial_ports() -> Result<Vec<SerialPortInfo>> {
7879
path::PathBuf,
7980
};
8081

81-
use serialport::UsbPortInfo;
82-
8382
let ports = available_ports().into_diagnostic()?;
8483
let ports = ports
8584
.into_iter()
8685
.filter_map(|port_info| {
8786
// with musl, the paths we get are `/sys/class/tty/*`
8887
let path = PathBuf::from(&port_info.port_name);
8988

90-
// this will give something like `/sys/devices/pci0000:00/0000:00:07.1/0000:0c:00.3/usb5/5-3/5-3.1/5-3.1:1.0/ttyUSB0/tty/ttyUSB0`
89+
// this will give something like:
90+
// `/sys/devices/pci0000:00/0000:00:07.1/0000:0c:00.3/usb5/5-3/5-3.1/5-3.1:1.0/ttyUSB0/tty/ttyUSB0`
9191
let mut parent_dev = path.canonicalize().ok()?;
9292

93-
// walk up 3 dirs to get to the device hosting the tty `/sys/devices/pci0000:00/0000:00:07.1/0000:0c:00.3/usb5/5-3/5-3.1/5-3.1:1.0`
93+
// walk up 3 dirs to get to the device hosting the tty:
94+
// `/sys/devices/pci0000:00/0000:00:07.1/0000:0c:00.3/usb5/5-3/5-3.1/5-3.1:1.0`
9495
parent_dev.pop();
9596
parent_dev.pop();
9697
parent_dev.pop();
@@ -164,7 +165,7 @@ fn select_serial_port(
164165
if ports.len() > 1 {
165166
// Multiple serial ports detected
166167
println!(
167-
"Detected {} serial ports. Ports with match a known common dev board are highlighted.\n",
168+
"Detected {} serial ports. Ports which match a known common dev board are highlighted.\n",
168169
ports.len()
169170
);
170171

@@ -177,30 +178,36 @@ fn select_serial_port(
177178
} else {
178179
port_info.port_name.as_str().reset()
179180
};
181+
180182
if let Some(product) = &info.product {
181183
format!("{} - {}", formatted, product)
182184
} else {
183-
format!("{}", formatted)
185+
formatted.to_string()
184186
}
185187
}
186188
_ => port_info.port_name.clone(),
187189
})
188190
.collect::<Vec<_>>();
191+
189192
let index = Select::with_theme(&ColorfulTheme::default())
190193
.items(&port_names)
191194
.default(0)
192195
.interact_opt()?
193196
.ok_or(Error::Canceled)?;
194197

195198
match ports.get(index) {
196-
Some(
197-
port_info @ SerialPortInfo {
198-
port_type: SerialPortType::UsbPort(usb_info),
199-
..
200-
},
201-
) => Ok((port_info.clone(), device_matches(usb_info))),
202-
Some(port_info) => Ok((port_info.clone(), false)),
203-
None => Err(Error::NoSerial),
199+
Some(port_info) => {
200+
let matches = if let SerialPortType::UsbPort(usb_info) = &port_info.port_type {
201+
device_matches(usb_info)
202+
} else {
203+
false
204+
};
205+
206+
Ok((port_info.to_owned(), matches))
207+
}
208+
None => Err(Error::SerialNotFound(
209+
port_names.get(index).unwrap().to_string(),
210+
)),
204211
}
205212
} else if let [port] = ports.as_slice() {
206213
// Single serial port detected
@@ -211,24 +218,27 @@ fn select_serial_port(
211218
};
212219

213220
if device_matches(port_info) {
214-
Ok((port.clone(), true))
215-
} else if Confirm::with_theme(&ColorfulTheme::default())
216-
.with_prompt({
217-
if let Some(product) = &port_info.product {
218-
format!("Use serial port '{}' - {}?", port_name, product)
219-
} else {
220-
format!("Use serial port '{}'?", port_name)
221-
}
222-
})
223-
.interact_opt()?
224-
.ok_or(Error::Canceled)?
225-
{
226-
Ok((port.clone(), false))
221+
Ok((port.to_owned(), true))
222+
} else if confirm_port(&port_name, port_info)? {
223+
Ok((port.to_owned(), false))
227224
} else {
228-
Err(Error::NoSerial)
225+
Err(Error::SerialNotFound(port_name))
229226
}
230227
} else {
231228
// No serial ports detected
232229
Err(Error::NoSerial)
233230
}
234231
}
232+
233+
fn confirm_port(port_name: &str, port_info: &UsbPortInfo) -> Result<bool, Error> {
234+
Confirm::with_theme(&ColorfulTheme::default())
235+
.with_prompt({
236+
if let Some(product) = &port_info.product {
237+
format!("Use serial port '{}' - {}?", port_name, product)
238+
} else {
239+
format!("Use serial port '{}'?", port_name)
240+
}
241+
})
242+
.interact_opt()?
243+
.ok_or(Error::Canceled)
244+
}

espflash/src/error.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,18 @@ https://github.com/espressif/esp32c3-direct-boot-example"
7575
)
7676
)]
7777
InvalidDirectBootBinary,
78-
#[error("No serial port specified in arguments or config")]
78+
#[error("No serial ports could be detected")]
7979
#[diagnostic(
80-
code(cargo_espflash::no_serial),
81-
help("Add a command line option with the serial port to use")
80+
code(espflash::no_serial),
81+
help("Make sure you have connected a device to the host system")
8282
)]
8383
NoSerial,
84+
#[error("The serial port '{0}' could not be found")]
85+
#[diagnostic(
86+
code(espflash::serial_not_found),
87+
help("Make sure the correct device is connected to the host system")
88+
)]
89+
SerialNotFound(String),
8490
#[error("Canceled by user")]
8591
Canceled,
8692
}
@@ -383,7 +389,8 @@ impl CSVError {
383389
}
384390
}
385391

386-
/// since csv doesn't give us the position in the line the error occurs, we highlight the entire line
392+
/// since csv doesn't give us the position in the line the error occurs, we
393+
/// highlight the entire line
387394
///
388395
/// line starts at 1
389396
fn line_to_span(source: &str, line: usize) -> SourceSpan {

0 commit comments

Comments
 (0)