xapi_rs/data/
actor.rs

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