Skip to main content

c2pa_structured_text/
lib.rs

1// Copyright 2026 WritersLogic. All rights reserved.
2// Licensed under the Apache License, Version 2.0 or the MIT license,
3// at your option.
4
5//! C2PA manifest embedding, hard binding, and validation for structured text.
6//!
7//! Implements the *Embedding Manifests into Structured Text* section of the
8//! C2PA Technical Specification, which associates a C2PA Manifest Store with
9//! source code, configuration files, markup, and other text formats that
10//! support comment syntax or front matter conventions.
11//!
12//! The manifest block uses fixed ASCII armour-style delimiters:
13//! `-----BEGIN C2PA MANIFEST-----` and `-----END C2PA MANIFEST-----`.
14//!
15//! # Scope
16//!
17//! This crate owns three things:
18//!
19//! - **Embed** ([`embed_manifest`], [`embed_manifest_at_end`],
20//!   [`embed_front_matter`]) a reference or an inline manifest as a comment or
21//!   front matter block.
22//! - **Extract** ([`extract_manifest`], [`classify_reference`]) the block and
23//!   resolve its reference.
24//! - The **hard binding** ([`hardbinding`]): the exact `c2pa.hash.data`
25//!   coverage over the normalized-free raw byte stream, with the manifest block
26//!   excluded, plus compute and verify.
27//!
28//! Signature verification, certificate trust, and assertion validation are
29//! **not** implemented here; the [`bridge`] (feature `c2pa`) delegates them to
30//! `c2pa-rs`.
31//!
32//! # Examples
33//!
34//! The block is embedded using the host format's comment syntax and located
35//! again by its ASCII delimiters, independent of that syntax. One round trip
36//! per comment style:
37//!
38//! Line comments — `#` (Python, Ruby, Shell, YAML, TOML), `//` (JS, Rust, Go,
39//! C++), `--` (SQL, Lua, Haskell):
40//!
41//! ```
42//! use c2pa_structured_text::{embed_manifest, extract_manifest, ManifestRef};
43//!
44//! for prefix in ["#", "//", "--"] {
45//!     let signed = embed_manifest("value = 1\n", ManifestRef::Url("https://ex.com/m.c2pa"), prefix, None);
46//!     assert!(signed.starts_with(prefix));
47//!     assert_eq!(extract_manifest(&signed).unwrap().reference, "https://ex.com/m.c2pa");
48//! }
49//! ```
50//!
51//! Block comments — `/* */` (CSS, C, Java):
52//!
53//! ```
54//! use c2pa_structured_text::{embed_manifest, extract_manifest, ManifestRef};
55//!
56//! let signed = embed_manifest("body {}\n", ManifestRef::Url("https://ex.com/m.c2pa"), "/*", Some("*/"));
57//! assert!(signed.contains("-----END C2PA MANIFEST----- */"));
58//! assert_eq!(extract_manifest(&signed).unwrap().reference, "https://ex.com/m.c2pa");
59//! ```
60//!
61//! Markup comments — `<!-- -->` (Markdown, non-HTML XML):
62//!
63//! ```
64//! use c2pa_structured_text::{embed_manifest, extract_manifest, ManifestRef};
65//!
66//! let signed = embed_manifest("# Title\n", ManifestRef::Url("https://ex.com/m.c2pa"), "<!--", Some("-->"));
67//! assert!(signed.starts_with("<!-- -----BEGIN C2PA MANIFEST-----"));
68//! assert_eq!(extract_manifest(&signed).unwrap().reference, "https://ex.com/m.c2pa");
69//! ```
70//!
71//! Front matter — multi-line form for YAML (`---`) / TOML (`+++`):
72//!
73//! ```
74//! use c2pa_structured_text::{embed_front_matter, extract_manifest, ManifestRef};
75//!
76//! let signed = embed_front_matter("title: doc\n", ManifestRef::Url("https://ex.com/m.c2pa"), "---");
77//! assert!(signed.starts_with("---\n-----BEGIN C2PA MANIFEST-----\n"));
78//! assert_eq!(extract_manifest(&signed).unwrap().reference, "https://ex.com/m.c2pa");
79//! ```
80//!
81//! # Features
82//!
83//! - `hard-binding` — concrete SHA2-256/384/512 [`hardbinding::compute_data_hash`]
84//!   and [`hardbinding::verify_data_hash`] (pulls `sha2`). The exclusion-range
85//!   and covered-byte primitives ([`hardbinding::manifest_exclusion`],
86//!   [`hardbinding::hashed_bytes`]) are always available and dependency-free.
87//! - `c2pa` — the [`bridge`] to `c2pa-rs` for signature/trust/assertion validation.
88//! - `remote` — HTTP(S) resolution of URL references in the bridge (pulls `ureq`).
89//! - `wasm` — JS/WASM bindings for the npm package (pulls `wasm-bindgen`).
90//! - `python` — Python bindings for the PyPI wheel (pulls `pyo3`, built with maturin).
91//!
92//! No feature is enabled by default; the core embed/extract/binding-range API
93//! has no dependencies.
94
95pub mod codec;
96mod embed;
97mod error;
98mod extract;
99pub mod hardbinding;
100
101#[cfg(feature = "c2pa")]
102pub mod bridge;
103
104#[cfg(feature = "wasm")]
105mod wasm;
106
107#[cfg(feature = "python")]
108mod python;
109
110pub use embed::{embed_front_matter, embed_manifest, embed_manifest_at_end, ManifestRef};
111pub use error::Error;
112pub use extract::{
113    classify_reference, extract_manifest, find_delimiter, ExtractionResult, Reference, BEGIN,
114    DATA_URI_PREFIX, END,
115};