use std::time::Duration;
use serde_json::{json, Value};
use sha2::{Digest, Sha256};
pub const DEFAULT_ENDPOINT: &str = "https://api.encypher.com/api/v1/lookup";
const REQUEST_TIMEOUT: Duration = Duration::from_secs(5);
const USER_AGENT: &str = concat!(
"encypher-c2pa-cli/",
env!("CARGO_PKG_VERSION"),
" (+https://encypher.com/c2pa)"
);
pub fn content_sha256(bytes: &[u8]) -> String {
hex::encode(Sha256::digest(bytes))
}
pub fn lookup(endpoint: &str, content_sha256: &str) -> Value {
let agent = ureq::AgentBuilder::new().timeout(REQUEST_TIMEOUT).build();
let response = agent
.post(endpoint)
.set("User-Agent", USER_AGENT)
.send_json(json!({ "content_sha256": content_sha256 }));
match response {
Ok(resp) => match resp.into_json::<Value>() {
Ok(value) => value,
Err(error) => {
eprintln!("warning: encypher api returned an unreadable response ({error})");
json!({ "error": "invalid_response" })
}
},
Err(ureq::Error::Status(code, _)) => {
eprintln!("warning: encypher api returned HTTP status {code}");
json!({ "error": "http_status", "status": code })
}
Err(ureq::Error::Transport(error)) => {
eprintln!("warning: encypher api unreachable ({error})");
json!({ "error": "unreachable" })
}
}
}
pub fn render_human(result: &Value) {
if let Some(error) = result.get("error").and_then(Value::as_str) {
println!("encypher api: lookup failed ({error})");
return;
}
let found = result
.get("found")
.and_then(Value::as_bool)
.unwrap_or(false);
if !found {
println!("encypher api: not found");
return;
}
println!("encypher api: found");
if let Some(url) = result.get("verification_url").and_then(Value::as_str) {
println!(" verification url: {url}");
}
if let Some(org) = result.get("organization_name").and_then(Value::as_str) {
println!(" organization: {org}");
}
if let Some(media) = result.get("media_type").and_then(Value::as_str) {
println!(" media type: {media}");
}
}