1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::common::{CommonProperties, StixObject};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub struct Location {
10 #[serde(flatten)]
11 pub common: CommonProperties,
12
13 pub name: Option<String>,
14 pub description: Option<String>,
15
16 pub latitude: Option<f64>,
17 pub longitude: Option<f64>,
18 pub precision: Option<f64>,
19
20 pub region: Option<String>,
21 pub country: Option<String>,
22 pub administrative_area: Option<String>,
23 pub city: Option<String>,
24 pub street_address: Option<String>,
25 pub postal_code: Option<String>,
26}
27
28impl Location {
29 pub fn builder() -> LocationBuilder {
30 LocationBuilder::default()
31 }
32}
33
34#[derive(Debug, Default)]
35pub struct LocationBuilder {
36 name: Option<String>,
37 description: Option<String>,
38 latitude: Option<f64>,
39 longitude: Option<f64>,
40 precision: Option<f64>,
41 region: Option<String>,
42 country: Option<String>,
43 administrative_area: Option<String>,
44 city: Option<String>,
45 street_address: Option<String>,
46 postal_code: Option<String>,
47 created_by_ref: Option<String>,
48}
49
50impl LocationBuilder {
51 pub fn name(mut self, n: impl Into<String>) -> Self {
52 self.name = Some(n.into());
53 self
54 }
55 pub fn description(mut self, d: impl Into<String>) -> Self {
56 self.description = Some(d.into());
57 self
58 }
59 pub fn latitude(mut self, lat: f64) -> Self {
60 self.latitude = Some(lat);
61 self
62 }
63 pub fn longitude(mut self, lon: f64) -> Self {
64 self.longitude = Some(lon);
65 self
66 }
67 pub fn precision(mut self, p: f64) -> Self {
68 self.precision = Some(p);
69 self
70 }
71 pub fn region(mut self, r: impl Into<String>) -> Self {
72 self.region = Some(r.into());
73 self
74 }
75 pub fn country(mut self, c: impl Into<String>) -> Self {
76 self.country = Some(c.into());
77 self
78 }
79 pub fn administrative_area(mut self, a: impl Into<String>) -> Self {
80 self.administrative_area = Some(a.into());
81 self
82 }
83 pub fn city(mut self, c: impl Into<String>) -> Self {
84 self.city = Some(c.into());
85 self
86 }
87 pub fn street_address(mut self, s: impl Into<String>) -> Self {
88 self.street_address = Some(s.into());
89 self
90 }
91 pub fn postal_code(mut self, p: impl Into<String>) -> Self {
92 self.postal_code = Some(p.into());
93 self
94 }
95 pub fn created_by_ref(mut self, r: impl Into<String>) -> Self {
96 self.created_by_ref = Some(r.into());
97 self
98 }
99
100 pub fn build(self) -> Result<Location, super::BuilderError> {
101 if self.region.is_none()
103 && self.country.is_none()
104 && !(self.latitude.is_some() && self.longitude.is_some())
105 {
106 return Err(super::BuilderError::MissingField(
107 "one of region|country|(latitude+longitude)",
108 ));
109 }
110
111 let common = CommonProperties::new("location", self.created_by_ref);
112 Ok(Location {
113 common,
114 name: self.name,
115 description: self.description,
116 latitude: self.latitude,
117 longitude: self.longitude,
118 precision: self.precision,
119 region: self.region,
120 country: self.country,
121 administrative_area: self.administrative_area,
122 city: self.city,
123 street_address: self.street_address,
124 postal_code: self.postal_code,
125 })
126 }
127}
128
129impl StixObject for Location {
130 fn id(&self) -> &str {
131 &self.common.id
132 }
133 fn type_(&self) -> &str {
134 &self.common.r#type
135 }
136 fn created(&self) -> DateTime<Utc> {
137 self.common.created
138 }
139}
140
141impl From<Location> for crate::StixObjectEnum {
142 fn from(l: Location) -> Self {
143 crate::StixObjectEnum::Location(l)
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use serde_json::Value;
151
152 #[test]
153 fn location_builder_and_serialize() {
154 let loc = Location::builder().region("europe").build().unwrap();
155 let s = serde_json::to_string(&loc).unwrap();
156 let v: Value = serde_json::from_str(&s).unwrap();
157 assert_eq!(v.get("type").and_then(Value::as_str).unwrap(), "location");
158 assert!(v.get("region").is_some());
159 }
160}