use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("path not found: {0}")]
NotFound(PathBuf),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("container error: {0}")]
Container(String),
#[error("required stream missing: {0}")]
MissingStream(String),
#[error("decompression failed in stream {stream}: {source}")]
Decompress {
stream: String,
#[source]
source: std::io::Error,
},
#[error("invalid file header: {0}")]
InvalidHeader(String),
#[error("record parse error: {0}")]
Record(String),
}
#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)]
pub struct Warning {
pub code: WarningCode,
pub message: String,
pub location: Option<Location>,
}
#[derive(Debug, Clone, Copy, serde::Serialize, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum WarningCode {
UnknownRecordTag,
TruncatedRecord,
UnsupportedContent,
ShapeRefMissing,
}
#[derive(Debug, Clone, Default, serde::Serialize, PartialEq, Eq)]
pub struct Location {
pub section: Option<usize>,
pub paragraph: Option<usize>,
pub offset: Option<u64>,
}
#[derive(Debug, Default)]
pub struct WarningCollector {
pub warnings: Vec<Warning>,
}
impl WarningCollector {
pub fn push(
&mut self,
code: WarningCode,
message: impl Into<String>,
location: Option<Location>,
) {
self.warnings.push(Warning {
code,
message: message.into(),
location,
});
}
pub fn take(&mut self) -> Vec<Warning> {
std::mem::take(&mut self.warnings)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn collector_accumulates_and_drains() {
let mut c = WarningCollector::default();
c.push(WarningCode::UnknownRecordTag, "tag=0x999", None);
c.push(
WarningCode::TruncatedRecord,
"ran out at offset 42",
Some(Location {
section: Some(0),
paragraph: Some(3),
offset: Some(42),
}),
);
assert_eq!(c.warnings.len(), 2);
let taken = c.take();
assert_eq!(taken.len(), 2);
assert!(c.warnings.is_empty());
}
#[test]
fn warning_serializes_with_screaming_code() {
let w = Warning {
code: WarningCode::UnknownRecordTag,
message: "x".into(),
location: None,
};
let s = serde_json::to_string(&w).unwrap();
assert!(s.contains("\"UNKNOWN_RECORD_TAG\""));
}
}