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}