From 4aaeeec4ff372a9dd655fe98745275f8ef2596ef Mon Sep 17 00:00:00 2001 From: Wichert Akkerman Date: Wed, 4 Oct 2023 21:07:05 +0200 Subject: [PATCH 1/2] Use thiserror for ArchiveError --- Cargo.toml | 1 + src/types.rs | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5555000..f1c07dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ path = "src/lib.rs" [dependencies] chrono = "0.4.30" flate2 = "1.0.27" +thiserror = "1.0.49" [dev-dependencies] hex-literal = "0.4.1" diff --git a/src/types.rs b/src/types.rs index 2bc2688..d2d44e8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,6 @@ use std::fmt; use std::io; +use thiserror::Error; /// Type used for PostgreSQL version numbers pub type Version = (u8, u8, u8); @@ -8,31 +9,31 @@ pub type Version = (u8, u8, u8); /// /// Errors can be caused by underlying IO errors, unsupported features or /// invalid data. -#[derive(Debug)] +#[derive(Error, Debug)] pub enum ArchiveError { /// An IO errors occured while reading data. - IOError(io::Error), + #[error("IO error reading data")] + IOError(#[from] io::Error), /// Invalid data was found. This should only happen of the archive is /// corrupted (or pgarchive has a bug). + #[error("invalid data found in archive")] InvalidData, /// Returned when you try to read the data for a /// [`TocEntry`](crate::TocEntry), but it has no data. + #[error("TOC entry has no data")] NoDataPresent, /// pgarchive does not support reading blob data. + #[error("reading BLOB data is not supported")] BlobNotSupported, /// The archive was made by a pg_dump version that is not supported by this /// crate. + #[error("archive format {}.{}.{} is not supported", (.0).0, (.0).1, (.0).2)] UnsupportedVersionError(Version), /// An unsupported compression method was used for table data. + #[error("compression method {0} is not supported")] CompressionMethodNotSupported(CompressionMethod), } -impl From for ArchiveError { - fn from(e: io::Error) -> ArchiveError { - ArchiveError::IOError(e) - } -} - pub type Oid = u64; #[derive(Clone, Copy, PartialEq, Debug)] From 4cc62b1d4d65492773c99cceb691f1c2c10a445f Mon Sep 17 00:00:00 2001 From: Wichert Akkerman Date: Wed, 4 Oct 2023 21:16:12 +0200 Subject: [PATCH 2/2] Add a rational to InvalidData errors --- src/archive.rs | 25 ++++++++++++++++--------- src/io.rs | 2 +- src/toc.rs | 15 ++++++++++----- src/types.rs | 4 ++-- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index a481862..dd073d3 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -86,7 +86,9 @@ impl Archive { let mut buffer = vec![0; 5]; f.read_exact(buffer.as_mut_slice())?; if buffer != "PGDMP".as_bytes() { - return Err(ArchiveError::InvalidData); + return Err(ArchiveError::InvalidData( + "file does not start with PGDMP".into(), + )); } let mut io_config = ReadConfig::new(); @@ -105,24 +107,27 @@ impl Archive { if io_config.read_byte(f)? != 1 { // 1 = archCustom - return Err(ArchiveError::IOError(io::Error::new( - io::ErrorKind::Other, - "wrong file format", - ))); + return Err(ArchiveError::InvalidData( + "file format must be 1 (custom)".into(), + )); } let compression_method = if version >= (1, 15, 0) { io_config .read_byte(f)? .try_into() - .or(Err(ArchiveError::InvalidData))? + .or(Err(ArchiveError::InvalidData( + "invalid compression method".into(), + )))? } else { let compression = io_config.read_int(f)?; match compression { -1 => Ok(CompressionMethod::ZSTD), 0 => Ok(CompressionMethod::None), 1..=9 => Ok(CompressionMethod::Gzip(compression)), - _ => Err(ArchiveError::InvalidData), + _ => Err(ArchiveError::InvalidData( + "invalid compression method".into(), + )), }? }; @@ -139,9 +144,11 @@ impl Archive { created_mon as u32, created_mday as u32, ) - .ok_or(ArchiveError::InvalidData)? + .ok_or(ArchiveError::InvalidData("invalid creation date".into()))? .and_hms_opt(created_hour as u32, created_min as u32, created_sec as u32) - .ok_or(ArchiveError::InvalidData)?; + .ok_or(ArchiveError::InvalidData( + "invalid time in creation date".into(), + ))?; let database_name = io_config.read_string(f)?; let server_version = io_config.read_string(f)?; diff --git a/src/io.rs b/src/io.rs index 80ed2db..4b09e2d 100644 --- a/src/io.rs +++ b/src/io.rs @@ -100,7 +100,7 @@ impl ReadConfig { let block_type: BlockType = self .read_byte(f)? .try_into() - .or(Err(ArchiveError::InvalidData))?; + .or(Err(ArchiveError::InvalidData("invalid block type".into())))?; let _id = self.read_int(f)?; match block_type { BlockType::Blob => Err(ArchiveError::BlobNotSupported), diff --git a/src/toc.rs b/src/toc.rs index 4cc540d..747bd43 100644 --- a/src/toc.rs +++ b/src/toc.rs @@ -49,7 +49,7 @@ impl TocEntry { pub fn parse(f: &mut (impl Read + ?Sized), cfg: &ReadConfig) -> Result { let id: ID = cfg.read_int(f)?; if id < 0 { - return Err(ArchiveError::InvalidData); + return Err(ArchiveError::InvalidData("negative TOC id".into())); } let had_dumper = cfg.read_int_bool(f)?; let table_oid = cfg.read_oid(f)?; @@ -59,7 +59,9 @@ impl TocEntry { let section: Section = cfg .read_int(f)? .try_into() - .or(Err(ArchiveError::InvalidData))?; + .or(Err(ArchiveError::InvalidData( + "invalid section type".into(), + )))?; let defn = cfg.read_string(f)?; let drop_stmt = cfg.read_string(f)?; let copy_stmt = cfg.read_string(f)?; @@ -69,7 +71,9 @@ impl TocEntry { let owner = cfg.read_string(f)?; if cfg.read_string_bool(f)? { // This *must* be false - return Err(ArchiveError::InvalidData); + return Err(ArchiveError::InvalidData( + "mysterious value must be false".into(), + )); } let mut dependencies = Vec::new(); loop { @@ -77,8 +81,9 @@ impl TocEntry { if dep_id.is_empty() { break; } - dependencies - .push(ID::from_str_radix(dep_id.as_str(), 10).or(Err(ArchiveError::InvalidData))?); + dependencies.push(ID::from_str_radix(dep_id.as_str(), 10).or(Err( + ArchiveError::InvalidData("invalid dependency id".into()), + ))?); } let offset = cfg.read_offset(f)?; diff --git a/src/types.rs b/src/types.rs index d2d44e8..7cc88ea 100644 --- a/src/types.rs +++ b/src/types.rs @@ -16,8 +16,8 @@ pub enum ArchiveError { IOError(#[from] io::Error), /// Invalid data was found. This should only happen of the archive is /// corrupted (or pgarchive has a bug). - #[error("invalid data found in archive")] - InvalidData, + #[error("format error: {0}")] + InvalidData(String), /// Returned when you try to read the data for a /// [`TocEntry`](crate::TocEntry), but it has no data. #[error("TOC entry has no data")]