1use crate::RecordReference;
4use serde::{Deserialize, Serialize};
5use std::collections::BTreeMap;
6#[cfg(feature = "openapi")]
7use utoipa::ToSchema;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10#[cfg_attr(feature = "openapi", derive(ToSchema))]
11#[serde(rename_all = "camelCase")]
12pub struct Contact {
13 #[serde(deserialize_with = "crate::shared::object_id_as_string", rename = "_id")]
14 pub id: String,
15 pub account: RecordReference,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub organisation: Option<RecordReference>,
18 pub primary: String,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
20 pub secondary: Option<String>,
21 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub tertiary: Option<String>,
23 pub name: String,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub nickname: Option<String>,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub email: Option<String>,
28 #[serde(default, skip_serializing_if = "Vec::is_empty")]
29 pub capabilities: Vec<String>,
30 #[serde(default, skip_serializing_if = "Vec::is_empty")]
31 pub groups: Vec<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub device: Option<ContactDevice>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub group: Option<String>,
36 #[serde(default, skip_serializing_if = "Option::is_none")]
37 pub opt_out: Option<bool>,
38 #[serde(rename = "_class", skip_serializing_if = "Option::is_none")]
39 pub class_name: Option<String>,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43#[serde(rename_all = "camelCase")]
44pub struct ContactRef {
45 pub primary: String,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub secondary: Option<String>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub tertiary: Option<String>,
50 pub name: String,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub nickname: Option<String>,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub email: Option<String>,
55 #[serde(default, skip_serializing_if = "Vec::is_empty")]
56 pub capabilities: Vec<String>,
57 #[serde(default, skip_serializing_if = "Vec::is_empty")]
58 pub groups: Vec<String>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub device: Option<ContactDevice>,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub group: Option<String>,
63 #[serde(default, skip_serializing_if = "Option::is_none")]
64 pub opt_out: Option<bool>,
65 #[serde(rename = "_class", skip_serializing_if = "Option::is_none")]
66 pub class_name: Option<String>,
67}
68
69impl From<Contact> for ContactRef {
70 fn from(contact: Contact) -> Self {
71 ContactRef {
72 primary: contact.primary,
73 secondary: contact.secondary,
74 tertiary: contact.tertiary,
75 name: contact.name,
76 nickname: contact.nickname,
77 email: contact.email,
78 capabilities: contact.capabilities,
79 groups: contact.groups,
80 device: contact.device,
81 group: contact.group,
82 opt_out: contact.opt_out,
83 class_name: contact.class_name,
84 }
85 }
86}
87
88impl From<&Contact> for ContactRef {
90 fn from(contact: &Contact) -> Self {
91 ContactRef {
92 primary: contact.primary.clone(),
93 secondary: contact.secondary.clone(),
94 tertiary: contact.tertiary.clone(),
95 name: contact.name.clone(),
96 nickname: contact.nickname.clone(),
97 email: contact.email.clone(),
98 capabilities: contact.capabilities.clone(),
99 groups: contact.groups.clone(),
100 device: contact.device.clone(),
101 group: contact.group.clone(),
102 opt_out: contact.opt_out,
103 class_name: contact.class_name.clone(),
104 }
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase")]
110pub struct ContactDevice {
111 #[serde(rename = "_id")]
112 pub id: String,
113 pub name: String,
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub class_name: Option<String>,
116 #[serde(skip_serializing_if = "Option::is_none")]
117 pub meta: Option<BTreeMap<String, String>>,
118}
119
120impl Contact {
121 pub fn new(
122 id: String,
123 account: RecordReference,
124 primary: String,
125 name: String,
126 ) -> Self {
127 Self {
128 id,
129 account,
130 name,
131 primary,
132 organisation: None,
133 secondary: None,
134 tertiary: None,
135 nickname: None,
136 email: None,
137 capabilities: Vec::new(),
138 groups: Vec::new(),
139 device: None,
140 group: None,
141 opt_out: None,
142 class_name: None,
143 }
144 }
145
146 pub fn with_email(mut self, email: String) -> Self {
147 self.email = Some(email);
148 self
149 }
150
151 pub fn with_nickname(mut self, nickname: String) -> Self {
152 self.nickname = Some(nickname);
153 self
154 }
155
156 pub fn with_capabilities(mut self, capabilities: Vec<String>) -> Self {
157 self.capabilities = capabilities;
158 self
159 }
160
161 pub fn with_groups(mut self, groups: Vec<String>) -> Self {
162 self.groups = groups;
163 self
164 }
165
166 pub fn with_device(mut self, device: ContactDevice) -> Self {
167 self.device = Some(device);
168 self
169 }
170
171 pub fn has_capability(&self, capability: &str) -> bool {
172 self.capabilities.iter().any(|c| c == capability)
173 }
174
175 pub fn is_in_group(&self, group: &str) -> bool {
176 self.groups.iter().any(|g| g == group)
177 }
178}
179
180impl ContactDevice {
181 pub fn new(id: String, name: String) -> Self {
182 Self {
183 id,
184 name,
185 class_name: None,
186 meta: None,
187 }
188 }
189
190 pub fn with_meta(mut self, meta: BTreeMap<String, String>) -> Self {
191 self.meta = Some(meta);
192 self
193 }
194
195 pub fn with_class_name(mut self, class_name: String) -> Self {
196 self.class_name = Some(class_name);
197 self
198 }
199}
200
201impl std::fmt::Display for Contact {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 write!(
205 f,
206 "Contact {{ id: {}, name: {}, primary: {}, organisation: {:?}, account: {:?} }}",
207 self.id, self.name, self.primary, self.organisation, self.account
208 )
209 }
210}
211
212impl std::fmt::Display for ContactDevice {
213 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 write!(f, "ContactDevice {{ id: {}, name: {} }}", self.id, self.name)
215 }
216}