Skip to main content

xapi_data/
statement_object.rs

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