Skip to content

Commit 1daf446

Browse files
authored
Fix using cli::IdfFormatArgs in espflash.toml (#931)
1 parent b3f5b28 commit 1daf446

File tree

4 files changed

+112
-38
lines changed

4 files changed

+112
-38
lines changed

cargo-espflash/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,15 @@ baudrate = 460800
147147
```
148148
- Bootloader:
149149
```toml
150+
[idf]
150151
bootloader = "path/to/custom/bootloader.bin"
151152
```
152-
- Partition table
153+
- Partition table:
153154
```toml
155+
[idf]
154156
partition_table = "path/to/custom/partition-table.bin"
155157
```
156-
- Flash settings
158+
- Flash settings:
157159
```toml
158160
[flash]
159161
mode = "qio"

espflash/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,15 @@ baudrate = 460800
167167
```
168168
- Bootloader:
169169
```toml
170+
[idf]
170171
bootloader = "path/to/custom/bootloader.bin"
171172
```
172-
- Partition table
173+
- Partition table:
173174
```toml
175+
[idf]
174176
partition_table = "path/to/custom/partition-table.bin"
175177
```
176-
- Flash settings
178+
- Flash settings:
177179
```toml
178180
[flash]
179181
mode = "qio"

espflash/src/cli/config.rs

Lines changed: 100 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
use std::{
1111
ffi::OsStr,
1212
fs::{create_dir_all, read_to_string, write},
13-
path::PathBuf,
13+
path::{Path, PathBuf},
1414
};
1515

1616
use directories::ProjectDirs;
@@ -91,7 +91,7 @@ pub struct ProjectConfig {
9191
#[serde(default)]
9292
pub format: ImageFormatKind,
9393
/// ESP-IDF format arguments
94-
#[serde(default)]
94+
#[serde(default, alias = "idf")]
9595
pub idf_format_args: cli::IdfFormatArgs,
9696
/// Flash settings
9797
#[serde(default)]
@@ -113,62 +113,128 @@ pub struct PortConfig {
113113
}
114114

115115
impl Config {
116-
/// Load configuration from the configuration files
116+
/// Load configuration from the configuration files.
117117
pub fn load() -> Result<Self> {
118118
let project_config_file = Self::project_config_path()?;
119119
let port_config_file = Self::port_config_path()?;
120120

121-
let project_config = if let Ok(data) = read_to_string(&project_config_file) {
122-
toml::from_str(&data).into_diagnostic()?
121+
let raw_data = read_to_string(&project_config_file).unwrap_or_default();
122+
let toml_value = toml::from_str::<toml::Value>(&raw_data)
123+
.unwrap_or_else(|_| toml::Value::Table(Default::default()));
124+
125+
if let toml::Value::Table(top_level) = &toml_value {
126+
Self::validate_keys(top_level)?;
127+
}
128+
129+
let project_config: ProjectConfig =
130+
toml::from_str(&raw_data).unwrap_or_else(|_| ProjectConfig::default());
131+
132+
Self::validate_partition_table_path(&project_config)?;
133+
Self::validate_bootloader_path(&project_config)?;
134+
135+
debug!("Config: {:#?}", &project_config);
136+
137+
let mut port_config =
138+
Self::load_port_config(&port_config_file, &project_config_file, &raw_data)?;
139+
port_config.save_path = port_config_file;
140+
debug!("Port Config: {:#?}", &port_config);
141+
142+
Ok(Config {
143+
project_config,
144+
port_config,
145+
})
146+
}
147+
148+
fn validate_keys(top_level: &toml::map::Map<String, toml::Value>) -> Result<()> {
149+
let forbidden_keys: &[&[&str]] = &[
150+
&[
151+
"bootloader",
152+
"partition_table",
153+
"partition_table_offset",
154+
"target_app_partition",
155+
],
156+
&["size", "mode", "frequency"],
157+
];
158+
let allowed_sections: &[&[&str]] = &[&["idf_format_args", "idf"], &["flash"]];
159+
160+
let mut misplaced_keys = Vec::new();
161+
162+
for (section_keys, allowed) in forbidden_keys.iter().zip(allowed_sections.iter()) {
163+
for &key in *section_keys {
164+
for (section_name, value) in top_level {
165+
if let toml::Value::Table(table) = value {
166+
if table.contains_key(key) && !allowed.contains(&section_name.as_str()) {
167+
misplaced_keys.push((key, allowed[0]));
168+
}
169+
}
170+
}
171+
if top_level.contains_key(key) {
172+
misplaced_keys.push((key, allowed[0]));
173+
}
174+
}
175+
}
176+
177+
if misplaced_keys.is_empty() {
178+
Ok(())
123179
} else {
124-
ProjectConfig::default()
125-
};
180+
let msg = misplaced_keys
181+
.into_iter()
182+
.map(|(key, section)| format!("'{key}' should be under [{section}]!"))
183+
.collect::<Vec<_>>()
184+
.join(", ");
185+
Err(Error::MisplacedKey(msg).into())
186+
}
187+
}
126188

127-
if let Some(table) = &project_config.idf_format_args.partition_table {
128-
match table.extension() {
129-
Some(ext) if ext == "bin" || ext == "csv" => {}
130-
_ => return Err(Error::InvalidPartitionTablePath.into()),
189+
fn validate_partition_table_path(config: &ProjectConfig) -> Result<()> {
190+
if let Some(path) = &config.idf_format_args.partition_table {
191+
match path.extension() {
192+
Some(ext) if ext == "bin" || ext == "csv" => Ok(()),
193+
_ => Err(Error::InvalidPartitionTablePath.into()),
131194
}
195+
} else {
196+
Ok(())
132197
}
198+
}
133199

134-
if let Some(bootloader) = &project_config.idf_format_args.bootloader {
135-
if bootloader.extension() != Some(OsStr::new("bin")) {
200+
fn validate_bootloader_path(config: &ProjectConfig) -> Result<()> {
201+
if let Some(path) = &config.idf_format_args.bootloader {
202+
if path.extension() != Some(OsStr::new("bin")) {
136203
return Err(Error::InvalidBootloaderPath.into());
137204
}
138205
}
206+
Ok(())
207+
}
139208

140-
debug!("Config: {:#?}", &project_config);
141-
142-
let mut port_config = if let Ok(data) = read_to_string(&port_config_file) {
143-
toml::from_str(&data).into_diagnostic()?
144-
} else if let Ok(data) = read_to_string(&project_config_file) {
209+
fn load_port_config(
210+
port_config_file: &Path,
211+
project_config_file: &Path,
212+
raw_data: &str,
213+
) -> Result<PortConfig> {
214+
if let Ok(data) = read_to_string(port_config_file) {
215+
toml::from_str(&data).into_diagnostic()
216+
} else if let Ok(data) = read_to_string(project_config_file) {
145217
if data.contains("[connection]") || data.contains("[[usb_device]]") {
146218
log::info!(
147219
"espflash@3 configuration detected. Migrating port config to port_config_file: {:#?}",
148220
&port_config_file
149221
);
150-
// Write the updated configs
151-
let port_config = toml::from_str(&data).into_diagnostic()?;
152-
Self::write_config(&port_config, &port_config_file)?;
153-
Self::write_config(&project_config, &project_config_file)?;
154222

155-
port_config
223+
let port_config: PortConfig = toml::from_str(&data).into_diagnostic()?;
224+
let project_config: ProjectConfig = toml::from_str(raw_data).unwrap_or_default();
225+
226+
Self::write_config(&port_config, port_config_file)?;
227+
Self::write_config(&project_config, project_config_file)?;
228+
Ok(port_config)
156229
} else {
157-
PortConfig::default()
230+
Ok(PortConfig::default())
158231
}
159232
} else {
160-
PortConfig::default()
161-
};
162-
port_config.save_path = port_config_file;
163-
debug!("Port Config: {:#?}", &port_config);
164-
165-
Ok(Config {
166-
project_config,
167-
port_config,
168-
})
233+
Ok(PortConfig::default())
234+
}
169235
}
170236

171-
fn write_config<T: Serialize>(config: &T, path: &PathBuf) -> Result<()> {
237+
fn write_config<T: Serialize>(config: &T, path: &Path) -> Result<()> {
172238
let serialized = toml::to_string(config)
173239
.into_diagnostic()
174240
.wrap_err("Failed to serialize config")?;

espflash/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,10 @@ pub enum Error {
345345
#[error("{0}")]
346346
#[diagnostic(code(espflash::app_desc::app_descriptor_not_present))]
347347
AppDescriptorNotPresent(String),
348+
349+
/// Key is not in the expected section
350+
#[error("Misplaced key, check your configuration file. Key: {0}")]
351+
MisplacedKey(String),
348352
}
349353

350354
#[cfg(feature = "serialport")]

0 commit comments

Comments
 (0)