Skip to main content

xapi_data/
actor.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3use crate::{
4    Account, Agent, AgentId, CIString, DataError, Fingerprint, Group, GroupId, MyEmailAddress,
5    Validate, ValidationError, emit_error, fingerprint::fingerprint_it,
6};
7use core::fmt;
8use iri_string::types::UriStr;
9use serde::{
10    Deserialize, Serialize,
11    de::{self, Error},
12};
13use serde_json::{Map, Value};
14use std::{hash::Hasher, str::FromStr};
15use tracing::{debug, error};
16
17/// Representation of an individual ([Agent]) or group ([Group]) (a) referenced
18/// in a [Statement][1] involved in an action within an [Activity][2] or (b) is
19/// the `authority` asserting the truthfulness of [Statement][1]s.
20///
21/// [1]: crate::Statement
22/// [2]: crate::Activity
23#[derive(Clone, Debug, PartialEq, Serialize)]
24#[serde(untagged)]
25pub enum Actor {
26    /// The [Actor] is effectively an [Agent].
27    Agent(Agent),
28    /// The [Actor] is effectively a [Group] of [Agent]s.
29    Group(Group),
30}
31
32#[derive(Debug, Serialize)]
33#[serde(untagged)]
34pub(crate) enum ActorId {
35    Agent(AgentId),
36    Group(GroupId),
37}
38
39impl From<Actor> for ActorId {
40    fn from(value: Actor) -> Self {
41        match value {
42            Actor::Agent(agent) => ActorId::Agent(AgentId::from(agent)),
43            Actor::Group(group) => ActorId::Group(GroupId::from(group)),
44        }
45    }
46}
47
48impl From<ActorId> for Actor {
49    fn from(value: ActorId) -> Self {
50        match value {
51            ActorId::Agent(x) => Actor::Agent(Agent::from(x)),
52            ActorId::Group(x) => Actor::Group(Group::from(x)),
53        }
54    }
55}
56
57impl Actor {
58    /// Construct and validate an [Actor] from a JSON Object.
59    pub fn from_json_obj(map: Map<String, Value>) -> Result<Self, DataError> {
60        match map.get("objectType") {
61            Some(x) => {
62                if x == &serde_json::json!("Agent") {
63                    Ok(Actor::Agent(Agent::from_json_obj(map)?))
64                } else if x == &serde_json::json!("Group") {
65                    Ok(Actor::Group(Group::from_json_obj(map)?))
66                } else {
67                    emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
68                        format!("Unknown objectType ({x})").into()
69                    )))
70                }
71            }
72            None => {
73                debug!("Missing 'objectType'. Assume Agent + continue...");
74                Ok(Actor::Agent(Agent::from_json_obj(map)?))
75            }
76        }
77    }
78
79    /// Coerce an [Agent] to an [Actor].
80    pub fn from_agent(actor: Agent) -> Self {
81        Actor::Agent(actor)
82    }
83
84    /// Coerce a [Group] to an [Actor].
85    pub fn from_group(actor: Group) -> Self {
86        Actor::Group(actor)
87    }
88
89    /// Return TRUE if this is an [Agent] variant; FALSE otherwise.
90    pub fn is_agent(&self) -> bool {
91        matches!(self, Actor::Agent(_))
92    }
93
94    /// Return TRUE if this is a [Group] variant; FALSE otherwise.
95    pub fn is_group(&self) -> bool {
96        matches!(self, Actor::Group(_))
97    }
98
99    /// Coerce this to an [Agent] if indeed this was an `Actor::Agent` variant.
100    /// Raise [DataError] if it was not.
101    pub fn as_agent(&self) -> Result<Agent, DataError> {
102        match self {
103            Actor::Agent(x) => Ok(x.to_owned()),
104            _ => emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
105                format!("This ({self}) is NOT an Agent").into()
106            ))),
107        }
108    }
109
110    /// Coerce this to a [Group] if indeed this was an `Actor::Group` variant.
111    /// Raise [DataError] if it was not.
112    pub fn as_group(&self) -> Result<Group, DataError> {
113        match self {
114            Actor::Group(x) => Ok(x.to_owned()),
115            _ => emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
116                format!("This ({self}) is NOT a Group").into()
117            ))),
118        }
119    }
120
121    // ===== convenience methods common to every Actor =====
122
123    /// Return `name` field if set; `None` otherwise.
124    pub fn name(&self) -> Option<&CIString> {
125        match self {
126            Actor::Agent(x) => x.name(),
127            Actor::Group(x) => x.name(),
128        }
129    }
130
131    /// Return `name` field as a string reference if set; `None` otherwise.
132    pub fn name_as_str(&self) -> Option<&str> {
133        match self {
134            Actor::Agent(x) => x.name_as_str(),
135            Actor::Group(x) => x.name_as_str(),
136        }
137    }
138
139    /// Return `mbox` field if set; `None` otherwise.
140    pub fn mbox(&self) -> Option<&MyEmailAddress> {
141        match self {
142            Actor::Agent(x) => x.mbox(),
143            Actor::Group(x) => x.mbox(),
144        }
145    }
146
147    /// Return `mbox_sha1sum` field (hex-encoded SHA1 hash of this entity's
148    /// `mbox` URI) if set; `None` otherwise.
149    pub fn mbox_sha1sum(&self) -> Option<&str> {
150        match self {
151            Actor::Agent(x) => x.mbox_sha1sum(),
152            Actor::Group(x) => x.mbox_sha1sum(),
153        }
154    }
155
156    /// Return `openid` field (openID URI of this entity) if set; `None`
157    /// otherwise.
158    pub fn openid(&self) -> Option<&UriStr> {
159        match self {
160            Actor::Agent(x) => x.openid(),
161            Actor::Group(x) => x.openid(),
162        }
163    }
164
165    /// Return `account` field (reference to this entity's [Account]) if set;
166    /// `None` otherwise.
167    pub fn account(&self) -> Option<&Account> {
168        match self {
169            Actor::Agent(x) => x.account(),
170            Actor::Group(x) => x.account(),
171        }
172    }
173
174    /// Return the fingerprint of this instance.
175    pub fn uid(&self) -> u64 {
176        fingerprint_it(self)
177    }
178
179    /// Return TRUE if this is _Equivalent_ to `that`; FALSE otherwise.
180    pub fn equivalent(&self, that: &Actor) -> bool {
181        self.uid() == that.uid()
182    }
183}
184
185impl<'de> Deserialize<'de> for Actor {
186    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
187    where
188        D: serde::Deserializer<'de>,
189    {
190        let val = serde_json::Value::deserialize(deserializer)?;
191        match Map::deserialize(val.clone()) {
192            Ok(x) => {
193                if x.contains_key("objectType") {
194                    if let Ok(x) = Agent::deserialize(val.clone())
195                        && x.check_object_type()
196                    {
197                        return Ok(Actor::Agent(x));
198                    }
199                    match Group::deserialize(val) {
200                        Ok(x) => Ok(Actor::Group(x)),
201                        _ => Err(D::Error::unknown_variant("actor", &["Agent", "Group"])),
202                    }
203                } else {
204                    // NOTE (rsn) 20241121 - only Agent is allowed to not have an
205                    // explicit 'objectType' property in its serialization...
206                    match Agent::deserialize(val.clone()) {
207                        Ok(x) => Ok(Actor::Agent(x)),
208                        _ => {
209                            error!("Alleged Actor has no 'objectType' and is NOT an Agent");
210                            Err(D::Error::unknown_field("actor", &["Agent", "Group"]))
211                        }
212                    }
213                }
214            }
215            Err(x) => {
216                error!("Failed deserializing '{}' as Actor: {}", val, x);
217                Err(de::Error::unknown_field("actor", &["Agent", "Group"]))
218            }
219        }
220    }
221}
222
223impl fmt::Display for Actor {
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        match self {
226            Actor::Agent(x) => write!(f, "{x}"),
227            Actor::Group(x) => write!(f, "{x}"),
228        }
229    }
230}
231
232impl Fingerprint for Actor {
233    fn fingerprint<H: Hasher>(&self, state: &mut H) {
234        match self {
235            Actor::Agent(x) => x.fingerprint(state),
236            Actor::Group(x) => x.fingerprint(state),
237        }
238    }
239}
240
241impl Validate for Actor {
242    fn validate(&self) -> Vec<ValidationError> {
243        match self {
244            Actor::Agent(x) => x.validate(),
245            Actor::Group(x) => x.validate(),
246        }
247    }
248}
249
250impl FromStr for Actor {
251    type Err = DataError;
252
253    fn from_str(s: &str) -> Result<Self, Self::Err> {
254        let map = serde_json::from_str::<Map<String, Value>>(s)?;
255        Self::from_json_obj(map)
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262    use tracing_test::traced_test;
263
264    #[test]
265    fn test_serde_actor_agent() -> Result<(), DataError> {
266        const JSON: &str =
267            r#"{"objectType":"Agent","name":"Z User","mbox":"mailto:zuser@somewhere.net"}"#;
268        let a1 = Agent::builder()
269            .with_object_type()
270            .name("Z User")?
271            .mbox("zuser@somewhere.net")?
272            .build()?;
273        let actor = Actor::Agent(a1);
274        let se_result = serde_json::to_string(&actor);
275        assert!(se_result.is_ok());
276        let json = se_result.unwrap();
277        assert_eq!(json, JSON);
278
279        let de_result = serde_json::from_str::<Actor>(JSON);
280        assert!(de_result.is_ok());
281        if let Ok(Actor::Agent(a2)) = de_result {
282            assert_eq!(a2.name().unwrap(), "Z User");
283        }
284
285        Ok(())
286    }
287
288    #[test]
289    fn test_de_actor_agent() {
290        const JSON: &str = r#"{
291            "objectType":"Agent", 
292            "name":"Z User",
293            "mbox":"mailto:zuser@somewhere.net"
294        }"#;
295
296        let de_result = serde_json::from_str::<Actor>(JSON);
297        assert!(de_result.is_ok());
298    }
299
300    #[traced_test]
301    #[test]
302    fn test_actor_bad() {
303        const IN1: &str = r#"{ "objectType": "Foo", "foo": 42 }"#;
304        const IN2: &str = r#"{ "foo": 42 }"#;
305
306        let r1 = serde_json::from_str::<Actor>(IN1);
307        assert!(r1.is_err()); // unknown variant
308        assert!(r1.err().unwrap().is_data());
309
310        let r2 = serde_json::from_str::<Actor>(IN2);
311        assert!(r2.is_err()); // unknown field
312        assert!(r2.err().unwrap().is_data());
313    }
314}