Skip to main content

docparser_core/
policy.rs

1//! 后端选择策略(与 Python `policy.py` 对齐)。
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
4pub enum Profile {
5    /// 只用自带后端,不依赖外部进程(默认,保证独立性)
6    #[default]
7    Minimal,
8    /// 允许外部后端(如 LibreOffice)作为回退
9    Compatibility,
10    /// 追求效果,启用全部可选后端
11    Agent,
12}
13
14#[derive(Debug, Clone, Default)]
15pub struct BackendPolicy {
16    pub profile: Profile,
17    pub preferred: Option<String>,
18    pub chain: Vec<String>,
19    pub allow_fallback: bool,
20    pub timeout_secs: f64,
21    pub enable_ocr: bool,
22    pub enable_layout: bool,
23    pub ocr_dpi: u32,
24    pub ocr_drop_score: f32,
25    pub max_pages: usize,
26}
27
28impl BackendPolicy {
29    /// 计算实际尝试顺序:显式 chain > preferred 置顶 > 默认链。
30    pub fn ordered_chain(&self, default_chain: &[String]) -> Vec<String> {
31        let mut base: Vec<String> = if !self.chain.is_empty() {
32            self.chain.clone()
33        } else {
34            let mut d = default_chain.to_vec();
35            if let Some(pref) = &self.preferred {
36                if let Some(pos) = d.iter().position(|x| x == pref) {
37                    d.remove(pos);
38                    d.insert(0, pref.clone());
39                } else {
40                    d.insert(0, pref.clone());
41                }
42            }
43            d
44        };
45        if !self.allow_fallback && base.len() > 1 {
46            base.truncate(1);
47        }
48        base
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    fn s(v: &[&str]) -> Vec<String> {
57        v.iter().map(|x| x.to_string()).collect()
58    }
59
60    #[test]
61    fn preferred_promotes() {
62        let p = BackendPolicy {
63            preferred: Some("b".into()),
64            allow_fallback: true,
65            ..Default::default()
66        };
67        assert_eq!(p.ordered_chain(&s(&["a", "b", "c"])), s(&["b", "a", "c"]));
68    }
69
70    #[test]
71    fn explicit_chain_wins() {
72        let p = BackendPolicy {
73            chain: s(&["c", "a"]),
74            allow_fallback: true,
75            ..Default::default()
76        };
77        assert_eq!(p.ordered_chain(&s(&["a", "b", "c"])), s(&["c", "a"]));
78    }
79
80    #[test]
81    fn no_fallback_truncates() {
82        let p = BackendPolicy {
83            allow_fallback: false,
84            ..Default::default()
85        };
86        assert_eq!(p.ordered_chain(&s(&["a", "b"])), s(&["a"]));
87    }
88}