Skip to main content

xapi_data/
statement_result.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3use crate::{DataError, Statement, StatementId, validate_irl};
4use core::fmt;
5use iri_string::types::{IriStr, IriString};
6use serde::{Deserialize, Serialize};
7use serde_with::skip_serializing_none;
8use tracing::warn;
9
10/// Structure that contains zero, one, or more [Statement]s.
11///
12/// The `statements` field will contain the result of a **`GET`** _Statement_
13/// Resource. If it is incomplete (due for example to pagination), the rest can
14/// be accessed at the IRL provided by the `more` property.
15///
16#[skip_serializing_none]
17#[derive(Debug, Default, Deserialize, Serialize)]
18pub struct StatementResult {
19    statements: Vec<Statement>,
20    more: Option<IriString>,
21}
22
23/// A doppelgänger structure, similar to that of [StatementId], that abides
24/// by the 'ids' format rules, but this time for [StatementResult].
25/// 
26#[skip_serializing_none]
27#[derive(Debug, Serialize)]
28pub struct StatementResultId {
29    statements: Vec<StatementId>,
30    more: Option<IriString>,
31}
32
33impl From<StatementResult> for StatementResultId {
34    fn from(value: StatementResult) -> Self {
35        StatementResultId {
36            statements: value
37                .statements
38                .into_iter()
39                .map(StatementId::from)
40                .collect(),
41            more: value.more,
42        }
43    }
44}
45
46impl StatementResult {
47    /// Construct a new instance from a given collection of [Statement]s.
48    pub fn from(statements: Vec<Statement>) -> Self {
49        StatementResult {
50            statements,
51            more: None,
52        }
53    }
54
55    /// Return a reference to this instance's statements collection.
56    pub fn statements(&self) -> &Vec<Statement> {
57        self.statements.as_ref()
58    }
59
60    /// Return TRUE if the `statements` collection is empty.
61    pub fn is_empty(&self) -> bool {
62        self.statements.is_empty()
63    }
64
65    /// Return the `more` field of this instance if set; `None` otherwise.
66    pub fn more(&self) -> Option<&IriStr> {
67        self.more.as_deref()
68    }
69
70    /// Set the `more` field.
71    ///
72    /// Raise [DataError] if the argument is empty, cannot be parsed as
73    /// an IRI, or the resulting IRI is not a valid URL.
74    pub fn set_more(&mut self, val: &str) -> Result<(), DataError> {
75        let s = val.trim();
76        if s.is_empty() {
77            warn!("Input value is empty. Unset URL");
78            self.more = None;
79        } else {
80            let iri = IriStr::new(s)?;
81            validate_irl(iri)?;
82            self.more = Some(iri.to_owned());
83        }
84        Ok(())
85    }
86}
87
88impl fmt::Display for StatementResult {
89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        let statements = self
91            .statements
92            .iter()
93            .map(|x| x.to_string())
94            .collect::<Vec<_>>()
95            .join(", ");
96        if let Some(z_more) = self.more.as_ref() {
97            write!(f, "StatementResult{{[ {} ], '{}'}}", statements, z_more)
98        } else {
99            write!(f, "StatementResult{{[ {statements} ]}}")
100        }
101    }
102}
103
104impl StatementResultId {
105    /// Construct a new instance of this from a [StatementId] one, setting
106    /// the `more` field to `None`.
107    pub fn from(statements: Vec<StatementId>) -> Self {
108        StatementResultId {
109            statements,
110            more: None,
111        }
112    }
113
114    /// Set this `more` field to given value.
115    pub fn set_more(&mut self, val: &str) -> Result<(), DataError> {
116        let s = val.trim();
117        let iri = IriStr::new(s)?;
118        self.more = Some(iri.to_owned());
119        Ok(())
120    }
121
122    /// Return whether this is empty (TRUE); i.e. has zero [StatementId]s,
123    /// or not (FALSE).
124    pub fn is_empty(&self) -> bool {
125        self.statements.is_empty()
126    }
127
128    /// Return this [StatementId]s collection.
129    pub fn statements(&self) -> &Vec<StatementId> {
130        self.statements.as_ref()
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn test_deserialization() {
140        const SR: &str = r#"{
141"statements":[{
142  "id":"01932d1e-a584-79d2-b83a-6b380546b21c",
143  "actor":{"mbox":"mailto:agent99@adlnet.gov"},
144  "verb":{"id":"http://adlnet.gov/expapi/verbs/attended"},
145  "object":{
146    "objectType":"SubStatement",
147    "actor":{"objectType":"Group","member":[],"mbox":"mailto:groupb@adlnet.gov"},
148    "verb":{"id":"http://adlnet.gov/expapi/verbs/attended"},
149    "object":{"id":"http://www.example.com/unicode/36c47486-83c8-4b4f-872c-67af87e9ad10"},
150    "timestamp":"2024-11-20T00:06:06.838Z"
151  },
152  "timestamp":"2024-11-20T00:06:06.801Z",
153  "stored":"2024-11-20T00:06:06.802+00:00",
154  "authority":{"mbox":"mailto:admin@my.xapi.net"}
155}],
156"more":null}"#;
157
158        let sr: StatementResult = serde_json::from_str(SR).unwrap();
159        assert_eq!(sr.statements().len(), 1);
160    }
161}