use crate::codec;
const BEGIN: &str = "-----BEGIN C2PA MANIFEST-----";
const END: &str = "-----END C2PA MANIFEST-----";
pub enum ManifestRef<'a> {
Url(&'a str),
Embedded(&'a [u8]),
}
impl ManifestRef<'_> {
fn render(&self) -> String {
match self {
ManifestRef::Url(url) => (*url).to_string(),
ManifestRef::Embedded(bytes) => {
format!("data:application/c2pa;base64,{}", codec::encode(bytes))
}
}
}
}
fn single_line(reference: &str, comment_prefix: &str, comment_suffix: Option<&str>) -> String {
let suffix = comment_suffix.unwrap_or("");
format!("{comment_prefix} {BEGIN} {reference} {END} {suffix}")
.trim_end()
.to_string()
}
pub fn embed_manifest(
text: &str,
manifest: ManifestRef<'_>,
comment_prefix: &str,
comment_suffix: Option<&str>,
) -> String {
let line = single_line(&manifest.render(), comment_prefix, comment_suffix);
format!("{line}\n{text}")
}
pub fn embed_manifest_at_end(
text: &str,
manifest: ManifestRef<'_>,
comment_prefix: &str,
comment_suffix: Option<&str>,
) -> String {
let line = single_line(&manifest.render(), comment_prefix, comment_suffix);
if text.is_empty() {
return line;
}
if text.ends_with('\n') {
format!("{text}{line}")
} else {
format!("{text}\n{line}")
}
}
pub fn embed_front_matter(text: &str, manifest: ManifestRef<'_>, fm_delim: &str) -> String {
let reference = manifest.render();
let block = format!("{BEGIN}\n{reference}\n{END}");
let opening = format!("{fm_delim}\n");
if let Some(rest) = text.strip_prefix(&opening) {
format!("{opening}{block}\n{rest}")
} else {
format!("{fm_delim}\n{block}\n{fm_delim}\n{text}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn embed_url_python() {
let text = "print('hello')\n";
let result = embed_manifest(
text,
ManifestRef::Url("https://example.com/m.c2pa"),
"#",
None,
);
assert!(result.starts_with("# -----BEGIN C2PA MANIFEST-----"));
assert!(result.contains("https://example.com/m.c2pa"));
assert!(result.contains("-----END C2PA MANIFEST-----"));
assert!(result.ends_with("print('hello')\n"));
}
#[test]
fn embed_url_css() {
let result = embed_manifest(
"body {}",
ManifestRef::Url("https://example.com/m.c2pa"),
"/*",
Some("*/"),
);
assert!(result.starts_with("/* -----BEGIN C2PA MANIFEST-----"));
assert!(result.contains("-----END C2PA MANIFEST----- */"));
}
#[test]
fn embed_data_uri() {
let bytes = b"test manifest";
let result = embed_manifest("content", ManifestRef::Embedded(bytes), "#", None);
assert!(result.contains("data:application/c2pa;base64,"));
}
#[test]
fn embed_at_end_after_shebang() {
let text = "#!/usr/bin/env python3\nprint('hi')\n";
let result = embed_manifest_at_end(
text,
ManifestRef::Url("https://example.com/m.c2pa"),
"#",
None,
);
assert!(result.starts_with("#!/usr/bin/env python3"));
assert!(result.ends_with("-----END C2PA MANIFEST-----"));
}
#[test]
fn embed_at_end_adds_newline_separator() {
let result =
embed_manifest_at_end("no trailing newline", ManifestRef::Url("u"), "//", None);
assert_eq!(
result,
"no trailing newline\n// -----BEGIN C2PA MANIFEST----- u -----END C2PA MANIFEST-----"
);
}
#[test]
fn embed_front_matter_into_existing() {
let text = "---\ntitle: doc\n---\nbody\n";
let result =
embed_front_matter(text, ManifestRef::Url("https://example.com/m.c2pa"), "---");
assert!(result.starts_with("---\n-----BEGIN C2PA MANIFEST-----\n"));
assert!(result.contains("\ntitle: doc\n"));
}
#[test]
fn embed_front_matter_creates_section() {
let result = embed_front_matter("# Heading\n", ManifestRef::Url("u"), "---");
assert!(result.starts_with(
"---\n-----BEGIN C2PA MANIFEST-----\nu\n-----END C2PA MANIFEST-----\n---\n"
));
}
}