Skip to main content

snitch/
lib.rs

1//! snitch: read, strip and credit image metadata, and check text for hidden characters.
2//!
3//! Three separate jobs that people usually conflate:
4//!
5//! ```text
6//! snitch      read everything and say plainly what is there
7//! credit      write credit in, and optionally stamp it into the pixels
8//! no-comment  take metadata out
9//! ```
10//!
11//! STRIPPING IS SEGMENT SURGERY, NOT RE-ENCODING. Dropping JPEG APPn/COM markers and keeping only
12//! the required PNG chunks leaves the decoded pixels byte-identical, and `strip::pixels_identical`
13//! proves it per run rather than asserting it. Re-encoding to "clear" metadata throws away image
14//! quality for no reason, and this tool will not do it.
15//!
16//! STAMPING DOES re-encode, because it has to: it changes pixels. That is stated at the call site
17//! and in the CLI output, because a user deserves to know which operation costs them quality.
18//!
19//! HOUSE STYLE. No async, no web framework, no CLI framework, seven direct dependencies, and the
20//! two obvious crates for C2PA and MCP were measured and refused. See Cargo.toml for the numbers.
21
22pub mod c2pa;
23pub mod cli;
24pub mod exif;
25pub mod inspect;
26pub mod jpeg;
27pub mod mcp;
28pub mod png;
29pub mod sign;
30pub mod stamp;
31pub mod strip;
32pub mod survival;
33pub mod text;
34
35pub const VERSION: &str = env!("CARGO_PKG_VERSION");
36
37/// Licence presets, exactly as the Python shipped them.
38pub const LICENCES: [(&str, &str, Option<&str>); 8] = [
39    (
40        "cc-by",
41        "CC BY 4.0",
42        Some("https://creativecommons.org/licenses/by/4.0/"),
43    ),
44    (
45        "cc-by-sa",
46        "CC BY-SA 4.0",
47        Some("https://creativecommons.org/licenses/by-sa/4.0/"),
48    ),
49    (
50        "cc-by-nd",
51        "CC BY-ND 4.0",
52        Some("https://creativecommons.org/licenses/by-nd/4.0/"),
53    ),
54    (
55        "cc-by-nc",
56        "CC BY-NC 4.0",
57        Some("https://creativecommons.org/licenses/by-nc/4.0/"),
58    ),
59    (
60        "cc-by-nc-sa",
61        "CC BY-NC-SA 4.0",
62        Some("https://creativecommons.org/licenses/by-nc-sa/4.0/"),
63    ),
64    (
65        "cc-by-nc-nd",
66        "CC BY-NC-ND 4.0",
67        Some("https://creativecommons.org/licenses/by-nc-nd/4.0/"),
68    ),
69    (
70        "cc0",
71        "CC0 1.0",
72        Some("https://creativecommons.org/publicdomain/zero/1.0/"),
73    ),
74    ("arr", "All rights reserved", None),
75];
76
77pub fn licence(key: &str) -> Option<(&'static str, Option<&'static str>)> {
78    LICENCES
79        .iter()
80        .find(|(k, _, _)| *k == key)
81        .map(|(_, name, url)| (*name, *url))
82}
83
84pub fn licence_keys() -> String {
85    LICENCES
86        .iter()
87        .map(|(k, _, _)| *k)
88        .collect::<Vec<_>>()
89        .join(", ")
90}