Skip to main content

easypdf_core/
handler_chain.rs

1//! 基于优先级的写入处理程序执行链。
2//!
3//! 提供 [`WriteHandlerChain`],管理按优先级排序的 [`PdfWriteHandler`]
4//! 实例。优先级值越小的处理程序越先执行。
5//!
6//! # 优先级常量
7//!
8//! - [`PRIORITY_HIGH`](0.0)——样式、布局,必须最先执行。
9//! - [`PRIORITY_NORMAL`](10.0)——大多数处理程序的默认优先级。
10//! - [`PRIORITY_LOW`](20.0)——页码、水印、后处理。
11
12use crate::error::Result;
13use crate::traits::PdfWriteHandler;
14
15/// 高优先级(0.0)——样式、布局和必须最先运行的处理程序。
16pub const PRIORITY_HIGH: f64 = 0.0;
17
18/// 普通优先级(10.0)——大多数处理程序的默认值。
19pub const PRIORITY_NORMAL: f64 = 10.0;
20
21/// 低优先级(20.0)——页码、水印和后处理。
22pub const PRIORITY_LOW: f64 = 20.0;
23
24/// 与其执行优先级配对的 [`PdfWriteHandler`]。
25///
26/// 优先级值越小越先执行。优先级相同的处理程序
27/// 保持注册顺序(稳定排序)。
28pub struct WriteHandlerRegistration {
29    /// 处理程序实例。
30    handler: Box<dyn PdfWriteHandler>,
31    /// 执行优先级(值越小越先执行)。
32    priority: f64,
33}
34
35impl WriteHandlerRegistration {
36    /// 使用给定的处理程序和优先级创建新的注册。
37    #[must_use]
38    pub fn new(handler: Box<dyn PdfWriteHandler>, priority: f64) -> Self {
39        Self { handler, priority }
40    }
41
42    /// 返回此注册的优先级。
43    #[must_use]
44    pub const fn priority(&self) -> f64 {
45        self.priority
46    }
47
48    /// 借用处理程序。
49    #[must_use]
50    pub fn handler(&self) -> &dyn PdfWriteHandler {
51        self.handler.as_ref()
52    }
53
54    /// 可变借用处理程序。
55    pub fn handler_mut(&mut self) -> &mut dyn PdfWriteHandler {
56        self.handler.as_mut()
57    }
58}
59
60/// 按优先级排序的 [`PdfWriteHandler`] 实例有序链。
61///
62/// 在每个生命周期阶段,处理程序按优先级升序(最小的最先)调用。
63/// 链使用稳定排序,因此优先级相同的处理程序保持注册顺序。
64///
65/// # Examples
66///
67/// ```
68/// use easypdf_core::handler_chain::{WriteHandlerChain, PRIORITY_HIGH, PRIORITY_NORMAL};
69/// use easypdf_core::{PdfWriteHandler, Result};
70///
71/// struct StyleHandler;
72/// impl PdfWriteHandler for StyleHandler {}
73///
74/// struct WatermarkHandler;
75/// impl PdfWriteHandler for WatermarkHandler {}
76///
77/// let mut chain = WriteHandlerChain::new();
78/// chain.register(Box::new(WatermarkHandler), PRIORITY_NORMAL);
79/// chain.register(Box::new(StyleHandler), PRIORITY_HIGH);
80/// // StyleHandler 在 WatermarkHandler 之前运行,因为 HIGH < NORMAL。
81/// ```
82pub struct WriteHandlerChain {
83    registrations: Vec<WriteHandlerRegistration>,
84    sorted: bool,
85}
86
87impl WriteHandlerChain {
88    /// 创建空的处理程序链。
89    #[must_use]
90    pub fn new() -> Self {
91        Self {
92            registrations: Vec::new(),
93            sorted: true,
94        }
95    }
96
97    /// 使用给定优先级注册处理程序。
98    ///
99    /// 链将在下次生命周期调用前重新排序。
100    pub fn register(&mut self, handler: Box<dyn PdfWriteHandler>, priority: f64) {
101        self.registrations
102            .push(WriteHandlerRegistration::new(handler, priority));
103        self.sorted = false;
104    }
105
106    /// 返回已注册处理程序的数量。
107    #[must_use]
108    pub fn len(&self) -> usize {
109        self.registrations.len()
110    }
111
112    /// 返回链是否没有处理程序。
113    #[must_use]
114    pub fn is_empty(&self) -> bool {
115        self.registrations.is_empty()
116    }
117
118    /// 确保注册按优先级排序(升序、稳定)。
119    fn ensure_sorted(&mut self) {
120        if !self.sorted {
121            self.registrations.sort_by(|a, b| {
122                a.priority
123                    .partial_cmp(&b.priority)
124                    .unwrap_or(std::cmp::Ordering::Equal)
125            });
126            self.sorted = true;
127        }
128    }
129
130    /// 按优先级顺序在所有处理程序上调用 `before_document`。
131    ///
132    /// # Errors
133    ///
134    /// 返回任何处理程序的第一个错误;后续处理程序被跳过。
135    pub fn before_document(&mut self) -> Result<()> {
136        self.ensure_sorted();
137        for reg in &mut self.registrations {
138            reg.handler.before_document()?;
139        }
140        Ok(())
141    }
142
143    /// 按优先级顺序在所有处理程序上调用 `before_page`。
144    ///
145    /// # Errors
146    ///
147    /// 返回任何处理程序的第一个错误;后续处理程序被跳过。
148    pub fn before_page(&mut self, page_number: usize) -> Result<()> {
149        self.ensure_sorted();
150        for reg in &mut self.registrations {
151            reg.handler.before_page(page_number)?;
152        }
153        Ok(())
154    }
155
156    /// 按优先级顺序在所有处理程序上调用 `after_page`。
157    ///
158    /// # Errors
159    ///
160    /// 返回任何处理程序的第一个错误;后续处理程序被跳过。
161    pub fn after_page(&mut self, page_number: usize) -> Result<()> {
162        self.ensure_sorted();
163        for reg in &mut self.registrations {
164            reg.handler.after_page(page_number)?;
165        }
166        Ok(())
167    }
168
169    /// 按优先级顺序在所有处理程序上调用 `after_document`。
170    ///
171    /// # Errors
172    ///
173    /// 返回任何处理程序的第一个错误;后续处理程序被跳过。
174    pub fn after_document(&mut self) -> Result<()> {
175        self.ensure_sorted();
176        for reg in &mut self.registrations {
177            reg.handler.after_document()?;
178        }
179        Ok(())
180    }
181}
182
183impl Default for WriteHandlerChain {
184    fn default() -> Self {
185        Self::new()
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192    use std::sync::{Arc, Mutex};
193
194    /// Records the order in which handlers are invoked.
195    #[derive(Clone)]
196    struct RecordingHandler {
197        name: &'static str,
198        log: Arc<Mutex<Vec<&'static str>>>,
199    }
200
201    impl PdfWriteHandler for RecordingHandler {
202        fn before_document(&mut self) -> Result<()> {
203            self.log.lock().expect("poisoned").push(self.name);
204            Ok(())
205        }
206        fn before_page(&mut self, _page_number: usize) -> Result<()> {
207            self.log.lock().expect("poisoned").push(self.name);
208            Ok(())
209        }
210        fn after_page(&mut self, _page_number: usize) -> Result<()> {
211            self.log.lock().expect("poisoned").push(self.name);
212            Ok(())
213        }
214        fn after_document(&mut self) -> Result<()> {
215            self.log.lock().expect("poisoned").push(self.name);
216            Ok(())
217        }
218    }
219
220    struct FailingHandler;
221
222    impl PdfWriteHandler for FailingHandler {
223        fn before_document(&mut self) -> Result<()> {
224            Err(crate::error::PdfError::Other("intentional failure".into()))
225        }
226    }
227
228    #[test]
229    fn empty_chain_succeeds() {
230        let mut chain = WriteHandlerChain::new();
231        assert!(chain.before_document().is_ok());
232        assert!(chain.before_page(1).is_ok());
233        assert!(chain.after_page(1).is_ok());
234        assert!(chain.after_document().is_ok());
235        assert!(chain.is_empty());
236    }
237
238    #[test]
239    fn handlers_execute_in_priority_order() {
240        let log = Arc::new(Mutex::new(Vec::new()));
241        let mut chain = WriteHandlerChain::new();
242        chain.register(
243            Box::new(RecordingHandler {
244                name: "low",
245                log: Arc::clone(&log),
246            }),
247            PRIORITY_LOW,
248        );
249        chain.register(
250            Box::new(RecordingHandler {
251                name: "high",
252                log: Arc::clone(&log),
253            }),
254            PRIORITY_HIGH,
255        );
256        chain.register(
257            Box::new(RecordingHandler {
258                name: "normal",
259                log: Arc::clone(&log),
260            }),
261            PRIORITY_NORMAL,
262        );
263
264        chain.before_document().unwrap();
265        let entries: Vec<_> = log.lock().expect("poisoned").clone();
266        assert_eq!(entries, vec!["high", "normal", "low"]);
267    }
268
269    #[test]
270    fn equal_priority_preserves_registration_order() {
271        let log = Arc::new(Mutex::new(Vec::new()));
272        let mut chain = WriteHandlerChain::new();
273        chain.register(
274            Box::new(RecordingHandler {
275                name: "first",
276                log: Arc::clone(&log),
277            }),
278            PRIORITY_NORMAL,
279        );
280        chain.register(
281            Box::new(RecordingHandler {
282                name: "second",
283                log: Arc::clone(&log),
284            }),
285            PRIORITY_NORMAL,
286        );
287
288        chain.before_page(1).unwrap();
289        let entries: Vec<_> = log.lock().expect("poisoned").clone();
290        assert_eq!(entries, vec!["first", "second"]);
291    }
292
293    #[test]
294    fn error_stops_chain_execution() {
295        let log = Arc::new(Mutex::new(Vec::new()));
296        let mut chain = WriteHandlerChain::new();
297        chain.register(
298            Box::new(RecordingHandler {
299                name: "before",
300                log: Arc::clone(&log),
301            }),
302            PRIORITY_HIGH,
303        );
304        chain.register(Box::new(FailingHandler), PRIORITY_NORMAL);
305        chain.register(
306            Box::new(RecordingHandler {
307                name: "after",
308                log: Arc::clone(&log),
309            }),
310            PRIORITY_LOW,
311        );
312
313        let result = chain.before_document();
314        assert!(result.is_err());
315        let entries: Vec<_> = log.lock().expect("poisoned").clone();
316        assert_eq!(entries, vec!["before"]);
317    }
318
319    #[test]
320    fn len_tracks_registrations() {
321        let mut chain = WriteHandlerChain::new();
322        assert_eq!(chain.len(), 0);
323        chain.register(Box::new(FailingHandler), PRIORITY_NORMAL);
324        assert_eq!(chain.len(), 1);
325    }
326
327    #[test]
328    fn default_is_empty() {
329        let chain = WriteHandlerChain::default();
330        assert!(chain.is_empty());
331    }
332
333    #[test]
334    fn lifecycle_methods_all_invoked() {
335        let log = Arc::new(Mutex::new(Vec::new()));
336        let mut chain = WriteHandlerChain::new();
337        chain.register(
338            Box::new(RecordingHandler {
339                name: "h",
340                log: Arc::clone(&log),
341            }),
342            PRIORITY_NORMAL,
343        );
344
345        chain.before_document().unwrap();
346        chain.before_page(1).unwrap();
347        chain.after_page(1).unwrap();
348        chain.after_document().unwrap();
349
350        let entries: Vec<_> = log.lock().expect("poisoned").clone();
351        assert_eq!(entries, vec!["h", "h", "h", "h"]);
352    }
353}