stix_rs/sdos/
observed_data.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::common::{CommonProperties, StixObject};
5use crate::sdos::BuilderError;
6
7#[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}