Skip to main content

stix_rs/sdos/
grouping.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::common::{CommonProperties, StixObject};
5
6/// Grouping SDO
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub struct Grouping {
10    #[serde(flatten)]
11    pub common: CommonProperties,
12
13    pub name: Option<String>,
14    pub description: Option<String>,
15    pub context: String,
16    pub object_refs: Vec<String>,
17}
18
19impl Grouping {
20    pub fn builder() -> GroupingBuilder {
21        GroupingBuilder::default()
22    }
23}
24
25#[derive(Debug, Default)]
26pub struct GroupingBuilder {
27    name: Option<String>,
28    description: Option<String>,
29    context: Option<String>,
30    object_refs: Option<Vec<String>>,
31    created_by_ref: Option<String>,
32}
33
34impl GroupingBuilder {
35    pub fn name(mut self, name: impl Into<String>) -> Self {
36        self.name = Some(name.into());
37        self
38    }
39    pub fn description(mut self, d: impl Into<String>) -> Self {
40        self.description = Some(d.into());
41        self
42    }
43    pub fn context(mut self, c: impl Into<String>) -> Self {
44        self.context = Some(c.into());
45        self
46    }
47    pub fn object_refs(mut self, o: Vec<String>) -> Self {
48        self.object_refs = Some(o);
49        self
50    }
51    pub fn created_by_ref(mut self, r: impl Into<String>) -> Self {
52        self.created_by_ref = Some(r.into());
53        self
54    }
55
56    pub fn build(self) -> Result<Grouping, super::BuilderError> {
57        let context = self
58            .context
59            .ok_or(super::BuilderError::MissingField("context"))?;
60        let object_refs = self
61            .object_refs
62            .ok_or(super::BuilderError::MissingField("object_refs"))?;
63        let common = CommonProperties::new("grouping", self.created_by_ref);
64        Ok(Grouping {
65            common,
66            name: self.name,
67            description: self.description,
68            context,
69            object_refs,
70        })
71    }
72}
73
74impl StixObject for Grouping {
75    fn id(&self) -> &str {
76        &self.common.id
77    }
78    fn type_(&self) -> &str {
79        &self.common.r#type
80    }
81    fn created(&self) -> DateTime<Utc> {
82        self.common.created
83    }
84}
85
86impl From<Grouping> for crate::StixObjectEnum {
87    fn from(g: Grouping) -> Self {
88        crate::StixObjectEnum::Grouping(g)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use serde_json::Value;
96
97    #[test]
98    fn grouping_builder_and_serialize() {
99        let g = Grouping::builder()
100            .context("suspicious-activity")
101            .object_refs(vec!["file--1234".into()])
102            .build()
103            .unwrap();
104
105        let s = serde_json::to_string(&g).unwrap();
106        let v: Value = serde_json::from_str(&s).unwrap();
107        assert_eq!(v.get("type").and_then(Value::as_str).unwrap(), "grouping");
108        assert!(v.get("object_refs").is_some());
109    }
110}