|
| 1 | +use self::clap::ConnectArgs; |
1 | 2 | /// CLI utilities shared between espflash and cargo-espflash
|
2 | 3 | ///
|
3 | 4 | /// No stability guaranties applies
|
4 | 5 | use config::Config;
|
5 |
| -use crossterm::style::Stylize; |
6 |
| -use dialoguer::{theme::ColorfulTheme, Confirm, Select}; |
7 |
| -use miette::{IntoDiagnostic, Result, WrapErr}; |
8 |
| -use serialport::{available_ports, FlowControl, SerialPortInfo, SerialPortType}; |
| 6 | +use miette::{Result, WrapErr}; |
| 7 | +use serialport::FlowControl; |
9 | 8 |
|
10 |
| -use self::clap::ConnectArgs; |
11 |
| -use crate::cli::config::UsbDevice; |
| 9 | +use crate::cli::serial::get_serial_port; |
12 | 10 | use crate::{error::Error, Flasher};
|
13 | 11 |
|
14 | 12 | pub mod clap;
|
15 | 13 | pub mod config;
|
16 | 14 | mod line_endings;
|
17 | 15 | pub mod monitor;
|
18 |
| - |
19 |
| -fn get_serial_port(matches: &ConnectArgs, config: &Config) -> Result<String, Error> { |
20 |
| - // A serial port should be specified either as a command-line argument or in a |
21 |
| - // configuration file. In the case that both have been provided the command-line |
22 |
| - // argument takes precedence. |
23 |
| - // |
24 |
| - // Users may optionally specify the device's VID and PID in the configuration |
25 |
| - // file. If no VID/PID have been provided, the user will always be prompted to |
26 |
| - // select a serial device. If some VID/PID have been provided the user will be |
27 |
| - // prompted to select a serial device, unless there is only one found and its |
28 |
| - // VID/PID matches the configured values. |
29 |
| - if let Some(serial) = &matches.serial { |
30 |
| - Ok(serial.to_owned()) |
31 |
| - } else if let Some(serial) = &config.connection.serial { |
32 |
| - Ok(serial.to_owned()) |
33 |
| - } else if let Ok(ports) = detect_usb_serial_ports() { |
34 |
| - select_serial_port(ports, &config.usb_device) |
35 |
| - } else { |
36 |
| - Err(Error::NoSerial) |
37 |
| - } |
38 |
| -} |
39 |
| - |
40 |
| -fn detect_usb_serial_ports() -> Result<Vec<SerialPortInfo>> { |
41 |
| - let ports = available_ports().into_diagnostic()?; |
42 |
| - let ports = ports |
43 |
| - .iter() |
44 |
| - .filter_map(|port_info| match port_info.port_type { |
45 |
| - SerialPortType::UsbPort(..) => Some(port_info.to_owned()), |
46 |
| - _ => None, |
47 |
| - }) |
48 |
| - .collect::<Vec<_>>(); |
49 |
| - |
50 |
| - Ok(ports) |
51 |
| -} |
52 |
| - |
53 |
| -fn select_serial_port(ports: Vec<SerialPortInfo>, devices: &[UsbDevice]) -> Result<String, Error> { |
54 |
| - let device_matches = |info| devices.iter().any(|dev| dev.matches(info)); |
55 |
| - |
56 |
| - if ports.len() > 1 { |
57 |
| - // Multiple serial ports detected |
58 |
| - println!( |
59 |
| - "Detected {} serial ports. Ports with VID/PID matching configured values are bolded.\n", |
60 |
| - ports.len() |
61 |
| - ); |
62 |
| - |
63 |
| - let port_names = ports |
64 |
| - .iter() |
65 |
| - .map(|port_info| match &port_info.port_type { |
66 |
| - SerialPortType::UsbPort(info) => { |
67 |
| - let formatted = if device_matches(info) { |
68 |
| - port_info.port_name.as_str().bold() |
69 |
| - } else { |
70 |
| - port_info.port_name.as_str().reset() |
71 |
| - }; |
72 |
| - if let Some(product) = &info.product { |
73 |
| - format!("{} - {}", formatted, product) |
74 |
| - } else { |
75 |
| - format!("{}", formatted) |
76 |
| - } |
77 |
| - } |
78 |
| - _ => port_info.port_name.clone(), |
79 |
| - }) |
80 |
| - .collect::<Vec<_>>(); |
81 |
| - let index = Select::with_theme(&ColorfulTheme::default()) |
82 |
| - .items(&port_names) |
83 |
| - .default(0) |
84 |
| - .interact_opt()? |
85 |
| - .ok_or(Error::Canceled)?; |
86 |
| - |
87 |
| - match ports.get(index) { |
88 |
| - Some(port_info) => Ok(port_info.port_name.to_owned()), |
89 |
| - None => Err(Error::NoSerial), |
90 |
| - } |
91 |
| - } else if let [port] = ports.as_slice() { |
92 |
| - // Single serial port detected |
93 |
| - let port_name = port.port_name.clone(); |
94 |
| - let port_info = match &port.port_type { |
95 |
| - SerialPortType::UsbPort(info) => info, |
96 |
| - _ => unreachable!(), |
97 |
| - }; |
98 |
| - |
99 |
| - if device_matches(port_info) |
100 |
| - || Confirm::with_theme(&ColorfulTheme::default()) |
101 |
| - .with_prompt({ |
102 |
| - if let Some(product) = &port_info.product { |
103 |
| - format!("Use serial port '{}' - {}?", port_name, product) |
104 |
| - } else { |
105 |
| - format!("Use serial port '{}'?", port_name) |
106 |
| - } |
107 |
| - }) |
108 |
| - .interact_opt()? |
109 |
| - .ok_or(Error::Canceled)? |
110 |
| - { |
111 |
| - Ok(port_name) |
112 |
| - } else { |
113 |
| - Err(Error::NoSerial) |
114 |
| - } |
115 |
| - } else { |
116 |
| - // No serial ports detected |
117 |
| - Err(Error::NoSerial) |
118 |
| - } |
119 |
| -} |
| 16 | +mod serial; |
120 | 17 |
|
121 | 18 | pub fn connect(matches: &ConnectArgs, config: &Config) -> Result<Flasher> {
|
122 | 19 | let port = get_serial_port(matches, config)?;
|
|
0 commit comments