xapi_rs/data/
sub_statement.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3use crate::{
4    data::{
5        Actor, ActorId, Attachment, Context, ContextId, DataError, Fingerprint, MyTimestamp,
6        ObjectType, SubStatementObject, SubStatementObjectId, Validate, ValidationError, Verb,
7        VerbId, XResult, fingerprint_it,
8    },
9    emit_error,
10};
11use chrono::{DateTime, Utc};
12use core::fmt;
13use serde::{Deserialize, Serialize};
14use serde_with::skip_serializing_none;
15use std::{hash::Hasher, str::FromStr};
16
17/// Alternative representation of a [Statement][1] when referenced as the
18/// _object_ of another.
19///
20/// [1]: crate::Statement
21#[skip_serializing_none]
22#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
23#[serde(deny_unknown_fields)]
24pub struct SubStatement {
25    #[serde(rename = "objectType")]
26    object_type: ObjectType,
27    actor: Actor,
28    verb: Verb,
29    object: SubStatementObject,
30    result: Option<XResult>,
31    context: Option<Context>,
32    timestamp: Option<MyTimestamp>,
33    attachments: Option<Vec<Attachment>>,
34}
35
36#[skip_serializing_none]
37#[derive(Debug, Serialize)]
38#[serde(deny_unknown_fields)]
39pub(crate) struct SubStatementId {
40    #[serde(rename = "objectType")]
41    object_type: ObjectType,
42    actor: ActorId,
43    verb: VerbId,
44    object: SubStatementObjectId,
45    result: Option<XResult>,
46    context: Option<ContextId>,
47    timestamp: Option<MyTimestamp>,
48    attachments: Option<Vec<Attachment>>,
49}
50
51impl From<SubStatement> for SubStatementId {
52    fn from(value: SubStatement) -> Self {
53        SubStatementId {
54            object_type: ObjectType::SubStatement,
55            actor: ActorId::from(value.actor),
56            verb: VerbId::from(value.verb),
57            object: SubStatementObjectId::from(value.object),
58            result: value.result,
59            context: value.context.map(ContextId::from),
60            timestamp: value.timestamp,
61            attachments: value.attachments,
62        }
63    }
64}
65
66impl From<Box<SubStatement>> for SubStatementId {
67    fn from(value: Box<SubStatement>) -> Self {
68        SubStatementId {
69            object_type: ObjectType::SubStatement,
70            actor: ActorId::from(value.actor),
71            verb: VerbId::from(value.verb),
72            object: SubStatementObjectId::from(value.object),
73            result: value.result,
74            context: value.context.map(ContextId::from),
75            timestamp: value.timestamp,
76            attachments: value.attachments,
77        }
78    }
79}
80
81impl From<SubStatementId> for SubStatement {
82    fn from(value: SubStatementId) -> Self {
83        SubStatement {
84            object_type: ObjectType::SubStatement,
85            actor: Actor::from(value.actor),
86            verb: Verb::from(value.verb),
87            object: SubStatementObject::from(value.object),
88            result: value.result,
89            context: value.context.map(Context::from),
90            timestamp: value.timestamp,
91            attachments: value.attachments,
92        }
93    }
94}
95
96impl SubStatement {
97    /// Return a [SubStatement] _Builder_.
98    pub fn builder() -> SubStatementBuilder {
99        SubStatementBuilder::default()
100    }
101
102    /// Return TRUE if the `objectType` property is [SubStatement][1]; FALSE
103    /// otherwise.
104    ///
105    /// [1]: ObjectType#variant.SubStatement
106    pub fn check_object_type(&self) -> bool {
107        self.object_type == ObjectType::SubStatement
108    }
109
110    /// Return the [Actor] whom the Sub-Statement is about. The [Actor] is either
111    /// an [Agent][1] or a [Group][2].
112    ///
113    /// [1]: crate::Agent
114    /// [2]: crate::Group
115    pub fn actor(&self) -> &Actor {
116        &self.actor
117    }
118
119    /// Return the _action_ taken by the _actor_.
120    pub fn verb(&self) -> &Verb {
121        &self.verb
122    }
123
124    /// Return an [Activity][1], an [Agent][2], or another [Statement][3] that
125    /// is the _Object_ of this Sub-Statement.
126    ///
127    /// [1]: crate::Activity
128    /// [2]: crate::Agent
129    /// [3]: crate::Statement
130    pub fn object(&self) -> &SubStatementObject {
131        &self.object
132    }
133
134    /// Return the [Result] instance if set; `None` otherwise.
135    pub fn result(&self) -> Option<&XResult> {
136        self.result.as_ref()
137    }
138
139    /// Return the [Context] of this instance if set; `None` otherwise.
140    pub fn context(&self) -> Option<&Context> {
141        self.context.as_ref()
142    }
143
144    /// Return timestamp of when the events described in this [SubStatement]
145    /// occurred if set; `None` otherwise.
146    ///
147    /// It's set by the LRS if not provided.
148    pub fn timestamp(&self) -> Option<&DateTime<Utc>> {
149        if self.timestamp.is_none() {
150            None
151        } else {
152            Some(self.timestamp.as_ref().unwrap().inner())
153        }
154    }
155
156    /// Return [`attachments`][Attachment] if set; `None` otherwise.
157    pub fn attachments(&self) -> Option<&[Attachment]> {
158        self.attachments.as_deref()
159    }
160
161    /// Return fingerprint of this instance.
162    pub fn uid(&self) -> u64 {
163        fingerprint_it(self)
164    }
165
166    /// Return TRUE if this is _Equivalent_ to `that`; FALSE otherwise.
167    pub fn equivalent(&self, that: &SubStatement) -> bool {
168        self.uid() == that.uid()
169    }
170}
171
172impl fmt::Display for SubStatement {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        let mut vec = vec![];
175
176        vec.push(format!("actor: {}", self.actor));
177        vec.push(format!("verb: {}", self.verb));
178        vec.push(format!("object: {}", self.object));
179        if self.result.is_some() {
180            vec.push(format!("result: {}", self.result.as_ref().unwrap()));
181        }
182        if self.context.is_some() {
183            vec.push(format!("context: {}", self.context.as_ref().unwrap()));
184        }
185        if self.timestamp.is_some() {
186            vec.push(format!(
187                "timestamp: \"{}\"",
188                self.timestamp.as_ref().unwrap()
189            ));
190        }
191        if self.attachments.is_some() {
192            let items = self.attachments.as_deref().unwrap();
193            vec.push(format!(
194                "attachments: [{}]",
195                items
196                    .iter()
197                    .map(|x| x.to_string())
198                    .collect::<Vec<_>>()
199                    .join(", ")
200            ))
201        }
202
203        let res = vec
204            .iter()
205            .map(|x| x.to_string())
206            .collect::<Vec<_>>()
207            .join(", ");
208        write!(f, "SubStatement{{ {res} }}")
209    }
210}
211
212impl Fingerprint for SubStatement {
213    fn fingerprint<H: Hasher>(&self, state: &mut H) {
214        // discard `object_type`
215        self.actor.fingerprint(state);
216        self.verb.fingerprint(state);
217        self.object.fingerprint(state);
218        // self.result.hash(state);
219        // self.context.hash(state);
220        // discard `timestamp`, `stored`, `authority`, `version` and `attachments`
221    }
222}
223
224impl Validate for SubStatement {
225    fn validate(&self) -> Vec<ValidationError> {
226        let mut vec = vec![];
227
228        if !self.check_object_type() {
229            vec.push(ValidationError::WrongObjectType {
230                expected: ObjectType::SubStatement,
231                found: self.object_type.to_string().into(),
232            })
233        }
234        vec.extend(self.actor.validate());
235        vec.extend(self.verb.validate());
236        vec.extend(self.object.validate());
237        if self.result.is_some() {
238            vec.extend(self.result.as_ref().unwrap().validate())
239        }
240        if self.context.is_some() {
241            vec.extend(self.context.as_ref().unwrap().validate());
242            // NOTE (rsn) 20241017 - same as in Statement...
243            if !self.object().is_activity()
244                && (self.context().as_ref().unwrap().revision().is_some()
245                    || self.context().as_ref().unwrap().platform().is_some())
246            {
247                vec.push(ValidationError::ConstraintViolation(
248                    "SubStatement context w/ revision | platform but object != Activity".into(),
249                ))
250            }
251        }
252        if self.attachments.is_some() {
253            for att in self.attachments.as_ref().unwrap().iter() {
254                vec.extend(att.validate())
255            }
256        }
257
258        vec
259    }
260}
261
262/// A Type that knows how to construct a [SubStatement].
263#[derive(Debug, Default)]
264pub struct SubStatementBuilder {
265    _actor: Option<Actor>,
266    _verb: Option<Verb>,
267    _object: Option<SubStatementObject>,
268    _result: Option<XResult>,
269    _context: Option<Context>,
270    _timestamp: Option<MyTimestamp>,
271    _attachments: Option<Vec<Attachment>>,
272}
273
274impl SubStatementBuilder {
275    /// Set the `actor` field.
276    ///
277    /// Raise [DataError] if the argument is invalid.
278    pub fn actor(mut self, val: Actor) -> Result<Self, DataError> {
279        val.check_validity()?;
280        self._actor = Some(val);
281        Ok(self)
282    }
283
284    /// Set the `verb` field.
285    ///
286    /// Raise [DataError] if the argument is invalid.
287    pub fn verb(mut self, val: Verb) -> Result<Self, DataError> {
288        val.check_validity()?;
289        self._verb = Some(val);
290        Ok(self)
291    }
292
293    /// Set the `object` field.
294    ///
295    /// Raise [DataError] if the argument is invalid.
296    pub fn object(mut self, val: SubStatementObject) -> Result<Self, DataError> {
297        val.check_validity()?;
298        self._object = Some(val);
299        Ok(self)
300    }
301
302    /// Set the `result` field.
303    ///
304    /// Raise [DataError] if the argument is invalid.
305    pub fn result(mut self, val: XResult) -> Result<Self, DataError> {
306        val.check_validity()?;
307        self._result = Some(val);
308        Ok(self)
309    }
310
311    /// Set the `context` field.
312    ///
313    /// Raise [DataError] if the argument is invalid.
314    pub fn context(mut self, val: Context) -> Result<Self, DataError> {
315        val.check_validity()?;
316        self._context = Some(val);
317        Ok(self)
318    }
319
320    /// Set the `timestamp` field.
321    ///
322    /// Raise [DataError] if the argument is empty or invalid.
323    pub fn timestamp(mut self, val: &str) -> Result<Self, DataError> {
324        let val = val.trim();
325        if val.is_empty() {
326            emit_error!(DataError::Validation(ValidationError::Empty(
327                "timestamp".into()
328            )))
329        }
330        let ts = MyTimestamp::from_str(val)?;
331        self._timestamp = Some(ts);
332        Ok(self)
333    }
334
335    /// Replace the `timestamp` field w/ `val`.
336    pub fn with_timestamp(mut self, val: DateTime<Utc>) -> Self {
337        self._timestamp = Some(MyTimestamp::from(val));
338        self
339    }
340
341    /// Add `att` to `attachments` field if valid; otherwise raise a
342    /// [DataError].
343    pub fn attachment(mut self, att: Attachment) -> Result<Self, DataError> {
344        att.check_validity()?;
345        if self._attachments.is_none() {
346            self._attachments = Some(vec![])
347        }
348        self._attachments.as_mut().unwrap().push(att);
349        Ok(self)
350    }
351
352    /// Create a [SubStatement] from set field values.
353    ///
354    /// Raise [DataError] if an inconsistency is detected.
355    pub fn build(self) -> Result<SubStatement, DataError> {
356        if self._actor.is_none() || self._verb.is_none() || self._object.is_none() {
357            emit_error!(DataError::Validation(ValidationError::MissingField(
358                "actor | verb | object".into()
359            )))
360        }
361        Ok(SubStatement {
362            object_type: ObjectType::SubStatement,
363            actor: self._actor.unwrap(),
364            verb: self._verb.unwrap(),
365            object: self._object.unwrap(),
366            result: self._result,
367            context: self._context,
368            timestamp: self._timestamp,
369            attachments: self._attachments,
370        })
371    }
372}