use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::common::MaecObject;
use crate::error::{MaecError, Result};
use crate::objects::types::{FieldData, Name};
use crate::Capability;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct MalwareInstance {
#[serde(flatten)]
pub common: crate::common::CommonProperties,
pub instance_object_refs: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub aliases: Vec<Name>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub labels: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub field_data: Option<FieldData>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub os_execution_envs: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub architecture_execution_envs: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub capabilities: Vec<Capability>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub os_features: Vec<String>,
}
impl MalwareInstance {
pub fn builder() -> MalwareInstanceBuilder {
MalwareInstanceBuilder::default()
}
pub fn new(instance_object_refs: Vec<String>) -> Self {
Self {
common: crate::common::CommonProperties::new("malware-instance", None),
instance_object_refs,
name: None,
aliases: vec![],
labels: vec![],
description: None,
field_data: None,
os_execution_envs: vec![],
architecture_execution_envs: vec![],
capabilities: vec![],
os_features: vec![],
}
}
pub fn validate(&self) -> Result<()> {
if self.common.r#type != "malware-instance" {
return Err(MaecError::ValidationError(format!(
"type must be 'malware-instance', got '{}'",
self.common.r#type
)));
}
if !crate::common::is_valid_maec_id(&self.common.id) {
return Err(MaecError::InvalidId(self.common.id.clone()));
}
if self.instance_object_refs.is_empty() {
return Err(MaecError::MissingField("instance_object_refs"));
}
Ok(())
}
}
impl MaecObject for MalwareInstance {
fn id(&self) -> &str {
&self.common.id
}
fn type_(&self) -> &str {
&self.common.r#type
}
fn created(&self) -> DateTime<Utc> {
self.common.created
}
}
#[derive(Debug, Default)]
pub struct MalwareInstanceBuilder {
id: Option<String>,
instance_object_refs: Vec<String>,
name: Option<Name>,
aliases: Vec<Name>,
labels: Vec<String>,
description: Option<String>,
field_data: Option<FieldData>,
os_execution_envs: Vec<String>,
architecture_execution_envs: Vec<String>,
capabilities: Vec<Capability>,
os_features: Vec<String>,
}
impl MalwareInstanceBuilder {
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn add_instance_object_ref(mut self, ref_id: impl Into<String>) -> Self {
self.instance_object_refs.push(ref_id.into());
self
}
pub fn instance_object_refs(mut self, refs: Vec<String>) -> Self {
self.instance_object_refs = refs;
self
}
pub fn name(mut self, name: impl Into<Name>) -> Self {
self.name = Some(name.into());
self
}
pub fn description(mut self, desc: impl Into<String>) -> Self {
self.description = Some(desc.into());
self
}
pub fn add_label(mut self, label: impl Into<String>) -> Self {
self.labels.push(label.into());
self
}
pub fn field_data(mut self, field_data: FieldData) -> Self {
self.field_data = Some(field_data);
self
}
pub fn add_capability(mut self, capability: Capability) -> Self {
self.capabilities.push(capability);
self
}
pub fn build(self) -> Result<MalwareInstance> {
if self.instance_object_refs.is_empty() {
return Err(MaecError::MissingField("instance_object_refs"));
}
let mut common = crate::common::CommonProperties::new("malware-instance", None);
if let Some(id) = self.id {
common.id = id;
}
let instance = MalwareInstance {
common,
instance_object_refs: self.instance_object_refs,
name: self.name,
aliases: self.aliases,
labels: self.labels,
description: self.description,
field_data: self.field_data,
os_execution_envs: self.os_execution_envs,
architecture_execution_envs: self.architecture_execution_envs,
capabilities: self.capabilities,
os_features: self.os_features,
};
instance.validate()?;
Ok(instance)
}
}