use std::cell::RefCell;
#[derive(Debug, Clone, PartialEq)]
pub struct ParseWarning {
pub phase: ParsePhase,
pub location: Option<LocationHint>,
pub message: String,
pub severity: Severity,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParsePhase {
Metadata,
ViewerPreferences,
Outline,
Destinations,
Annotations {
page: usize,
},
Form,
PageBoxes {
page: usize,
},
EmbeddedFiles,
Layers,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum LocationHint {
Page(usize),
Object { obj_num: u32, gen_num: u16 },
FieldName(String),
OutlineTitle(String),
Name(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Severity {
Info,
Warning,
Error,
}
#[derive(Debug, Default)]
pub struct WarningSink {
inner: RefCell<Vec<ParseWarning>>,
}
impl WarningSink {
pub fn new() -> Self {
Self::default()
}
pub fn push(&self, w: ParseWarning) {
self.inner.borrow_mut().push(w);
}
pub fn record(
&self,
phase: ParsePhase,
location: Option<LocationHint>,
severity: Severity,
message: impl Into<String>,
) {
self.push(ParseWarning {
phase,
location,
severity,
message: message.into(),
});
}
pub fn borrow_slice(&self) -> std::cell::Ref<'_, [ParseWarning]> {
std::cell::Ref::map(self.inner.borrow(), Vec::as_slice)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn record_and_borrow() {
let sink = WarningSink::new();
sink.record(
ParsePhase::Outline,
Some(LocationHint::OutlineTitle("Chapter 1".to_string())),
Severity::Warning,
"cycle detected; truncating",
);
sink.record(
ParsePhase::Annotations { page: 3 },
None,
Severity::Info,
"missing /Rect; entry skipped",
);
let view = sink.borrow_slice();
assert_eq!(view.len(), 2);
assert_eq!(view[0].phase, ParsePhase::Outline);
assert_eq!(view[0].severity, Severity::Warning);
assert_eq!(view[1].phase, ParsePhase::Annotations { page: 3 });
assert_eq!(view[1].severity, Severity::Info);
}
#[test]
fn location_hint_variants_are_distinguishable() {
let p = LocationHint::Page(5);
let o = LocationHint::Object {
obj_num: 42,
gen_num: 0,
};
let f = LocationHint::FieldName("user.email".to_string());
let n = LocationHint::Name("attachment.csv".to_string());
assert_ne!(p, o);
assert_ne!(o, f);
assert_ne!(f, n);
}
}