use std::fmt;
#[cfg(feature = "json_schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::utils::DebugByteSlice;
#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub struct HashedUri {
url: String,
#[serde(skip_serializing_if = "Option::is_none")]
alg: Option<String>,
#[serde(with = "serde_bytes")]
#[cfg_attr(feature = "json_schema", schemars(with = "Vec<u8>"))]
hash: Vec<u8>,
#[serde(skip_deserializing, skip_serializing)]
salt: Option<Vec<u8>>,
}
impl HashedUri {
pub fn new(url: String, alg: Option<String>, hash_bytes: &[u8]) -> Self {
Self {
url,
alg,
hash: hash_bytes.to_vec(),
salt: None,
}
}
pub fn url(&self) -> String {
self.url.clone()
}
pub fn is_relative_url(&self) -> bool {
crate::jumbf::labels::manifest_label_from_uri(&self.url).is_none()
}
pub fn alg(&self) -> Option<String> {
self.alg.clone()
}
pub fn hash(&self) -> Vec<u8> {
self.hash.clone()
}
pub(crate) fn update_hash(&mut self, hash: Vec<u8>) {
self.hash = hash;
}
pub fn add_salt(&mut self, salt: Option<Vec<u8>>) {
self.salt = salt;
}
pub fn salt(&self) -> &Option<Vec<u8>> {
&self.salt
}
}
impl fmt::Debug for HashedUri {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_struct("HashedUri")
.field("url", &self.url)
.field("alg", &self.alg)
.field("hash", &DebugByteSlice(&self.hash))
.finish()
}
}
impl fmt::Display for HashedUri {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "url: {}, alg: {:?}, hash", self.url, self.alg)
}
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
use super::HashedUri;
#[test]
fn impl_clone() {
let h = HashedUri::new(
"self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data".to_owned(),
Some("sha256".to_owned()),
&hex!("53d1b2cf4e6d9a97ed9281183fa5d836c32751b9d2fca724b40836befee7d67f"),
);
let h2 = h.clone();
assert!(h == h2);
}
#[test]
fn impl_debug() {
let h = HashedUri::new(
"self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data".to_owned(),
Some("sha256".to_owned()),
&hex!("53d1b2cf4e6d9a97ed9281183fa5d836c32751b9d2fca724b40836befee7d67f"),
);
assert_eq!(format!("{h:#?}"), "HashedUri {\n url: \"self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data\",\n alg: Some(\n \"sha256\",\n ),\n hash: 32 bytes starting with [53, d1, b2, cf, 4e, 6d, 9a, 97, ed, 92, 81, 18, 3f, a5, d8, 36, c3, 27, 51, b9],\n}");
}
#[test]
fn impl_display() {
let h = HashedUri::new(
"self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data".to_owned(),
Some("sha256".to_owned()),
&hex!("53d1b2cf4e6d9a97ed9281183fa5d836c32751b9d2fca724b40836befee7d67f"),
);
assert_eq!(format!("{h}"), "url: self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data, alg: Some(\"sha256\"), hash");
}
}