1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7use crate::common::{ExternalReference, MaecObject};
8use crate::error::{MaecError, Result};
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
16#[serde(rename_all = "snake_case")]
17pub struct Behavior {
18 #[serde(flatten)]
20 pub common: crate::common::CommonProperties,
21
22 pub name: crate::vocab_large::Behavior,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub description: Option<String>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub timestamp: Option<DateTime<Utc>>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub attributes: Option<HashMap<String, serde_json::Value>>,
36
37 #[serde(default, skip_serializing_if = "Vec::is_empty")]
39 pub action_refs: Vec<String>,
40
41 #[serde(default, skip_serializing_if = "Vec::is_empty")]
43 pub technique_refs: Vec<ExternalReference>,
44}
45
46impl Behavior {
47 pub fn builder() -> BehaviorBuilder {
49 BehaviorBuilder::default()
50 }
51
52 pub fn new(name: crate::vocab_large::Behavior) -> Self {
54 Self {
55 common: crate::common::CommonProperties::new("behavior", None),
56 name,
57 description: None,
58 timestamp: None,
59 attributes: None,
60 action_refs: vec![],
61 technique_refs: vec![],
62 }
63 }
64
65 pub fn validate(&self) -> Result<()> {
67 if self.common.r#type != "behavior" {
68 return Err(MaecError::ValidationError(format!(
69 "type must be 'behavior', got '{}'",
70 self.common.r#type
71 )));
72 }
73
74 if !crate::common::is_valid_maec_id(&self.common.id) {
75 return Err(MaecError::InvalidId(self.common.id.clone()));
76 }
77
78 Ok(())
79 }
80}
81
82impl MaecObject for Behavior {
83 fn id(&self) -> &str {
84 &self.common.id
85 }
86
87 fn type_(&self) -> &str {
88 &self.common.r#type
89 }
90
91 fn created(&self) -> DateTime<Utc> {
92 self.common.created
93 }
94}
95
96#[derive(Debug, Default)]
98pub struct BehaviorBuilder {
99 id: Option<String>,
100 name: Option<crate::vocab_large::Behavior>,
101 description: Option<String>,
102 timestamp: Option<DateTime<Utc>>,
103 attributes: Option<HashMap<String, serde_json::Value>>,
104 action_refs: Vec<String>,
105 technique_refs: Vec<ExternalReference>,
106}
107
108impl BehaviorBuilder {
109 pub fn id(mut self, id: impl Into<String>) -> Self {
110 self.id = Some(id.into());
111 self
112 }
113
114 pub fn name(mut self, name: crate::vocab_large::Behavior) -> Self {
115 self.name = Some(name);
116 self
117 }
118
119 pub fn description(mut self, desc: impl Into<String>) -> Self {
120 self.description = Some(desc.into());
121 self
122 }
123
124 pub fn timestamp(mut self, timestamp: DateTime<Utc>) -> Self {
125 self.timestamp = Some(timestamp);
126 self
127 }
128
129 pub fn add_action_ref(mut self, ref_id: impl Into<String>) -> Self {
130 self.action_refs.push(ref_id.into());
131 self
132 }
133
134 pub fn add_technique_ref(mut self, reference: ExternalReference) -> Self {
135 self.technique_refs.push(reference);
136 self
137 }
138
139 pub fn build(self) -> Result<Behavior> {
140 let name = self.name.ok_or(MaecError::MissingField("name"))?;
141
142 let mut common = crate::common::CommonProperties::new("behavior", None);
143 if let Some(id) = self.id {
144 common.id = id;
145 }
146
147 let behavior = Behavior {
148 common,
149 name,
150 description: self.description,
151 timestamp: self.timestamp,
152 attributes: self.attributes,
153 action_refs: self.action_refs,
154 technique_refs: self.technique_refs,
155 };
156
157 behavior.validate()?;
158 Ok(behavior)
159 }
160}