1pub mod incoming;
7pub mod name_utils;
8pub mod outgoing;
9
10use super::engine::{ConfigSyncEngine, EngineConfig, SyncEngineStorageImpl};
11use super::{
12 MergeResult, Metadata, ProcessIncomingRecordImpl, ProcessOutgoingRecordImpl, SyncRecord,
13 UnknownFields,
14};
15use crate::db::models::address::InternalAddress;
16use crate::error::*;
17use crate::sync_merge_field_check;
18use incoming::IncomingAddressesImpl;
19use name_utils::{split_name, NameParts};
20use outgoing::OutgoingAddressesImpl;
21use rusqlite::Transaction;
22use serde::{Deserialize, Serialize};
23use std::sync::Arc;
24use sync_guid::Guid;
25use types::Timestamp;
26
27pub(crate) fn create_engine(store: Arc<crate::Store>) -> ConfigSyncEngine<InternalAddress> {
29 ConfigSyncEngine::new(
30 EngineConfig {
31 namespace: "addresses".to_string(),
32 collection: "addresses".into(),
33 },
34 store,
35 Box::new(AddressesEngineStorageImpl {}),
36 )
37}
38
39pub(super) struct AddressesEngineStorageImpl {}
40
41impl SyncEngineStorageImpl<InternalAddress> for AddressesEngineStorageImpl {
42 fn get_incoming_impl(
43 &self,
44 enc_key: &Option<String>,
45 ) -> Result<Box<dyn ProcessIncomingRecordImpl<Record = InternalAddress>>> {
46 assert!(enc_key.is_none());
47 Ok(Box::new(IncomingAddressesImpl {}))
48 }
49
50 fn reset_storage(&self, tx: &Transaction<'_>) -> Result<()> {
51 tx.execute_batch(
52 "DELETE FROM addresses_mirror;
53 DELETE FROM addresses_tombstones;",
54 )?;
55 Ok(())
56 }
57
58 fn get_outgoing_impl(
59 &self,
60 enc_key: &Option<String>,
61 ) -> Result<Box<dyn ProcessOutgoingRecordImpl<Record = InternalAddress>>> {
62 assert!(enc_key.is_none());
63 Ok(Box::new(OutgoingAddressesImpl {}))
64 }
65}
66
67#[derive(Default, Deserialize, Serialize)]
70pub struct AddressPayload {
71 id: Guid,
72 entry: PayloadEntry,
75}
76
77#[derive(Default, Deserialize, Serialize)]
78#[serde(default, rename_all = "kebab-case")]
79struct PayloadEntry {
80 pub name: String,
81 pub given_name: String,
85 pub additional_name: String,
86 pub family_name: String,
87 pub organization: String,
88 pub street_address: String,
89 pub address_level3: String,
90 pub address_level2: String,
91 pub address_level1: String,
92 pub postal_code: String,
93 pub country: String,
94 pub tel: String,
95 pub email: String,
96 #[serde(rename = "timeCreated")]
98 pub time_created: Timestamp,
99 #[serde(rename = "timeLastUsed")]
100 pub time_last_used: Timestamp,
101 #[serde(rename = "timeLastModified")]
102 pub time_last_modified: Timestamp,
103 #[serde(rename = "timesUsed")]
104 pub times_used: i64,
105 pub version: u32, #[serde(flatten)]
109 unknown_fields: UnknownFields,
110}
111
112impl InternalAddress {
113 fn from_payload(p: AddressPayload) -> Result<Self> {
114 if p.entry.version != 1 {
115 return Err(Error::InvalidSyncPayload(format!(
117 "invalid version - {}",
118 p.entry.version
119 )));
120 }
121
122 Ok(InternalAddress {
123 guid: p.id,
124 name: p.entry.name,
125 organization: p.entry.organization,
126 street_address: p.entry.street_address,
127 address_level3: p.entry.address_level3,
128 address_level2: p.entry.address_level2,
129 address_level1: p.entry.address_level1,
130 postal_code: p.entry.postal_code,
131 country: p.entry.country,
132 tel: p.entry.tel,
133 email: p.entry.email,
134 metadata: Metadata {
135 time_created: p.entry.time_created,
136 time_last_used: p.entry.time_last_used,
137 time_last_modified: p.entry.time_last_modified,
138 times_used: p.entry.times_used,
139 sync_change_counter: 0,
140 },
141 })
142 }
143
144 fn into_payload(self) -> Result<AddressPayload> {
145 let NameParts {
146 given,
147 middle,
148 family,
149 } = split_name(&self.name);
150 Ok(AddressPayload {
151 id: self.guid,
152 entry: PayloadEntry {
153 name: self.name,
154 given_name: given,
155 additional_name: middle,
156 family_name: family,
157 organization: self.organization,
158 street_address: self.street_address,
159 address_level3: self.address_level3,
160 address_level2: self.address_level2,
161 address_level1: self.address_level1,
162 postal_code: self.postal_code,
163 country: self.country,
164 tel: self.tel,
165 email: self.email,
166 time_created: self.metadata.time_created,
167 time_last_used: self.metadata.time_last_used,
168 time_last_modified: self.metadata.time_last_modified,
169 times_used: self.metadata.times_used,
170 unknown_fields: Default::default(),
171 version: 1,
172 },
173 })
174 }
175}
176
177impl SyncRecord for InternalAddress {
178 fn record_name() -> &'static str {
179 "Address"
180 }
181
182 fn id(&self) -> &Guid {
183 &self.guid
184 }
185
186 fn metadata(&self) -> &Metadata {
187 &self.metadata
188 }
189
190 fn metadata_mut(&mut self) -> &mut Metadata {
191 &mut self.metadata
192 }
193
194 #[allow(clippy::cognitive_complexity)] fn merge(incoming: &Self, local: &Self, mirror: &Option<Self>) -> MergeResult<Self> {
202 let mut merged_record: Self = Default::default();
203 assert_eq!(incoming.guid, local.guid);
205
206 if let Some(m) = mirror {
207 assert_eq!(incoming.guid, m.guid)
208 };
209
210 merged_record.guid = incoming.guid.clone();
211
212 sync_merge_field_check!(name, incoming, local, mirror, merged_record);
213 sync_merge_field_check!(organization, incoming, local, mirror, merged_record);
214 sync_merge_field_check!(street_address, incoming, local, mirror, merged_record);
215 sync_merge_field_check!(address_level3, incoming, local, mirror, merged_record);
216 sync_merge_field_check!(address_level2, incoming, local, mirror, merged_record);
217 sync_merge_field_check!(address_level1, incoming, local, mirror, merged_record);
218 sync_merge_field_check!(postal_code, incoming, local, mirror, merged_record);
219 sync_merge_field_check!(country, incoming, local, mirror, merged_record);
220 sync_merge_field_check!(tel, incoming, local, mirror, merged_record);
221 sync_merge_field_check!(email, incoming, local, mirror, merged_record);
222
223 merged_record.metadata = incoming.metadata;
224 merged_record
225 .metadata
226 .merge(&local.metadata, mirror.as_ref().map(|m| m.metadata()));
227
228 MergeResult::Merged {
229 merged: merged_record,
230 }
231 }
232}
233
234fn get_forked_record(local_record: InternalAddress) -> InternalAddress {
237 let mut local_record_data = local_record;
238 local_record_data.guid = Guid::random();
239 local_record_data.metadata.time_created = Timestamp::now();
240 local_record_data.metadata.time_last_used = Timestamp::now();
241 local_record_data.metadata.time_last_modified = Timestamp::now();
242 local_record_data.metadata.times_used = 0;
243 local_record_data.metadata.sync_change_counter = 1;
244
245 local_record_data
246}