Skip to content

Commit 90884cb

Browse files
authored
Merge pull request #100 from esp-rs/config-save-adapter
ask to save vid/pid in config if an unrecognized adapter is used
2 parents 457e9ee + f6c8496 commit 90884cb

File tree

8 files changed

+87
-33
lines changed

8 files changed

+87
-33
lines changed

.github/workflows/rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ jobs:
159159
- uses: actions-rs/toolchain@v1
160160
with:
161161
profile: minimal
162-
toolchain: 1.55
162+
toolchain: 1.56
163163
override: true
164164
- uses: Swatinem/rust-cache@v1
165165
- uses: actions-rs/cargo@v1

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ This repository contains two applications:
1414
| [cargo-espflash] | Cargo subcommand for espflash |
1515
| [espflash] | Library and `espflash` binary (_without_ Cargo integration) |
1616

17-
> **NOTE:** requires `rustc >= 1.55.0` in order to build either application
17+
> **NOTE:** requires `rustc >= 1.56.0` in order to build either application
1818
1919
## Installation
2020

cargo-espflash/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors = [
66
"Jesse Braham <[email protected]>",
77
]
88
edition = "2018"
9-
rust-version = "1.55"
9+
rust-version = "1.56"
1010
description = "Cargo subcommand for flashing Espressif devices over serial"
1111
repository = "https://github.com/esp-rs/espflash"
1212
license = "GPL-2.0"

cargo-espflash/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ fn main() -> Result<()> {
6161

6262
let CargoSubCommand::Espflash(opts) = Opts::parse().sub_cmd;
6363

64-
let config = Config::load();
64+
let config = Config::load()?;
6565
let metadata = CargoEspFlashMeta::load("Cargo.toml")?;
6666
let cargo_config = parse_cargo_config(".")?;
6767

espflash/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "espflash"
33
version = "1.1.0"
44
authors = ["Robin Appelman <[email protected]>"]
55
edition = "2018"
6-
rust-version = "1.55"
6+
rust-version = "1.56"
77
license = "GPL-2.0"
88
description = "ESP8266 and ESP32 serial flasher"
99
repository = "https://github.com/esp-rs/espflash"
@@ -38,6 +38,7 @@ miette = { version = "3", features = ["fancy"] }
3838
crossterm = "0.22"
3939
directories-next = "2"
4040
dialoguer = "0.9"
41+
serde-hex = "0.1"
4142

4243
[package.metadata.binstall]
4344
pkg-url = "{ repo }/releases/download/v{ version }/{ name }"

espflash/src/cli/config.rs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
1-
use std::fs::read;
2-
31
use directories_next::ProjectDirs;
4-
use serde::Deserialize;
2+
use miette::{IntoDiagnostic, Result, WrapErr};
3+
use serde::{Deserialize, Serialize};
4+
use serde_hex::{Compact, SerHex};
55
use serialport::UsbPortInfo;
6+
use std::fs::{create_dir_all, read, write};
7+
use std::path::PathBuf;
68

7-
#[derive(Debug, Deserialize, Default)]
9+
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
810
pub struct Config {
911
#[serde(default)]
1012
pub connection: Connection,
1113
#[serde(default)]
1214
pub usb_device: Vec<UsbDevice>,
15+
#[serde(skip)]
16+
save_path: PathBuf,
1317
}
1418

15-
#[derive(Debug, Deserialize, Default)]
19+
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
1620
pub struct Connection {
1721
pub serial: Option<String>,
1822
}
1923

20-
#[derive(Debug, Deserialize, Default)]
24+
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
2125
pub struct UsbDevice {
26+
#[serde(with = "SerHex::<Compact>")]
2227
pub vid: u16,
28+
#[serde(with = "SerHex::<Compact>")]
2329
pub pid: u16,
2430
}
2531

@@ -31,14 +37,31 @@ impl UsbDevice {
3137

3238
impl Config {
3339
/// Load the config from config file
34-
pub fn load() -> Self {
40+
pub fn load() -> Result<Self> {
3541
let dirs = ProjectDirs::from("rs", "esp", "espflash").unwrap();
3642
let file = dirs.config_dir().join("espflash.toml");
3743

38-
if let Ok(data) = read(&file) {
39-
toml::from_slice(&data).unwrap()
44+
let mut config = if let Ok(data) = read(&file) {
45+
toml::from_slice(&data).into_diagnostic()?
4046
} else {
4147
Self::default()
42-
}
48+
};
49+
config.save_path = file;
50+
Ok(config)
51+
}
52+
53+
pub fn save_with<F: Fn(&mut Self)>(&self, modify_fn: F) -> Result<()> {
54+
let mut copy = self.clone();
55+
modify_fn(&mut copy);
56+
57+
let serialized = toml::to_string(&copy)
58+
.into_diagnostic()
59+
.wrap_err("Failed to serialize config")?;
60+
create_dir_all(self.save_path.parent().unwrap())
61+
.into_diagnostic()
62+
.wrap_err("Failed to create config directory")?;
63+
write(&self.save_path, serialized)
64+
.into_diagnostic()
65+
.wrap_err_with(|| format!("Failed to write config to {}", self.save_path.display()))
4366
}
4467
}

espflash/src/cli/serial.rs

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,27 @@ pub fn get_serial_port(matches: &ConnectArgs, config: &Config) -> Result<String,
2323
} else if let Some(serial) = &config.connection.serial {
2424
Ok(serial.to_owned())
2525
} else if let Ok(ports) = detect_usb_serial_ports() {
26-
select_serial_port(ports, &config.usb_device)
26+
let (port, matches) = select_serial_port(ports, config)?;
27+
match port.port_type {
28+
SerialPortType::UsbPort(usb_info) if !matches => {
29+
if Confirm::with_theme(&ColorfulTheme::default())
30+
.with_prompt("Remember this serial port for future use?")
31+
.interact_opt()?
32+
.unwrap_or_default()
33+
{
34+
if let Err(e) = config.save_with(|config| {
35+
config.usb_device.push(UsbDevice {
36+
vid: usb_info.vid,
37+
pid: usb_info.pid,
38+
})
39+
}) {
40+
eprintln!("Failed to save config {:#}", e);
41+
}
42+
}
43+
}
44+
_ => {}
45+
}
46+
Ok(port.port_name)
2747
} else {
2848
Err(Error::NoSerial)
2949
}
@@ -56,10 +76,11 @@ const KNOWN_DEVICES: &[UsbDevice] = &[
5676

5777
fn select_serial_port(
5878
ports: Vec<SerialPortInfo>,
59-
configured_devices: &[UsbDevice],
60-
) -> Result<String, Error> {
79+
config: &Config,
80+
) -> Result<(SerialPortInfo, bool), Error> {
6181
let device_matches = |info| {
62-
configured_devices
82+
config
83+
.usb_device
6384
.iter()
6485
.chain(KNOWN_DEVICES.iter())
6586
.any(|dev| dev.matches(info))
@@ -97,7 +118,15 @@ fn select_serial_port(
97118
.ok_or(Error::Canceled)?;
98119

99120
match ports.get(index) {
100-
Some(port_info) => Ok(port_info.port_name.to_owned()),
121+
Some(
122+
port_info
123+
@
124+
SerialPortInfo {
125+
port_type: SerialPortType::UsbPort(usb_info),
126+
..
127+
},
128+
) => Ok((port_info.clone(), device_matches(usb_info))),
129+
Some(port_info) => Ok((port_info.clone(), false)),
101130
None => Err(Error::NoSerial),
102131
}
103132
} else if let [port] = ports.as_slice() {
@@ -108,19 +137,20 @@ fn select_serial_port(
108137
_ => unreachable!(),
109138
};
110139

111-
if device_matches(port_info)
112-
|| Confirm::with_theme(&ColorfulTheme::default())
113-
.with_prompt({
114-
if let Some(product) = &port_info.product {
115-
format!("Use serial port '{}' - {}?", port_name, product)
116-
} else {
117-
format!("Use serial port '{}'?", port_name)
118-
}
119-
})
120-
.interact_opt()?
121-
.ok_or(Error::Canceled)?
140+
if device_matches(port_info) {
141+
Ok((port.clone(), true))
142+
} else if Confirm::with_theme(&ColorfulTheme::default())
143+
.with_prompt({
144+
if let Some(product) = &port_info.product {
145+
format!("Use serial port '{}' - {}?", port_name, product)
146+
} else {
147+
format!("Use serial port '{}'?", port_name)
148+
}
149+
})
150+
.interact_opt()?
151+
.ok_or(Error::Canceled)?
122152
{
123-
Ok(port_name)
153+
Ok((port.clone(), false))
124154
} else {
125155
Err(Error::NoSerial)
126156
}

espflash/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ fn main() -> Result<()> {
5252
miette::set_panic_hook();
5353

5454
let mut opts = Opts::parse();
55-
let config = Config::load();
55+
let config = Config::load()?;
5656

5757
// If only a single argument is passed, it's always going to be the ELF file. In
5858
// the case that the serial port was not provided as a command-line argument,

0 commit comments

Comments
 (0)