Skip to main content

compress_pdf/
error.rs

1//! Why a document was not compressed, or its output not accepted. One
2//! enum, so a program can react to a specific outcome (skip files that
3//! need a password, treat a verification regression as a bug) without
4//! comparing message text; `Display` gives the message the command prints.
5
6use std::fmt;
7
8use crate::verify::Category;
9use crate::verify::render::Comparison;
10
11#[derive(Debug)]
12#[non_exhaustive]
13pub enum Refusal {
14    /// The input does not parse as a PDF.
15    Unparseable(lopdf::Error),
16    /// A password is needed to open the input.
17    PasswordRequired,
18    /// The input is encrypted with crypt filters the parser could not
19    /// read, so it was not decrypted and would be written as garbage.
20    UndecryptedCryptFilters,
21    /// The page tree refers to objects the parser could not load.
22    DamagedPageTree,
23    /// Page content or resources refer to objects the parser could not load.
24    DamagedResources,
25    /// The output verifies worse than the input: per category, the
26    /// input's problem count and the output's.
27    VerificationRegressed(Vec<(Category, usize, usize)>),
28    /// Pages fell below the similarity floor under strict verification.
29    BelowSimilarityFloor(Comparison),
30    /// A stage or the writer failed.
31    Internal(anyhow::Error),
32}
33
34impl fmt::Display for Refusal {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            Refusal::Unparseable(e) => write!(f, "parsing the input: {e}"),
38            Refusal::PasswordRequired => {
39                f.write_str("input is encrypted with a password that is needed to open it")
40            }
41            Refusal::UndecryptedCryptFilters => f.write_str(
42                "input is encrypted with crypt filters the parser could not read, so it was not decrypted",
43            ),
44            Refusal::DamagedPageTree => {
45                f.write_str("page tree refers to objects the parser could not load")
46            }
47            Refusal::DamagedResources => {
48                f.write_str("page content or resources refer to objects the parser could not load")
49            }
50            Refusal::VerificationRegressed(regressions) => write!(
51                f,
52                "output failed verification with new problems {regressions:?}; nothing written (this is a bug, please report it)"
53            ),
54            Refusal::BelowSimilarityFloor(_) => {
55                f.write_str("pages below the similarity floor; nothing written (--strict)")
56            }
57            Refusal::Internal(e) => write!(f, "{e:#}"),
58        }
59    }
60}
61
62impl std::error::Error for Refusal {
63    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
64        match self {
65            Refusal::Unparseable(e) => Some(e),
66            Refusal::Internal(e) => Some(e.as_ref()),
67            _ => None,
68        }
69    }
70}
71
72/// Stage and writer errors become `Internal`, so `?` works inside the
73/// pipeline.
74impl From<anyhow::Error> for Refusal {
75    fn from(e: anyhow::Error) -> Self {
76        Refusal::Internal(e)
77    }
78}