xapi_rs/data/
statement_object.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3use crate::{
4    data::{
5        Activity, ActivityId, Agent, AgentId, DataError, Fingerprint, Group, GroupId, StatementRef,
6        SubStatement, SubStatementId, Validate, ValidationError,
7    },
8    emit_error,
9};
10use core::fmt;
11use serde::{
12    Deserialize, Serialize,
13    de::{self},
14};
15use serde_json::Value;
16use std::hash::Hasher;
17use tracing::{debug, error};
18
19/// Enumeration representing the _subject_ (or _target_) of an _action_ (a
20/// [Verb][1]) carried out by an [Actor][2] (an [Agent] or a [Group]) captured
21/// in a [Statement][5].
22///
23/// The exact variant of the _object_ is gleaned --explicitly most of the times
24/// but implicitly in special cases-- from its `objectType` property value (a
25/// variant of [ObjectType][6]).
26///
27/// IMPORTANT (rsn) - xAPI (Section 4.2.2 Statement, as well as 4.2.2.3 Object
28/// for the _Object As Sub-Statement Table_) define the _Object_ of a _Statement_
29/// as _"Activity, **Agent**, or another Statement that is the Object of the
30/// Statement."_ only. However, other sections of the same document mention it
31/// can also be a [Group]. Conformance tests, show that this is the case and
32/// that it applies for both _Statement_ and _SubStatement_.
33///
34/// [1]: crate::Verb
35/// [2]: crate::Actor
36/// [5]: crate::Statement
37/// [6]: crate::ObjectType
38#[derive(Debug, PartialEq, Serialize)]
39#[serde(untagged)]
40pub enum StatementObject {
41    /// The _object_ is an [Agent].
42    Agent(Agent),
43    /// The _object_ is a [Group].
44    Group(Group),
45    /// The _object_ is a [Statement-Reference][StatementRef].
46    StatementRef(StatementRef),
47    /// The _object_ is a [Sub-Statement][SubStatement].
48    SubStatement(Box<SubStatement>),
49    /// The _object_ is an [Activity].
50    Activity(Activity),
51}
52
53#[derive(Debug, Serialize)]
54#[serde(untagged)]
55pub(crate) enum StatementObjectId {
56    Activity(ActivityId),
57    Agent(AgentId),
58    Group(GroupId),
59    StatementRef(StatementRef),
60    SubStatement(Box<SubStatementId>),
61}
62
63impl From<StatementObject> for StatementObjectId {
64    fn from(value: StatementObject) -> Self {
65        match value {
66            StatementObject::Agent(agent) => StatementObjectId::Agent(agent.into()),
67            StatementObject::Group(group) => StatementObjectId::Group(group.into()),
68            StatementObject::StatementRef(stmt_ref) => StatementObjectId::StatementRef(stmt_ref),
69            StatementObject::SubStatement(sub_stmt) => {
70                StatementObjectId::SubStatement(Box::new(sub_stmt.into()))
71            }
72            StatementObject::Activity(activity) => StatementObjectId::Activity(activity.into()),
73        }
74    }
75}
76
77impl From<StatementObjectId> for StatementObject {
78    fn from(value: StatementObjectId) -> Self {
79        match value {
80            StatementObjectId::Activity(x) => StatementObject::Activity(Activity::from(x)),
81            StatementObjectId::Agent(x) => StatementObject::Agent(Agent::from(x)),
82            StatementObjectId::Group(x) => StatementObject::Group(Group::from(x)),
83            StatementObjectId::StatementRef(x) => StatementObject::StatementRef(x),
84            StatementObjectId::SubStatement(x) => {
85                StatementObject::SubStatement(Box::new(SubStatement::from(*x)))
86            }
87        }
88    }
89}
90
91impl<'de> Deserialize<'de> for StatementObject {
92    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
93    where
94        D: serde::Deserializer<'de>,
95    {
96        let v: Value = Deserialize::deserialize(deserializer)?;
97        match v {
98            Value::Object(ref map) => {
99                let ot = map.get("objectType").map_or(
100                    {
101                        debug!("Missing 'objectType'. Assume 'Activity' + continue");
102                        Some("Activity")
103                    },
104                    |x| x.as_str(),
105                );
106                match ot {
107                    Some("Agent") => match Agent::deserialize(v) {
108                        Ok(x) => Ok(StatementObject::Agent(x)),
109                        Err(x) => {
110                            let msg = format!("input is not Agent: {x}");
111                            error!("objectType is 'Agent', but {}", msg);
112                            Err(de::Error::custom(msg))
113                        }
114                    },
115                    Some("Group") => match Group::deserialize(v) {
116                        Ok(x) => Ok(StatementObject::Group(x)),
117                        Err(x) => {
118                            let msg = format!("input is not Group: {x}");
119                            error!("objectType is 'Group', but {}", msg);
120                            Err(de::Error::custom(msg))
121                        }
122                    },
123                    Some("StatementRef") => match StatementRef::deserialize(v) {
124                        Ok(x) => Ok(StatementObject::StatementRef(x)),
125                        Err(x) => {
126                            let msg = format!("input is not StatementRef: {x}");
127                            error!("objectType is 'StatementRef', but {}", msg);
128                            Err(de::Error::custom(msg))
129                        }
130                    },
131                    Some("SubStatement") => match SubStatement::deserialize(v) {
132                        Ok(x) => Ok(StatementObject::SubStatement(Box::new(x))),
133                        Err(x) => {
134                            let msg = format!("input is not SubStatement: {x}");
135                            error!("objectType is 'SubStatement', but {}", msg);
136                            Err(de::Error::custom(msg))
137                        }
138                    },
139                    Some("Activity") => match Activity::deserialize(v) {
140                        Ok(x) => Ok(StatementObject::Activity(x)),
141                        Err(x) => {
142                            let msg = format!("input is not Activity: {x}");
143                            error!("objectType is 'Activity', but {}", msg);
144                            Err(de::Error::custom(msg))
145                        }
146                    },
147                    _ => Err(de::Error::custom(
148                        "Unknown 'objectType'. Expected Agent | Group | StatementRef | SubStatement | Activity",
149                    )),
150                }
151            }
152            _ => Err(de::Error::custom("Expected JSON object")),
153        }
154    }
155}
156
157/// When storing a _Statement_ we indicate the kind of _Object_ it references
158/// w/ an integer value in the range [0..=4].
159#[derive(Debug)]
160#[doc(hidden)]
161pub enum ObjectKind {
162    /// Object is an [Activity]
163    ActivityObject = 0,
164    /// Object is an [Agent]
165    AgentObject = 1,
166    /// Object is a [Group]
167    GroupObject = 2,
168    /// Object is a [StatementRef]
169    StatementRefObject = 3,
170    /// Object is a [SubStatement]
171    SubStatementObject = 4,
172}
173
174impl fmt::Display for ObjectKind {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        match self {
177            ObjectKind::ActivityObject => write!(f, "[Activity]"),
178            ObjectKind::AgentObject => write!(f, "[Agent]"),
179            ObjectKind::GroupObject => write!(f, "[Group]"),
180            ObjectKind::StatementRefObject => write!(f, "[StatementRef]"),
181            ObjectKind::SubStatementObject => write!(f, "[SubStatement]"),
182        }
183    }
184}
185
186impl From<i16> for ObjectKind {
187    fn from(value: i16) -> Self {
188        match value {
189            0 => ObjectKind::ActivityObject,
190            1 => ObjectKind::AgentObject,
191            2 => ObjectKind::GroupObject,
192            3 => ObjectKind::StatementRefObject,
193            _ => ObjectKind::SubStatementObject,
194        }
195    }
196}
197
198impl StatementObject {
199    /// Construct a variant from the given [Activity] instance.
200    pub fn from_activity(obj: Activity) -> Self {
201        StatementObject::Activity(obj)
202    }
203
204    /// Construct a variant from the given [Agent] instance.
205    pub fn from_agent(obj: Agent) -> Self {
206        StatementObject::Agent(obj)
207    }
208
209    /// Construct a variant from the given [Group] instance]
210    pub fn from_group(obj: Group) -> Self {
211        StatementObject::Group(obj)
212    }
213
214    /// Construct a variant from the given [StatementRef] instance.
215    pub fn from_statement_ref(obj: StatementRef) -> Self {
216        StatementObject::StatementRef(obj)
217    }
218
219    /// Construct a variant from the given [SubStatement] instance.
220    pub fn from_sub_statement(obj: SubStatement) -> Self {
221        StatementObject::SubStatement(Box::new(obj))
222    }
223
224    /// Return TRUE if this is an [Activity][1] variant or FALSE otherwise.
225    ///
226    /// [1]: StatementObject#variant.Activity
227    pub fn is_activity(&self) -> bool {
228        matches!(self, StatementObject::Activity(_))
229    }
230
231    /// Return TRUE if this is an [Agent][1] variant or FALSE otherwise.
232    ///
233    /// [1]: StatementObject#variant.Agent
234    pub fn is_agent(&self) -> bool {
235        matches!(self, StatementObject::Agent(_))
236    }
237
238    /// Return TRUE if this is a [Group][1] variant or FALSE otherwise.
239    ///
240    /// [1]: StatementObject#variant.Group
241    pub fn is_group(&self) -> bool {
242        matches!(self, StatementObject::Group(_))
243    }
244
245    /// Return TRUE if this is an [StatementRef][1] variant or FALSE otherwise.
246    ///
247    /// [1]: StatementObject#variant.StatementRef
248    pub fn is_statement_ref(&self) -> bool {
249        matches!(self, StatementObject::StatementRef(_))
250    }
251
252    /// Return TRUE if this is a [SubStatement][1] variant or FALSE otherwise.
253    ///
254    /// [1]: StatementObject#variant.SubStatement
255    pub fn is_sub_statement(&self) -> bool {
256        matches!(self, StatementObject::SubStatement(_))
257    }
258
259    /// Return the target [Activity] if it was set; `None` otherwise.
260    pub fn as_activity(&self) -> Result<Activity, DataError> {
261        match self {
262            StatementObject::Activity(x) => Ok(x.to_owned()),
263            _ => emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
264                format!("This ({self}) is NOT an Activity").into()
265            ))),
266        }
267    }
268
269    /// Return the target if it was an [Agent]. Raise [DataError] otherwise.
270    pub fn as_agent(&self) -> Result<Agent, DataError> {
271        match self {
272            StatementObject::Agent(x) => Ok(x.to_owned()),
273            _ => emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
274                format!("This ({self}) is NOT an Agent").into()
275            ))),
276        }
277    }
278
279    /// Return the target if it was a [Group]. Raise [DataError] otherwise.
280    pub fn as_group(&self) -> Result<Group, DataError> {
281        match self {
282            StatementObject::Group(x) => Ok(x.to_owned()),
283            _ => emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
284                format!("This ({self}) is NOT a Group").into()
285            ))),
286        }
287    }
288
289    /// Return the target if it was a [Statement-Reference][crate::StatementRef].
290    /// Raise [DataError] otherwise.
291    pub fn as_statement_ref(&self) -> Result<StatementRef, DataError> {
292        match self {
293            StatementObject::StatementRef(x) => Ok(x.to_owned()),
294            _ => emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
295                format!("This ({self}) is NOT a Statement-Ref").into()
296            ))),
297        }
298    }
299
300    /// Return the target if it was a [Sub-Statement][crate::SubStatement]. Raise
301    /// [DataError] otherwise.
302    pub fn as_sub_statement(&self) -> Result<SubStatement, DataError> {
303        match self {
304            StatementObject::SubStatement(x) => Ok(*x.to_owned()),
305            _ => emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
306                format!("This ({self}) is NOT a Sub-Statement").into()
307            ))),
308        }
309    }
310
311    /// Return the enum variant _kind_ for this incarnation.
312    pub fn kind(&self) -> ObjectKind {
313        match self {
314            StatementObject::Activity(_) => ObjectKind::ActivityObject,
315            StatementObject::Agent(_) => ObjectKind::AgentObject,
316            StatementObject::Group(_) => ObjectKind::GroupObject,
317            StatementObject::StatementRef(_) => ObjectKind::StatementRefObject,
318            StatementObject::SubStatement(_) => ObjectKind::SubStatementObject,
319        }
320    }
321}
322
323impl fmt::Display for StatementObject {
324    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325        match self {
326            StatementObject::Agent(x) => write!(f, "{x}"),
327            StatementObject::Group(x) => write!(f, "{x}"),
328            StatementObject::StatementRef(x) => write!(f, "{x}"),
329            StatementObject::SubStatement(x) => write!(f, "{x}"),
330            StatementObject::Activity(x) => write!(f, "{x}"),
331        }
332    }
333}
334
335impl Fingerprint for StatementObject {
336    fn fingerprint<H: Hasher>(&self, state: &mut H) {
337        match self {
338            StatementObject::Agent(x) => x.fingerprint(state),
339            StatementObject::Group(x) => x.fingerprint(state),
340            StatementObject::StatementRef(x) => x.fingerprint(state),
341            StatementObject::SubStatement(x) => x.fingerprint(state),
342            StatementObject::Activity(x) => x.fingerprint(state),
343        }
344    }
345}
346
347impl Validate for StatementObject {
348    fn validate(&self) -> Vec<ValidationError> {
349        match self {
350            StatementObject::Agent(x) => x.validate(),
351            StatementObject::Group(x) => x.validate(),
352            StatementObject::StatementRef(x) => x.validate(),
353            StatementObject::SubStatement(x) => x.validate(),
354            StatementObject::Activity(x) => x.validate(),
355        }
356    }
357}