Files
craft-libs/crates/psd/src/error.rs
T
Dylan M. Taylor ee78a9bd46 craft-psd: Photoshop PSD/PSB reader and writer, moved from PhotoCraft (#875)
PhotoCraft's `photocraft-psd` crate (crates/psd at storytold/photocraft
63b641b55e54bf8f5c83efd977fdb16a2c255431) as `craft-psd`: the lossless
PSD/PSB model, reader, writer and builder, descriptors, the Photoshop
resource files that share its primitives (.abr, .asl, .aco, .act, .ase,
.grd, .pat, .atn, .csh, .shc) and the Photoshop blocks of a layered TIFF.
The crate had no dependency on any other PhotoCraft crate.

Code is unchanged apart from the crate name, craft-libs' rustfmt width,
comments that named other PhotoCraft crates, issue references (now
`photocraft#N`), and the corpus tests' directory (`CRAFT_PSD_CORPUS` or
`corpus/` at the workspace root). Tests, proptests, fuzz targets and
fixtures move with it.

Part of #778, #786.
2026-11-11 17:00:33 -05:00

77 lines
2.8 KiB
Rust

//! Error type for every fallible operation in this crate.
use thiserror::Error;
/// Errors produced while parsing, decoding, encoding or writing PSD/PSB data.
///
/// Parsers never panic on malformed input; every failure is reported through
/// this type.
#[derive(Debug, Error, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum PsdError {
/// The input ended before a structure was complete.
#[error("unexpected end of data at offset {offset} (needed {needed} more bytes)")]
UnexpectedEof {
/// Offset (relative to the current section) where the read started.
offset: usize,
/// Number of bytes that were requested but not available.
needed: usize,
},
/// A four-byte signature did not match.
#[error("invalid signature {found:?}, expected {expected}")]
InvalidSignature {
/// Human-readable description of the expected signature(s).
expected: &'static str,
/// The bytes that were found.
found: [u8; 4],
},
/// Header version is neither 1 (PSD) nor 2 (PSB).
#[error("unsupported file version {0}")]
UnsupportedVersion(u16),
/// A dimension, count or allocation exceeded the configured safety limits.
#[error("limit exceeded: {0}")]
LimitExceeded(&'static str),
/// A buffer could not be allocated: the system has no memory for it. Unlike
/// [`PsdError::LimitExceeded`] the data may be valid, so the caller can retry smaller.
#[error("out of memory: {0}")]
OutOfMemory(&'static str),
/// Structurally invalid data.
#[error("invalid data: {0}")]
Invalid(String),
/// A compressed stream could not be decoded.
#[error("decompression failed: {0}")]
Decompress(String),
/// Reading from a [`crate::source::ReadAt`] source failed.
#[error("read failed: {0}")]
Read(String),
/// An operation is not supported for this data (e.g. unknown compression).
#[error("unsupported: {0}")]
Unsupported(String),
}
impl PsdError {
pub(crate) fn invalid(msg: impl Into<String>) -> Self {
PsdError::Invalid(msg.into())
}
/// The error a streaming write ([`crate::PsdFile::write_to`]) failed with: the serializer's
/// own error when it was one, otherwise the I/O error's text.
pub(crate) fn from_io(e: std::io::Error) -> Self {
let text = e.to_string();
match e.into_inner().map(|inner| inner.downcast::<PsdError>()) {
Some(Ok(e)) => *e,
_ => PsdError::Invalid(format!("write failed: {text}")),
}
}
}
/// A serializer error inside a streaming write ([`crate::PsdFile::write_to`]), as `InvalidData`.
impl From<PsdError> for std::io::Error {
fn from(e: PsdError) -> Self {
std::io::Error::new(std::io::ErrorKind::InvalidData, e)
}
}
/// Convenience alias.
pub type Result<T, E = PsdError> = std::result::Result<T, E>;