Skip to main content

stix_rs/sdos/
observed_data.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::common::{CommonProperties, StixObject};
5use crate::sdos::BuilderError;
6
7/// Observed Data SDO
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10pub struct ObservedData {
11    #[serde(flatten)]
12    pub common: CommonProperties,
13    pub first_observed: DateTime<Utc>,
14    pub last_observed: DateTime<Utc>,
15    pub number_observed: u32,
16    pub object_refs: Vec<String>,
17}
18
19impl ObservedData {
20    pub fn builder() -> ObservedDataBuilder {
21        ObservedDataBuilder::default()
22    }
23}
24
25#[derive(Debug, Default)]
26pub struct ObservedDataBuilder {
27    first_observed: Option<DateTime<Utc>>,
28    last_observed: Option<DateTime<Utc>>,
29    number_observed: Option<u32>,
30    object_refs: Option<Vec<String>>,
31    created_by_ref: Option<String>,
32}
33
34impl ObservedDataBuilder {
35    pub fn first_observed(mut self, t: DateTime<Utc>) -> Self {
36        self.first_observed = Some(t);
37        self
38    }
39
40    pub fn last_observed(mut self, t: DateTime<Utc>) -> Self {
41        self.last_observed = Some(t);
42        self
43    }
44
45    pub fn number_observed(mut self, n: u32) -> Self {
46        self.number_observed = Some(n);
47        self
48    }
49
50    pub fn object_refs(mut self, refs: Vec<String>) -> Self {
51        self.object_refs = Some(refs);
52        self
53    }
54
55    pub fn created_by_ref(mut self, r: impl Into<String>) -> Self {
56        self.created_by_ref = Some(r.into());
57        self
58    }
59
60    pub fn build(self) -> Result<ObservedData, BuilderError> {
61        let first = self
62            .first_observed
63            .ok_or(BuilderError::MissingField("first_observed"))?;
64        let last = self
65            .last_observed
66            .ok_or(BuilderError::MissingField("last_observed"))?;
67        let num = self
68            .number_observed
69            .ok_or(BuilderError::MissingField("number_observed"))?;
70        let objs = self
71            .object_refs
72            .ok_or(BuilderError::MissingField("object_refs"))?;
73        let common = CommonProperties::new("observed-data", self.created_by_ref);
74        Ok(ObservedData {
75            common,
76            first_observed: first,
77            last_observed: last,
78            number_observed: num,
79            object_refs: objs,
80        })
81    }
82}
83
84impl StixObject for ObservedData {
85    fn id(&self) -> &str {
86        &self.common.id
87    }
88
89    fn type_(&self) -> &str {
90        &self.common.r#type
91    }
92
93    fn created(&self) -> DateTime<Utc> {
94        self.common.created
95    }
96}
97
98impl From<ObservedData> for crate::StixObjectEnum {
99    fn from(o: ObservedData) -> Self {
100        crate::StixObjectEnum::ObservedData(o)
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use chrono::Utc;
108    use serde_json::Value;
109
110    #[test]
111    fn observed_data_builder() {
112        let od = ObservedData::builder()
113            .first_observed(Utc::now())
114            .last_observed(Utc::now())
115            .number_observed(1)
116            .object_refs(vec!["file--1234".into()])
117            .build()
118            .unwrap();
119
120        assert_eq!(od.number_observed, 1);
121        assert_eq!(od.common.r#type, "observed-data");
122    }
123
124    #[test]
125    fn observed_data_serialize() {
126        let od = ObservedData::builder()
127            .first_observed(Utc::now())
128            .last_observed(Utc::now())
129            .number_observed(1)
130            .object_refs(vec!["file--1234".into()])
131            .build()
132            .unwrap();
133
134        let s = serde_json::to_string(&od).unwrap();
135        let v: Value = serde_json::from_str(&s).unwrap();
136        assert_eq!(
137            v.get("type").and_then(Value::as_str).unwrap(),
138            "observed-data"
139        );
140        assert!(v.get("object_refs").is_some());
141    }
142}