use std::io::Cursor;
use c2pa::{Context, Reader};
use crate::error::Error;
use crate::extract::{classify_reference, extract_manifest, Reference};
pub const DEFAULT_FORMAT: &str = "text/plain";
#[derive(Debug)]
pub enum BridgeError {
Extract(Error),
RemoteNotEnabled(String),
#[cfg(feature = "remote")]
Fetch(String),
C2pa(c2pa::Error),
}
impl std::fmt::Display for BridgeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Extract(e) => write!(f, "manifest extraction failed: {e}"),
Self::RemoteNotEnabled(url) => {
write!(
f,
"reference is a URL ({url}); enable the `remote` feature to resolve it"
)
}
#[cfg(feature = "remote")]
Self::Fetch(msg) => write!(f, "remote manifest fetch failed: {msg}"),
Self::C2pa(e) => write!(f, "c2pa validation error: {e}"),
}
}
}
impl std::error::Error for BridgeError {}
impl From<Error> for BridgeError {
fn from(e: Error) -> Self {
BridgeError::Extract(e)
}
}
impl From<c2pa::Error> for BridgeError {
fn from(e: c2pa::Error) -> Self {
BridgeError::C2pa(e)
}
}
pub fn validate_with_manifest(
text: &str,
c2pa_data: &[u8],
format: &str,
) -> Result<Reader, BridgeError> {
let reader = Reader::from_context(Context::default()).with_manifest_data_and_stream(
c2pa_data,
format,
Cursor::new(text.as_bytes()),
)?;
Ok(reader)
}
pub fn validate(text: &str, format: &str) -> Result<Reader, BridgeError> {
let extracted = extract_manifest(text)?;
match classify_reference(&extracted.reference)? {
Reference::Embedded(bytes) => validate_with_manifest(text, &bytes, format),
Reference::Url(url) => {
#[cfg(feature = "remote")]
{
let bytes = resolve_url(&url)?;
validate_with_manifest(text, &bytes, format)
}
#[cfg(not(feature = "remote"))]
{
Err(BridgeError::RemoteNotEnabled(url))
}
}
}
}
#[cfg(feature = "remote")]
pub fn resolve_url(url: &str) -> Result<Vec<u8>, BridgeError> {
use std::io::Read;
let resp = ureq::get(url)
.call()
.map_err(|e| BridgeError::Fetch(e.to_string()))?;
let mut bytes = Vec::new();
resp.into_reader()
.take(64 * 1024 * 1024)
.read_to_end(&mut bytes)
.map_err(|e| BridgeError::Fetch(e.to_string()))?;
Ok(bytes)
}