1use 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#[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 pub fn builder() -> SubStatementBuilder {
99 SubStatementBuilder::default()
100 }
101
102 pub fn check_object_type(&self) -> bool {
107 self.object_type == ObjectType::SubStatement
108 }
109
110 pub fn actor(&self) -> &Actor {
116 &self.actor
117 }
118
119 pub fn verb(&self) -> &Verb {
121 &self.verb
122 }
123
124 pub fn object(&self) -> &SubStatementObject {
131 &self.object
132 }
133
134 pub fn result(&self) -> Option<&XResult> {
136 self.result.as_ref()
137 }
138
139 pub fn context(&self) -> Option<&Context> {
141 self.context.as_ref()
142 }
143
144 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 pub fn attachments(&self) -> Option<&[Attachment]> {
158 self.attachments.as_deref()
159 }
160
161 pub fn uid(&self) -> u64 {
163 fingerprint_it(self)
164 }
165
166 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 self.actor.fingerprint(state);
216 self.verb.fingerprint(state);
217 self.object.fingerprint(state);
218 }
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 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#[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 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 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 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 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 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 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 pub fn with_timestamp(mut self, val: DateTime<Utc>) -> Self {
337 self._timestamp = Some(MyTimestamp::from(val));
338 self
339 }
340
341 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 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}