Skip to content

Commit 4f64f03

Browse files
committed
improve more cargo-espflash error messages
1 parent 3a5b9bc commit 4f64f03

File tree

5 files changed

+110
-29
lines changed

5 files changed

+110
-29
lines changed

cargo-espflash/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ guess_host_triple = "0.1"
3232
serde = { version = "1.0", features = ["derive"] }
3333
serial = "0.4"
3434
toml = "0.5"
35+
thiserror = "1"

cargo-espflash/src/error.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use miette::Diagnostic;
2+
use thiserror::Error;
3+
4+
#[derive(Error, Debug, Diagnostic)]
5+
#[non_exhaustive]
6+
pub enum Error {
7+
#[error("No executable artifact found")]
8+
#[diagnostic(
9+
code(cargo_espflash::no_artifact),
10+
help("If you're trying to run an example you need to specify it using the `--example` argument")
11+
)]
12+
NoArtifact,
13+
#[error("'build-std' not configured")]
14+
#[diagnostic(
15+
code(cargo_espflash::build_std),
16+
help(
17+
"cargo currently requires the unstable 'build-std' feature, ensure \
18+
that .cargo/config{{.toml}} has the appropriate options.\n \
19+
\tSee: https://doc.rust-lang.org/cargo/reference/unstable.html#build-std"
20+
)
21+
)]
22+
NoBuildStd,
23+
#[error("Multiple build artifacts found")]
24+
#[diagnostic(
25+
code(cargo_espflash::multiple_artifacts),
26+
help("Please specify which artifact to flash using --bin")
27+
)]
28+
MultipleArtifacts,
29+
}

cargo-espflash/src/main.rs

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use cargo_metadata::Message;
22
use clap::{App, Arg, SubCommand};
3-
use espflash::{Config, Error, Flasher, PartitionTable};
4-
use miette::{bail, miette, IntoDiagnostic, Result, WrapErr};
3+
use error::Error;
4+
use espflash::{Config, Flasher, PartitionTable};
5+
use miette::{IntoDiagnostic, Result, WrapErr};
56
use serial::{BaudRate, SerialPort};
67

78
use std::{
@@ -12,6 +13,8 @@ use std::{
1213
};
1314

1415
mod cargo_config;
16+
mod error;
17+
1518
use cargo_config::has_build_std;
1619

1720
fn main() -> Result<()> {
@@ -121,7 +124,7 @@ fn main() -> Result<()> {
121124
println!("Serial port: {}", port);
122125
println!("Connecting...\n");
123126
let mut serial = serial::open(&port)
124-
.map_err(Error::from)
127+
.map_err(espflash::Error::from)
125128
.wrap_err_with(|| format!("Failed to open serial port {}", port))?;
126129
serial
127130
.reconfigure(&|settings| {
@@ -161,11 +164,8 @@ fn main() -> Result<()> {
161164
let partition_table = if let Some(path) = matches.value_of("partition_table") {
162165
let path = fs::canonicalize(path).into_diagnostic()?;
163166
let data = fs::read_to_string(path).into_diagnostic()?;
164-
165-
match PartitionTable::try_from_str(data) {
166-
Ok(t) => Some(t),
167-
Err(e) => bail!("{}", e),
168-
}
167+
let table = PartitionTable::try_from_str(data)?;
168+
Some(table)
169169
} else {
170170
None
171171
};
@@ -192,11 +192,7 @@ fn build(release: bool, example: Option<&str>, features: Option<&str>) -> Result
192192
// cross-compilation. If it has not been set then we cannot build the
193193
// application.
194194
if !has_build_std(".") {
195-
bail!(
196-
"cargo currently requires the unstable 'build-std' feature, ensure \
197-
that .cargo/config{.toml} has the appropriate options.\n \
198-
See: https://doc.rust-lang.org/cargo/reference/unstable.html#build-std"
199-
);
195+
return Err(Error::NoBuildStd.into());
200196
};
201197

202198
// Build the list of arguments to pass to 'cargo build'.
@@ -239,8 +235,7 @@ fn build(release: bool, example: Option<&str>, features: Option<&str>) -> Result
239235
Message::CompilerArtifact(artifact) => {
240236
if artifact.executable.is_some() {
241237
if target_artifact.is_some() {
242-
// We found multiple binary artifacts, so we don't know which one to use.
243-
bail!("Multiple artifacts found, please specify one with --bin");
238+
return Err(Error::MultipleArtifacts.into());
244239
} else {
245240
target_artifact = Some(artifact);
246241
}
@@ -264,16 +259,9 @@ fn build(release: bool, example: Option<&str>, features: Option<&str>) -> Result
264259
}
265260

266261
// If no target artifact was found, we don't have a path to return.
267-
if target_artifact.is_none() {
268-
bail!("Artifact not found");
269-
}
262+
let target_artifact = target_artifact.ok_or(Error::NoArtifact)?;
270263

271-
let artifact_path = PathBuf::from(
272-
target_artifact
273-
.unwrap()
274-
.executable
275-
.ok_or_else(|| miette!("artifact executable path is missing"))?,
276-
);
264+
let artifact_path = target_artifact.executable.unwrap().into();
277265

278266
Ok(artifact_path)
279267
}

espflash/src/error.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use miette::Diagnostic;
1+
use csv::Position;
2+
use miette::{Diagnostic, SourceOffset, SourceSpan};
23
use slip_codec::Error as SlipError;
34
use std::io;
45
use thiserror::Error;
@@ -41,6 +42,9 @@ pub enum Error {
4142
#[error("Failed to connect to on-device flash")]
4243
#[diagnostic(code(espflash::flash_connect))]
4344
FlashConnect,
45+
#[error(transparent)]
46+
#[diagnostic(transparent)]
47+
MalformedPartitionTable(PartitionTableError),
4448
}
4549

4650
#[derive(Error, Debug, Diagnostic)]
@@ -204,3 +208,60 @@ impl<T> ResultExt for Result<T, Error> {
204208
}
205209
}
206210
}
211+
212+
#[derive(Debug, Error, Diagnostic)]
213+
#[error("Malformed partition table")]
214+
#[diagnostic(
215+
code(espflash::mallformed_partition_table),
216+
help("See the espressif documentation for information on the partition table format:
217+
218+
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#creating-custom-tables")
219+
)]
220+
pub struct PartitionTableError {
221+
source: String,
222+
#[snippet(source)]
223+
snip: SourceSpan,
224+
#[highlight(snip, label("{}", self.hint))]
225+
err_span: SourceSpan,
226+
hint: String,
227+
#[source]
228+
error: csv::Error,
229+
}
230+
231+
impl PartitionTableError {
232+
pub fn new(error: csv::Error, source: String) -> Self {
233+
let err_pos = match error.kind() {
234+
csv::ErrorKind::Deserialize { pos: Some(pos), .. } => pos.clone(),
235+
csv::ErrorKind::UnequalLengths { pos: Some(pos), .. } => pos.clone(),
236+
_ => Position::new(),
237+
};
238+
let hint = match error.kind() {
239+
csv::ErrorKind::Deserialize { err, .. } => err.to_string(),
240+
csv::ErrorKind::UnequalLengths {
241+
expected_len, len, ..
242+
} => format!(
243+
"record has {} fields, but the previous record has {} fields",
244+
len, expected_len
245+
),
246+
_ => String::new(),
247+
};
248+
let snip_start =
249+
SourceOffset::from_location(&source, err_pos.line().saturating_sub(2) as usize, 0);
250+
let snip_end =
251+
SourceOffset::from_location(&source, err_pos.line().saturating_add(2) as usize, 0);
252+
let snip = SourceSpan::new(snip_start, (snip_end.offset() - snip_start.offset()).into());
253+
let err_span = SourceSpan::new(pos_to_offset(err_pos), 0.into());
254+
255+
PartitionTableError {
256+
source,
257+
err_span,
258+
snip,
259+
hint,
260+
error,
261+
}
262+
}
263+
}
264+
265+
fn pos_to_offset(pos: Position) -> SourceOffset {
266+
(pos.byte() as usize + 1).into()
267+
}

espflash/src/partition_table.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use md5::{Context, Digest};
22
use regex::Regex;
33
use serde::{Deserialize, Deserializer};
44

5-
use std::{error::Error, io::Write};
5+
use crate::error::PartitionTableError;
6+
use std::io::Write;
67

78
const MAX_PARTITION_LENGTH: usize = 0xC00;
89
const PARTITION_TABLE_SIZE: usize = 0x1000;
@@ -147,9 +148,9 @@ impl PartitionTable {
147148
}
148149

149150
/// Attempt to parse a partition table from the given string. For more
150-
/// information on the paritition table CSV format see:
151+
/// information on the partition table CSV format see:
151152
/// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html
152-
pub fn try_from_str<S: Into<String>>(data: S) -> Result<Self, Box<dyn Error>> {
153+
pub fn try_from_str<S: Into<String>>(data: S) -> Result<Self, PartitionTableError> {
153154
let data = data.into();
154155
let mut reader = csv::ReaderBuilder::new()
155156
.comment(Some(b'#'))
@@ -159,7 +160,8 @@ impl PartitionTable {
159160

160161
let mut partitions = Vec::with_capacity(MAX_PARTITION_TABLE_ENTRIES);
161162
for partition in reader.deserialize() {
162-
let partition: Partition = partition?;
163+
let partition: Partition =
164+
partition.map_err(|e| PartitionTableError::new(e, data.clone()))?;
163165
partitions.push(partition);
164166
}
165167

0 commit comments

Comments
 (0)