From 81f657d119ae84c37a3ea9633d1162b3a9b19d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20L=C3=B6thberg?= Date: Wed, 27 Jul 2022 10:29:36 +0200 Subject: [PATCH 1/2] Move parsing either CSV or binary partition table into own associated function Since this is done in multiple places it makes sense for PartitionTable to just offer this as an associated function. --- espflash/src/cli/mod.rs | 30 ++++------------ espflash/src/partition_table.rs | 63 +++++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 27 deletions(-) diff --git a/espflash/src/cli/mod.rs b/espflash/src/cli/mod.rs index 7ed5e817..e57c3c7e 100644 --- a/espflash/src/cli/mod.rs +++ b/espflash/src/cli/mod.rs @@ -279,26 +279,14 @@ pub fn flash_elf_image( }; // If the '--partition-table' option is provided, load the partition table from - // the CSV at the specified path. + // the CSV or binary file at the specified path. let partition_table = if let Some(path) = partition_table { let path = fs::canonicalize(path).into_diagnostic()?; - // If a partition table was detected from ESP-IDF (eg. using `esp-idf-sys`) then - // it will be passed in its _binary_ form. Otherwise, it will be provided as a - // CSV. - let table = if path.extension().map(|e| e.to_str().unwrap()) == Some("csv") { - let data = fs::read_to_string(path) - .into_diagnostic() - .wrap_err("Failed to open partition table")?; - - PartitionTable::try_from_str(data).wrap_err("Failed to parse partition table")? - } else { - let data = fs::read(path) - .into_diagnostic() - .wrap_err("Failed to open partition table")?; - - PartitionTable::try_from_bytes(data).wrap_err("Failed to parse partition table")? - }; + let data = fs::read(path) + .into_diagnostic() + .wrap_err("Failed to open partition table")?; + let table = PartitionTable::try_from(data).wrap_err("Failed to parse partition table")?; Some(table) } else { @@ -350,13 +338,7 @@ pub fn partition_table(opts: PartitionTableOpts) -> Result<()> { // Try getting the partition table from either the csv or the binary representation and // fail otherwise. - let part_table = if let Ok(part_table) = - PartitionTable::try_from_bytes(input.clone()).into_diagnostic() - { - part_table - } else if let Ok(part_table) = - PartitionTable::try_from_str(String::from_utf8(input).into_diagnostic()?) - { + let part_table = if let Ok(part_table) = PartitionTable::try_from(input).into_diagnostic() { part_table } else { return Err((InvalidPartitionTable {}).into()); diff --git a/espflash/src/partition_table.rs b/espflash/src/partition_table.rs index 2579f1a6..952c98da 100644 --- a/espflash/src/partition_table.rs +++ b/espflash/src/partition_table.rs @@ -14,9 +14,9 @@ use strum::IntoEnumIterator; use strum_macros::EnumIter; use crate::error::{ - CSVError, DuplicatePartitionsError, InvalidChecksum, InvalidSubTypeError, - LengthNotMultipleOf32, NoAppError, NoEndMarker, OverlappingPartitionsError, - PartitionTableError, UnalignedPartitionError, + CSVError, DuplicatePartitionsError, InvalidChecksum, InvalidPartitionTable, + InvalidSubTypeError, LengthNotMultipleOf32, NoAppError, NoEndMarker, + OverlappingPartitionsError, PartitionTableError, UnalignedPartitionError, }; const MAX_PARTITION_LENGTH: usize = 0xC00; @@ -219,6 +219,30 @@ impl PartitionTable { } } + /// Attempt to parse either a binary or CSV partition table from the given input. + /// + /// For more information on the partition table format see: + /// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html + pub fn try_from(data: S) -> Result + where + S: Into>, + { + let input: Vec = data.into(); + + // If a partition table was detected from ESP-IDF (eg. using `esp-idf-sys`) then + // it will be passed in its _binary_ form. Otherwise, it will be provided as a + // CSV. + if let Ok(part_table) = Self::try_from_bytes(&*input) { + Ok(part_table) + } else if let Ok(part_table) = + Self::try_from_str(String::from_utf8(input).map_err(|_| InvalidPartitionTable)?) + { + Ok(part_table) + } else { + Err(InvalidPartitionTable.into()) + } + } + /// Attempt to parse a CSV partition table from the given string. /// /// For more information on the partition table format see: @@ -812,6 +836,39 @@ phy_init, data, phy, 0xf000, 0x1000, assert_eq!(expected, result.as_slice()); } + #[test] + fn test_from() { + let pt0 = PartitionTable::try_from(PTABLE_0); + assert!(pt0.is_ok()); + + let pt0 = pt0.unwrap(); + let nvs = pt0.find("nvs").unwrap(); + let fac = pt0.find("factory").unwrap(); + assert_eq!(nvs.flags(), None); + assert_eq!(fac.flags(), Some(Flags::Encrypted)); + + let pt1 = PartitionTable::try_from(PTABLE_1); + assert!(pt1.is_ok()); + + let pt_spiffs = PartitionTable::try_from(PTABLE_SPIFFS); + assert!(pt_spiffs.is_ok()); + + PartitionTable::try_from(PTABLE_NO_FACTORY) + .expect("Failed to parse partition table without factory partition"); + + PartitionTable::try_from(PTABLE_NO_APP) + .expect_err("Failed to reject partition table without factory or ota partition"); + + use std::fs::{read, read_to_string}; + let binary_table = read("./tests/data/partitions.bin").unwrap(); + let binary_parsed = PartitionTable::try_from_bytes(binary_table).unwrap(); + + let csv_table = read_to_string("./tests/data/partitions.csv").unwrap(); + let csv_parsed = PartitionTable::try_from(csv_table).unwrap(); + + assert_eq!(binary_parsed, csv_parsed); + } + #[test] fn test_from_str() { let pt0 = PartitionTable::try_from_str(PTABLE_0); From 73f4505672cff0b743a33fe124b61cfc830e3c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20L=C3=B6thberg?= Date: Wed, 27 Jul 2022 10:30:46 +0200 Subject: [PATCH 2/2] Make save-image able to use binary partition tables Fixes esp-rs/espflash#211 --- espflash/src/cli/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/espflash/src/cli/mod.rs b/espflash/src/cli/mod.rs index e57c3c7e..5b9f7971 100644 --- a/espflash/src/cli/mod.rs +++ b/espflash/src/cli/mod.rs @@ -178,15 +178,15 @@ pub fn save_elf_as_image( }; // If the '-T' option is provided, load the partition table from - // the CSV at the specified path. + // the CSV or binary file at the specified path. let partition_table = if let Some(partition_table_path) = partition_table_path { let path = fs::canonicalize(partition_table_path).into_diagnostic()?; - let data = fs::read_to_string(path) + let data = fs::read(path) .into_diagnostic() .wrap_err("Failed to open partition table")?; let table = - PartitionTable::try_from_str(data).wrap_err("Failed to parse partition table")?; + PartitionTable::try_from(data).wrap_err("Failed to parse partition table")?; Some(table) } else {