Skip to main content

caj2pdf_core/operations/
observe.rs

1// SPDX-License-Identifier: MIT
2
3//! A caller's [`Progress`] as the format engines see it: a [`Cancellation`],
4//! and a document source that reports its furthest byte read.
5
6use super::{Detection, InputFormat, detect_source};
7use crate::{Cancellation, ErrorKind, Limits, NeverCancel, RangedSource, Result};
8use std::cell::RefCell;
9
10/// What a facade operation tells its caller while it runs, and how the
11/// caller stops it. Every method has a default; [`NeverCancel`] uses them
12/// all.
13pub trait Progress {
14    /// The input family, given in the options or detected from the leading
15    /// signature; `None` when the input is empty or unrecognized. Called once,
16    /// before the operation refuses the family or reads past the signature.
17    fn format(&mut self, _format: Option<InputFormat>) {}
18
19    /// The furthest document byte read so far advanced to `done` of the
20    /// document's `total` bytes. Format engines read their indexes first and
21    /// then page payloads in order, so this advances with the operation.
22    fn input_read(&mut self, _done: u64, _total: u64) {}
23
24    /// Whether the caller asked to stop; checked between rows, pages and I/O
25    /// chunks. A cancelled operation fails with [`ErrorKind::Cancelled`] and
26    /// does not undo bytes already written.
27    fn is_cancelled(&self) -> bool {
28        false
29    }
30}
31
32/// Reports nothing and never cancels.
33impl Progress for NeverCancel {}
34
35/// The refusal of an empty, unrecognized or unaccepted input: an
36/// [`ErrorKind::UnsupportedFormat`] error without offset, context or reason.
37/// [`Progress::format`] has named the family, if any.
38pub(super) fn refused() -> crate::Error {
39    ErrorKind::UnsupportedFormat.into()
40}
41
42/// One operation's view of a [`Progress`].
43pub(super) struct Observer<'p> {
44    progress: RefCell<&'p mut dyn Progress>,
45}
46
47impl<'p> Observer<'p> {
48    pub(super) fn new(progress: &'p mut dyn Progress) -> Self {
49        Self {
50            progress: RefCell::new(progress),
51        }
52    }
53
54    /// `source` reporting its furthest byte read to the caller.
55    pub(super) fn track<'a, S: RangedSource>(&'a self, source: &'a mut S) -> Tracked<'a, 'p, S> {
56        Tracked {
57            source,
58            observer: self,
59            furthest: 0,
60        }
61    }
62
63    /// Use `format`, or detect it, and report it; an empty or unrecognized
64    /// input is then refused. An explicit format skips detection, so an
65    /// explicit PDF must start with its `%PDF-` header.
66    pub(super) fn resolve<S: RangedSource>(
67        &self,
68        source: &mut S,
69        format: Option<InputFormat>,
70        limits: &Limits,
71    ) -> Result<Detection> {
72        let detection = match format {
73            Some(format) => Some(Detection {
74                format,
75                header_offset: 0,
76                bytes_read: 0,
77            }),
78            None => detect_source(source, limits, self)?,
79        };
80        self.progress
81            .borrow_mut()
82            .format(detection.map(|detection| detection.format));
83        detection.ok_or_else(refused)
84    }
85}
86
87impl Cancellation for Observer<'_> {
88    fn is_cancelled(&self) -> bool {
89        self.progress.borrow().is_cancelled()
90    }
91}
92
93/// The document source of one operation.
94pub(super) struct Tracked<'a, 'p, S> {
95    source: &'a mut S,
96    observer: &'a Observer<'p>,
97    furthest: u64,
98}
99
100impl<S: RangedSource> RangedSource for Tracked<'_, '_, S> {
101    fn size(&self) -> u64 {
102        self.source.size()
103    }
104
105    fn read_at(&mut self, offset: u64, destination: &mut [u8]) -> Result<usize> {
106        let count = self.source.read_at(offset, destination)?;
107        let end = offset.saturating_add(count as u64);
108        if end > self.furthest {
109            self.furthest = end;
110            let total = self.source.size();
111            self.observer.progress.borrow_mut().input_read(end, total);
112        }
113        Ok(count)
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[derive(Default)]
122    struct Recorded {
123        formats: Vec<Option<InputFormat>>,
124        reads: Vec<(u64, u64)>,
125    }
126
127    impl Progress for Recorded {
128        fn format(&mut self, format: Option<InputFormat>) {
129            self.formats.push(format);
130        }
131
132        fn input_read(&mut self, done: u64, total: u64) {
133            self.reads.push((done, total));
134        }
135    }
136
137    #[test]
138    fn the_furthest_byte_read_is_reported_once_per_advance() {
139        let mut recorded = Recorded::default();
140        {
141            let observer = Observer::new(&mut recorded);
142            let bytes = [7; 200];
143            let mut slice = &bytes[..];
144            let mut source = observer.track(&mut slice);
145            let mut buffer = [0; 100];
146            assert_eq!(source.read_at(0, &mut buffer).unwrap(), 100);
147            source.read_at(0, &mut buffer[..10]).unwrap();
148            source.read_at(100, &mut buffer[..1]).unwrap();
149            source.read_at(150, &mut buffer).unwrap();
150            assert!(source.read_at(201, &mut buffer).is_err());
151            assert_eq!(source.size(), 200);
152            assert!(!observer.is_cancelled());
153        }
154        assert_eq!(recorded.reads, [(100, 200), (101, 200), (200, 200)]);
155    }
156
157    #[test]
158    fn the_format_is_reported_before_an_unrecognized_input_is_refused() {
159        let mut recorded = Recorded::default();
160        {
161            let observer = Observer::new(&mut recorded);
162            let limits = Limits::default();
163            let error = observer
164                .resolve(&mut &b"unknown"[..], None, &limits)
165                .unwrap_err();
166            assert!(matches!(error.kind, ErrorKind::UnsupportedFormat));
167            assert_eq!(error.to_string(), "unsupported input format");
168            let explicit = observer
169                .resolve(&mut &b""[..], Some(InputFormat::Teb), &limits)
170                .unwrap();
171            assert_eq!(
172                (explicit.format, explicit.bytes_read),
173                (InputFormat::Teb, 0)
174            );
175        }
176        assert_eq!(recorded.formats, [None, Some(InputFormat::Teb)]);
177    }
178}