Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions cargo-espflash/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,11 @@ fn main() -> Result<()> {
.or_else(|| metadata.partition_table.as_deref())
{
let path = fs::canonicalize(path).into_diagnostic()?;
let data = fs::read_to_string(path).into_diagnostic()?;
let table = PartitionTable::try_from_str(data)?;
let data = fs::read_to_string(path)
.into_diagnostic()
.wrap_err("Failed to open partition table")?;
let table =
PartitionTable::try_from_str(data).wrap_err("Failed to parse partition table")?;
Some(table)
} else {
None
Expand Down
1 change: 1 addition & 0 deletions espflash/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ slip-codec = "0.2.4"
thiserror = "1.0.20"
xmas-elf = "0.8.0"
serde = { version = "1.0", features = ["derive"] }
serde_plain = "1"
toml = "0.5"
directories-next = "2.0.0"
color-eyre = "0.5"
Expand Down
172 changes: 150 additions & 22 deletions espflash/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::flasher::Command;
use crate::image_format::ImageFormatId;
use crate::partition_table::{SubType, Type};
use crate::Chip;
use csv::Position;
use miette::{Diagnostic, SourceOffset, SourceSpan};
use slip_codec::Error as SlipError;
use std::fmt::{Display, Formatter};
Expand Down Expand Up @@ -266,32 +266,52 @@ impl<T> ResultExt for Result<T, Error> {
}
}

#[derive(Debug, Error, Diagnostic)]
pub enum PartitionTableError {
#[error(transparent)]
#[diagnostic(transparent)]
Csv(#[from] CSVError),
#[error(transparent)]
#[diagnostic(transparent)]
Overlapping(#[from] OverlappingPartitionsError),
#[error(transparent)]
#[diagnostic(transparent)]
Duplicate(#[from] DuplicatePartitionsError),
#[error(transparent)]
#[diagnostic(transparent)]
InvalidSubType(#[from] InvalidSubTypeError),
#[error(transparent)]
#[diagnostic(transparent)]
UnalignedPartitionError(#[from] UnalignedPartitionError),
}

#[derive(Debug, Error, Diagnostic)]
#[error("Malformed partition table")]
#[diagnostic(
code(espflash::mallformed_partition_table),
help("See the espressif documentation for information on the partition table format:
code(espflash::partition_table::mallformed),
help("{}See the espressif documentation for information on the partition table format:

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#creating-custom-tables")
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#creating-custom-tables", self.help)
)]
pub struct PartitionTableError {
pub struct CSVError {
#[source_code]
source: String,
#[label("{}", self.hint)]
err_span: SourceSpan,
hint: String,
#[source]
error: csv::Error,
help: String,
}

impl PartitionTableError {
impl CSVError {
pub fn new(error: csv::Error, source: String) -> Self {
let err_pos = match error.kind() {
csv::ErrorKind::Deserialize { pos: Some(pos), .. } => pos.clone(),
csv::ErrorKind::UnequalLengths { pos: Some(pos), .. } => pos.clone(),
_ => Position::new(),
let err_line = match error.kind() {
csv::ErrorKind::Deserialize { pos: Some(pos), .. } => pos.line(),
csv::ErrorKind::UnequalLengths { pos: Some(pos), .. } => pos.line(),
_ => 0,
};
let hint = match error.kind() {
let mut hint = match error.kind() {
csv::ErrorKind::Deserialize { err, .. } => err.to_string(),
csv::ErrorKind::UnequalLengths {
expected_len, len, ..
Expand All @@ -301,27 +321,135 @@ impl PartitionTableError {
),
_ => String::new(),
};
let mut help = String::new();

// string matching is fragile but afaik there is no better way in this case
// and if it does break the error is still not bad
if hint == "data did not match any variant of untagged enum SubType" {
hint = "Unknown sub-type".into();
help = format!(
"the following sub-types are supported:
{} for data partitions
{} for app partitions\n\n",
Type::Data.subtype_hint(),
Type::App.subtype_hint()
)
}

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

PartitionTableError {
CSVError {
source,
err_span,
hint,
error,
help,
}
}
}

/// since csv doesn't give us the position in the line the error occurs, we highlight the entire line
///
/// line starts at 1
fn line_to_span(source: &str, line: usize) -> SourceSpan {
let line_length = source.lines().nth(line - 1).unwrap().len().into();
SourceSpan::new(SourceOffset::from_location(source, line, 2), line_length)
}

#[derive(Debug, Error, Diagnostic)]
#[error("Overlapping partitions")]
#[diagnostic(code(espflash::partition_table::overlapping))]
pub struct OverlappingPartitionsError {
#[source_code]
source_code: String,
#[label("This partition")]
partition1_span: SourceSpan,
#[label("overlaps with this partition")]
partition2_span: SourceSpan,
}

impl OverlappingPartitionsError {
pub fn new(source: &str, partition1_line: usize, partition2_line: usize) -> Self {
OverlappingPartitionsError {
source_code: source.into(),
partition1_span: line_to_span(source, partition1_line),
partition2_span: line_to_span(source, partition2_line),
}
}
}

fn pos_to_offset(pos: Position) -> SourceOffset {
(pos.byte() as usize).into()
#[derive(Debug, Error, Diagnostic)]
#[error("Duplicate partitions")]
#[diagnostic(code(espflash::partition_table::duplicate))]
pub struct DuplicatePartitionsError {
#[source_code]
source_code: String,
#[label("This partition")]
partition1_span: SourceSpan,
#[label("has the same {} as this partition", self.ty)]
partition2_span: SourceSpan,
ty: &'static str,
}

impl DuplicatePartitionsError {
pub fn new(
source: &str,
partition1_line: usize,
partition2_line: usize,
ty: &'static str,
) -> Self {
DuplicatePartitionsError {
source_code: source.into(),
partition1_span: line_to_span(source, partition1_line),
partition2_span: line_to_span(source, partition2_line),
ty,
}
}
}

#[derive(Debug, Error, Diagnostic)]
#[error("Invalid subtype for type")]
#[diagnostic(
code(espflash::partition_table::invalid_type),
help("'{}' supports the following subtypes: {}", self.ty, self.ty.subtype_hint())
)]
pub struct InvalidSubTypeError {
#[source_code]
source_code: String,
#[label("'{}' is not a valid subtype for '{}'", self.sub_type, self.ty)]
span: SourceSpan,
ty: Type,
sub_type: SubType,
}

impl InvalidSubTypeError {
pub fn new(source: &str, line: usize, ty: Type, sub_type: SubType) -> Self {
InvalidSubTypeError {
source_code: source.into(),
span: line_to_span(source, line),
ty,
sub_type,
}
}
}

#[derive(Debug, Error, Diagnostic)]
#[error("Unaligned partition")]
#[diagnostic(code(espflash::partition_table::unaligned))]
pub struct UnalignedPartitionError {
#[source_code]
source_code: String,
#[label("App partition is not aligned to 64k (0x10000)")]
span: SourceSpan,
}

impl UnalignedPartitionError {
pub fn new(source: &str, line: usize) -> Self {
UnalignedPartitionError {
source_code: source.into(),
span: line_to_span(source, line),
}
}
}

#[derive(Debug, Error)]
Expand Down
Loading