1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2023 Adobe. All rights reserved.
// This file is licensed to you under the Apache License,
// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
// or the MIT license (http://opensource.org/licenses/MIT),
// at your option.

// Unless required by applicable law or agreed to in writing,
// this software is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
// implied. See the LICENSE-MIT and LICENSE-APACHE files for the
// specific language governing permissions and limitations under

// each license.
use std::collections::HashMap;

#[cfg(feature = "json_schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::resource_store::UriOrResource;

/// Description of the claim generator, or the software used in generating the claim.
///
/// This structure is also used for actions softwareAgent
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub struct ClaimGeneratorInfo {
    /// A human readable string naming the claim_generator
    pub name: String,
    /// A human readable string of the product's version
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,
    /// hashed URI to the icon (either embedded or remote)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub icon: Option<UriOrResource>,
    // Any other values that are not part of the standard
    #[serde(flatten)]
    other: HashMap<String, Value>,
}

impl Default for ClaimGeneratorInfo {
    fn default() -> Self {
        Self {
            name: crate::NAME.to_string(),
            version: Some(env!("CARGO_PKG_VERSION").to_string()),
            icon: None,
            other: HashMap::new(),
        }
    }
}

impl ClaimGeneratorInfo {
    pub fn new<S: Into<String>>(name: S) -> Self {
        Self {
            name: name.into(),
            version: None,
            icon: None,
            other: HashMap::new(),
        }
    }

    /// Returns the software agent that performed the action.
    pub fn icon(&self) -> Option<&UriOrResource> {
        self.icon.as_ref()
    }

    /// Sets the version of the generator.
    pub fn set_version<S: Into<String>>(&mut self, version: S) -> &mut Self {
        self.version = Some(version.into());
        self
    }

    /// Sets the icon of the generator.
    pub fn set_icon<S: Into<UriOrResource>>(&mut self, uri_or_resource: S) -> &mut Self {
        self.icon = Some(uri_or_resource.into());
        self
    }

    /// Adds a new key/value pair to the generator info.
    pub fn insert<K, V>(&mut self, key: K, value: V) -> &Self
    where
        K: Into<String>,
        V: Into<Value>,
    {
        self.other.insert(key.into(), value.into());
        self
    }

    /// Gets additional values by key.
    pub fn get(&self, key: &str) -> Option<&Value> {
        self.other.get(key)
    }
}

#[cfg(test)]
pub mod tests {
    #![allow(clippy::expect_used)]
    #![allow(clippy::unwrap_used)]

    use super::*;
    use crate::{hashed_uri::HashedUri, resource_store::ResourceRef};

    #[test]
    fn test_resource_ref() {
        let mut g = super::ClaimGeneratorInfo::new("test");
        g.set_version("1.0")
            .set_icon(ResourceRef::new("image/svg", "myicon"));

        let json = serde_json::to_string_pretty(&g).expect("Failed to serialize");
        println!("{json}");

        let result: ClaimGeneratorInfo =
            serde_json::from_str(&json).expect("Failed to deserialize");

        assert_eq!(g, result);
    }

    #[test]
    fn test_hashed_uri() {
        let mut g = super::ClaimGeneratorInfo::new("test");
        g.set_version("1.0").set_icon(HashedUri::new(
            "self#jumbf=c2pa.databoxes.data_box".to_string(),
            None,
            b"hashed",
        ));

        let json = serde_json::to_string_pretty(&g).expect("Failed to serialize");
        println!("{json}");

        let result: ClaimGeneratorInfo =
            serde_json::from_str(&json).expect("Failed to deserialize");

        assert_eq!(g, result);
    }
}