manifest_assertion.rs (8586B)
1 #[cfg(feature = "json_schema")] 2 use schemars::JsonSchema; 3 use serde::{de::DeserializeOwned, Deserialize, Serialize}; //, Deserializer, Serializer}; 4 use serde_json::Value; 5 6 use crate::{ 7 assertion::{AssertionBase, AssertionDecodeError}, 8 error::{Error, Result}, 9 }; 10 11 /// Assertions in C2PA can be stored in several formats 12 #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] 13 #[cfg_attr(feature = "json_schema", derive(JsonSchema))] 14 pub enum ManifestAssertionKind { 15 Cbor, 16 Json, 17 Binary, 18 Uri, 19 } 20 21 #[derive(Debug, Deserialize, Serialize, Clone)] 22 #[cfg_attr(feature = "json_schema", derive(JsonSchema))] 23 #[serde(untagged)] 24 enum ManifestData { 25 Json(Value), // { label: String, instance: usize, data: Value }, 26 Binary(Vec<u8>), // ) { label: String, instance: usize, data: Value }, 27 } 28 29 #[derive(Debug, Deserialize, Serialize, Clone)] 30 #[cfg_attr(feature = "json_schema", derive(JsonSchema))] 31 /// A labeled container for an Assertion value in a Manifest 32 pub struct ManifestAssertion { 33 /// An assertion label in reverse domain format 34 label: String, 35 /// The data of the assertion as Value 36 data: ManifestData, 37 /// There can be more than one assertion for any label 38 #[serde(skip_serializing_if = "Option::is_none")] 39 instance: Option<usize>, 40 /// The [ManifestAssertionKind] for this assertion (as stored in c2pa content) 41 #[serde(skip_serializing_if = "Option::is_none")] 42 kind: Option<ManifestAssertionKind>, 43 } 44 45 impl ManifestAssertion { 46 /// Create with label and value 47 pub const fn new(label: String, data: Value) -> Self { 48 Self { 49 label, 50 data: ManifestData::Json(data), 51 instance: None, 52 kind: None, 53 } 54 } 55 56 /// An assertion label in reverse domain format 57 pub fn label(&self) -> &str { 58 &self.label 59 } 60 61 /// An assertion label in reverse domain format with appended instance number 62 /// The instance number follows two underscores and is only added when the instance is > 1 63 /// This is a c2pa spec internal standard format 64 pub fn label_with_instance(&self) -> String { 65 match self.instance { 66 Some(i) if i > 1 => format!("{}__{}", self.label, i), 67 _ => self.label.to_owned(), 68 } 69 } 70 71 /// The data of the assertion as a serde_Json::Value 72 /// This will return UnsupportedType if the assertion data is binary 73 pub const fn value(&self) -> Result<&Value> { 74 match &self.data { 75 ManifestData::Json(d) => Ok(d), 76 ManifestData::Binary(_) => Err(Error::UnsupportedType), 77 } 78 } 79 80 /// The data of the assertion as u8 binary vector 81 /// This will return UnsupportedType if the assertion data is Json/String 82 pub fn binary(&self) -> Result<&[u8]> { 83 match &self.data { 84 ManifestData::Json(_) => Err(Error::UnsupportedType), 85 ManifestData::Binary(b) => Ok(b), 86 } 87 } 88 89 /// The instance number of this assertion 90 /// If the same label is used for multiple assertions, incremental instances are added 91 /// The first instance is always 1 and increased by 1 per duplicated label 92 pub fn instance(&self) -> usize { 93 self.instance.unwrap_or(1) 94 } 95 96 /// The ManifestAssertionKind for this assertion 97 /// This refers to how the format of the assertion inside a C2PA manifest 98 /// The default is ManifestAssertionKind::Cbor 99 pub const fn kind(&self) -> &ManifestAssertionKind { 100 match self.kind.as_ref() { 101 Some(kind) => kind, 102 None => &ManifestAssertionKind::Cbor, 103 } 104 } 105 106 /// This can be used to set an instance number, but generally should not be used 107 /// Instance numbers will be assigned automatically when the assertions are embedded 108 pub(crate) const fn set_instance(mut self, instance: usize) -> Self { 109 self.instance = if instance > 0 { Some(instance) } else { None }; 110 self 111 } 112 113 /// Allows overriding the default [ManifestAssertionKind] to Json 114 /// For assertions like Schema.org that require being stored in Json format 115 pub const fn set_kind(mut self, kind: ManifestAssertionKind) -> Self { 116 self.kind = Some(kind); 117 self 118 } 119 120 /// Creates a ManifestAssertion with the given label and any serde serializable object 121 /// 122 /// # Example: Creating a custom assertion from a serde_json object. 123 /// 124 ///``` 125 /// # use c2pa::Result; 126 /// use c2pa::ManifestAssertion; 127 /// use serde_json::json; 128 /// # fn main() -> Result<()> { 129 /// let value = json!({"my_tag": "Anything I want"}); 130 /// let _ma = ManifestAssertion::from_labeled_assertion("org.contentauth.foo", &value)?; 131 /// # Ok(()) 132 /// # } 133 /// ``` 134 pub fn from_labeled_assertion<S: Into<String>, T: Serialize>( 135 label: S, 136 data: &T, 137 ) -> Result<Self> { 138 Ok(Self::new( 139 label.into(), 140 serde_json::to_value(data).map_err(|_err| Error::AssertionEncoding)?, 141 )) 142 } 143 144 /// Creates a ManifestAssertion from an AssertionBase object 145 /// 146 /// # Example: Creating a custom assertion an Action assertion 147 /// 148 ///``` 149 /// # use c2pa::Result; 150 /// use c2pa::{ 151 /// assertions::{c2pa_action, Action, Actions}, 152 /// ManifestAssertion, 153 /// }; 154 /// # fn main() -> Result<()> { 155 /// let actions = Actions::new().add_action(Action::new(c2pa_action::EDITED)); 156 /// let _ma = ManifestAssertion::from_labeled_assertion(Actions::LABEL, &actions)?; 157 /// # Ok(()) 158 /// # } 159 /// ``` 160 pub fn from_assertion<T: Serialize + AssertionBase>(data: &T) -> Result<Self> { 161 Ok(Self::new( 162 data.label().to_owned(), 163 serde_json::to_value(data).map_err(|_err| Error::AssertionEncoding)?, 164 )) 165 } 166 167 /// Creates an Assertion object from a ManifestAssertion 168 /// 169 /// # Example: extracting an Actions Assertion 170 /// ``` 171 /// # use c2pa::Result; 172 /// use c2pa::{ 173 /// assertions::{c2pa_action, Action, Actions}, 174 /// ManifestAssertion, 175 /// }; 176 /// # fn main() -> Result<()> { 177 /// let actions = Actions::new().add_action(Action::new(c2pa_action::EDITED)); 178 /// let manifest_assertion = ManifestAssertion::from_labeled_assertion(Actions::LABEL, &actions)?; 179 /// 180 /// let actions: Actions = manifest_assertion.to_assertion()?; 181 /// for action in actions.actions { 182 /// println!("{}", action.action()); 183 /// } 184 /// # Ok(()) 185 /// # } 186 /// ``` 187 pub fn to_assertion<T: DeserializeOwned>(&self) -> Result<T> { 188 serde_json::from_value(self.value()?.to_owned()).map_err(|e| { 189 Error::AssertionDecoding(AssertionDecodeError::from_json_err( 190 self.label.to_owned(), 191 None, 192 "application/json".to_owned(), 193 e, 194 )) 195 }) 196 } 197 } 198 199 #[cfg(test)] 200 pub(crate) mod tests { 201 #![allow(clippy::expect_used)] 202 #![allow(clippy::unwrap_used)] 203 204 use super::*; 205 use crate::assertions::{c2pa_action, Action, Actions}; 206 207 #[test] 208 fn test_from_labeled() { 209 let data = serde_json::json!({"mytag": "mydata"}); 210 let ma = ManifestAssertion::from_labeled_assertion("org.contentauth.foo", &data) 211 .expect("from_labeled_assertion"); 212 assert_eq!(ma.label(), "org.contentauth.foo"); 213 assert!(ma.value().is_ok()); 214 } 215 216 #[test] 217 fn test_manifest_assertion() { 218 let actions = Actions::new().add_action(Action::new(c2pa_action::EDITED)); 219 let value = serde_json::to_value(actions).unwrap(); 220 let mut ma = ManifestAssertion::new(Actions::LABEL.to_owned(), value); 221 assert_eq!(ma.label(), Actions::LABEL); 222 223 ma = ma.set_instance(0); 224 assert_eq!(ma.instance, None); 225 ma = ma.set_instance(1); 226 assert_eq!(ma.instance(), 1); 227 ma = ma.set_instance(2); 228 assert_eq!(ma.instance(), 2); 229 assert_eq!(ma.kind(), &ManifestAssertionKind::Cbor); 230 ma = ma.set_kind(ManifestAssertionKind::Json); 231 assert_eq!(ma.kind(), &ManifestAssertionKind::Json); 232 233 let actions = Actions::new().add_action(Action::new(c2pa_action::EDITED)); 234 let ma2 = ManifestAssertion::from_assertion(&actions).expect("from_assertion"); 235 let actions2: Actions = ma2.to_assertion().expect("to_assertion"); 236 let actions3 = ManifestAssertion::from_labeled_assertion("foo".to_owned(), &actions2) 237 .expect("from_labeled_assertion"); 238 assert_eq!(actions3.label(), "foo"); 239 } 240 }