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