Skip to content

Commit 91e8ec6

Browse files
authored
Merge pull request #64 from esp-rs/partition-table-validation
Partition table validation
2 parents 2156737 + dd61da1 commit 91e8ec6

File tree

4 files changed

+288
-37
lines changed

4 files changed

+288
-37
lines changed

cargo-espflash/src/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,11 @@ fn main() -> Result<()> {
174174
.or_else(|| metadata.partition_table.as_deref())
175175
{
176176
let path = fs::canonicalize(path).into_diagnostic()?;
177-
let data = fs::read_to_string(path).into_diagnostic()?;
178-
let table = PartitionTable::try_from_str(data)?;
177+
let data = fs::read_to_string(path)
178+
.into_diagnostic()
179+
.wrap_err("Failed to open partition table")?;
180+
let table =
181+
PartitionTable::try_from_str(data).wrap_err("Failed to parse partition table")?;
179182
Some(table)
180183
} else {
181184
None

espflash/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ slip-codec = "0.2.4"
2626
thiserror = "1.0.20"
2727
xmas-elf = "0.8.0"
2828
serde = { version = "1.0", features = ["derive"] }
29+
serde_plain = "1"
2930
toml = "0.5"
3031
directories-next = "2.0.0"
3132
color-eyre = "0.5"

espflash/src/error.rs

Lines changed: 150 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::flasher::Command;
22
use crate::image_format::ImageFormatId;
3+
use crate::partition_table::{SubType, Type};
34
use crate::Chip;
4-
use csv::Position;
55
use miette::{Diagnostic, SourceOffset, SourceSpan};
66
use slip_codec::Error as SlipError;
77
use std::fmt::{Display, Formatter};
@@ -266,32 +266,52 @@ impl<T> ResultExt for Result<T, Error> {
266266
}
267267
}
268268

269+
#[derive(Debug, Error, Diagnostic)]
270+
pub enum PartitionTableError {
271+
#[error(transparent)]
272+
#[diagnostic(transparent)]
273+
Csv(#[from] CSVError),
274+
#[error(transparent)]
275+
#[diagnostic(transparent)]
276+
Overlapping(#[from] OverlappingPartitionsError),
277+
#[error(transparent)]
278+
#[diagnostic(transparent)]
279+
Duplicate(#[from] DuplicatePartitionsError),
280+
#[error(transparent)]
281+
#[diagnostic(transparent)]
282+
InvalidSubType(#[from] InvalidSubTypeError),
283+
#[error(transparent)]
284+
#[diagnostic(transparent)]
285+
UnalignedPartitionError(#[from] UnalignedPartitionError),
286+
}
287+
269288
#[derive(Debug, Error, Diagnostic)]
270289
#[error("Malformed partition table")]
271290
#[diagnostic(
272-
code(espflash::mallformed_partition_table),
273-
help("See the espressif documentation for information on the partition table format:
291+
code(espflash::partition_table::mallformed),
292+
help("{}See the espressif documentation for information on the partition table format:
274293
275-
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#creating-custom-tables")
294+
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#creating-custom-tables", self.help)
276295
)]
277-
pub struct PartitionTableError {
296+
pub struct CSVError {
278297
#[source_code]
279298
source: String,
280299
#[label("{}", self.hint)]
281300
err_span: SourceSpan,
282301
hint: String,
283302
#[source]
284303
error: csv::Error,
304+
help: String,
285305
}
286306

287-
impl PartitionTableError {
307+
impl CSVError {
288308
pub fn new(error: csv::Error, source: String) -> Self {
289-
let err_pos = match error.kind() {
290-
csv::ErrorKind::Deserialize { pos: Some(pos), .. } => pos.clone(),
291-
csv::ErrorKind::UnequalLengths { pos: Some(pos), .. } => pos.clone(),
292-
_ => Position::new(),
309+
let err_line = match error.kind() {
310+
csv::ErrorKind::Deserialize { pos: Some(pos), .. } => pos.line(),
311+
csv::ErrorKind::UnequalLengths { pos: Some(pos), .. } => pos.line(),
312+
_ => 0,
293313
};
294-
let hint = match error.kind() {
314+
let mut hint = match error.kind() {
295315
csv::ErrorKind::Deserialize { err, .. } => err.to_string(),
296316
csv::ErrorKind::UnequalLengths {
297317
expected_len, len, ..
@@ -301,27 +321,135 @@ impl PartitionTableError {
301321
),
302322
_ => String::new(),
303323
};
324+
let mut help = String::new();
325+
326+
// string matching is fragile but afaik there is no better way in this case
327+
// and if it does break the error is still not bad
328+
if hint == "data did not match any variant of untagged enum SubType" {
329+
hint = "Unknown sub-type".into();
330+
help = format!(
331+
"the following sub-types are supported:
332+
{} for data partitions
333+
{} for app partitions\n\n",
334+
Type::Data.subtype_hint(),
335+
Type::App.subtype_hint()
336+
)
337+
}
304338

305-
// since csv doesn't give us the position in the line the error occurs, we highlight the entire line
306-
let line_length = source
307-
.lines()
308-
.nth(err_pos.line() as usize - 1)
309-
.unwrap()
310-
.len()
311-
.into();
312-
let err_span = SourceSpan::new(pos_to_offset(err_pos), line_length);
339+
let err_span = line_to_span(&source, err_line as usize);
313340

314-
PartitionTableError {
341+
CSVError {
315342
source,
316343
err_span,
317344
hint,
318345
error,
346+
help,
347+
}
348+
}
349+
}
350+
351+
/// since csv doesn't give us the position in the line the error occurs, we highlight the entire line
352+
///
353+
/// line starts at 1
354+
fn line_to_span(source: &str, line: usize) -> SourceSpan {
355+
let line_length = source.lines().nth(line - 1).unwrap().len().into();
356+
SourceSpan::new(SourceOffset::from_location(source, line, 2), line_length)
357+
}
358+
359+
#[derive(Debug, Error, Diagnostic)]
360+
#[error("Overlapping partitions")]
361+
#[diagnostic(code(espflash::partition_table::overlapping))]
362+
pub struct OverlappingPartitionsError {
363+
#[source_code]
364+
source_code: String,
365+
#[label("This partition")]
366+
partition1_span: SourceSpan,
367+
#[label("overlaps with this partition")]
368+
partition2_span: SourceSpan,
369+
}
370+
371+
impl OverlappingPartitionsError {
372+
pub fn new(source: &str, partition1_line: usize, partition2_line: usize) -> Self {
373+
OverlappingPartitionsError {
374+
source_code: source.into(),
375+
partition1_span: line_to_span(source, partition1_line),
376+
partition2_span: line_to_span(source, partition2_line),
319377
}
320378
}
321379
}
322380

323-
fn pos_to_offset(pos: Position) -> SourceOffset {
324-
(pos.byte() as usize).into()
381+
#[derive(Debug, Error, Diagnostic)]
382+
#[error("Duplicate partitions")]
383+
#[diagnostic(code(espflash::partition_table::duplicate))]
384+
pub struct DuplicatePartitionsError {
385+
#[source_code]
386+
source_code: String,
387+
#[label("This partition")]
388+
partition1_span: SourceSpan,
389+
#[label("has the same {} as this partition", self.ty)]
390+
partition2_span: SourceSpan,
391+
ty: &'static str,
392+
}
393+
394+
impl DuplicatePartitionsError {
395+
pub fn new(
396+
source: &str,
397+
partition1_line: usize,
398+
partition2_line: usize,
399+
ty: &'static str,
400+
) -> Self {
401+
DuplicatePartitionsError {
402+
source_code: source.into(),
403+
partition1_span: line_to_span(source, partition1_line),
404+
partition2_span: line_to_span(source, partition2_line),
405+
ty,
406+
}
407+
}
408+
}
409+
410+
#[derive(Debug, Error, Diagnostic)]
411+
#[error("Invalid subtype for type")]
412+
#[diagnostic(
413+
code(espflash::partition_table::invalid_type),
414+
help("'{}' supports the following subtypes: {}", self.ty, self.ty.subtype_hint())
415+
)]
416+
pub struct InvalidSubTypeError {
417+
#[source_code]
418+
source_code: String,
419+
#[label("'{}' is not a valid subtype for '{}'", self.sub_type, self.ty)]
420+
span: SourceSpan,
421+
ty: Type,
422+
sub_type: SubType,
423+
}
424+
425+
impl InvalidSubTypeError {
426+
pub fn new(source: &str, line: usize, ty: Type, sub_type: SubType) -> Self {
427+
InvalidSubTypeError {
428+
source_code: source.into(),
429+
span: line_to_span(source, line),
430+
ty,
431+
sub_type,
432+
}
433+
}
434+
}
435+
436+
#[derive(Debug, Error, Diagnostic)]
437+
#[error("Unaligned partition")]
438+
#[diagnostic(code(espflash::partition_table::unaligned))]
439+
pub struct UnalignedPartitionError {
440+
#[source_code]
441+
source_code: String,
442+
#[label("App partition is not aligned to 64k (0x10000)")]
443+
span: SourceSpan,
444+
}
445+
446+
impl UnalignedPartitionError {
447+
pub fn new(source: &str, line: usize) -> Self {
448+
UnalignedPartitionError {
449+
source_code: source.into(),
450+
span: line_to_span(source, line),
451+
}
452+
}
325453
}
326454

327455
#[derive(Debug, Error)]

0 commit comments

Comments
 (0)