Skip to main content

c2pa_warc/
error.rs

1use std::fmt;
2
3#[derive(Debug)]
4pub enum Error {
5    /// No C2PA manifest record was present in the archive.
6    NotFound,
7    /// A WARC record was structurally malformed.
8    InvalidRecord(String),
9    Io(std::io::Error),
10}
11
12impl Error {
13    /// The registered C2PA validation status code for this error, or `None`
14    /// when the condition carries no status code.
15    ///
16    /// Always `None` here. [`Error::NotFound`] means the archive carries no
17    /// provenance, which is not a failure; the other two are archive-parsing
18    /// and I/O problems that occur before any manifest is located. The
19    /// specification defines no WARC-specific codes.
20    ///
21    /// Every crate in this family exposes this method, so a dispatcher handling
22    /// several embedding methods can ask the same question of any of them.
23    pub fn code(&self) -> Option<&'static str> {
24        None
25    }
26
27    /// Whether this error means the archive carries no provenance at all, as
28    /// opposed to provenance that was found and rejected.
29    pub fn is_no_manifest_located(&self) -> bool {
30        matches!(self, Self::NotFound)
31    }
32}
33
34impl fmt::Display for Error {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            Self::NotFound => write!(f, "no C2PA manifest record found"),
38            Self::InvalidRecord(s) => write!(f, "invalid WARC record: {s}"),
39            Self::Io(e) => write!(f, "I/O error: {e}"),
40        }
41    }
42}
43
44impl std::error::Error for Error {}
45
46impl From<std::io::Error> for Error {
47    fn from(e: std::io::Error) -> Self {
48        Self::Io(e)
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    /// The specification defines no WARC-specific codes, so none may appear.
57    /// Guards against a later edit inventing one.
58    #[test]
59    fn no_variant_claims_a_status_code() {
60        for e in [
61            Error::NotFound,
62            Error::InvalidRecord("x".into()),
63            Error::Io(std::io::Error::other("x")),
64        ] {
65            assert_eq!(e.code(), None, "{e:?} claimed a status code");
66        }
67    }
68
69    /// An archive carrying no manifest is unsigned; a malformed one is not.
70    /// Those two must stay distinguishable to a caller reporting provenance.
71    #[test]
72    fn only_a_missing_manifest_means_unsigned() {
73        assert!(Error::NotFound.is_no_manifest_located());
74        assert!(!Error::InvalidRecord("x".into()).is_no_manifest_located());
75        assert!(!Error::Io(std::io::Error::other("x")).is_no_manifest_located());
76    }
77}