autofill/db/models/
address.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2* License, v. 2.0. If a copy of the MPL was not distributed with this
3* file, You can obtain one at http://mozilla.org/MPL/2.0/.
4*/
5
6use super::Metadata;
7use rusqlite::Row;
8use sync_guid::Guid;
9
10// UpdatableAddressFields contains the fields we support for creating a new
11// address or updating an existing one. It's missing the guid, our "internal"
12// meta fields (such as the change counter) and "external" meta fields
13// (such as timeCreated) because it doesn't make sense for these things to be
14// specified as an item is created - any meta fields which can be updated
15// have special methods for doing so.
16#[derive(Debug, Clone, Default)]
17pub struct UpdatableAddressFields {
18    pub name: String,
19    pub organization: String,
20    pub street_address: String,
21    pub address_level3: String,
22    pub address_level2: String,
23    pub address_level1: String,
24    pub postal_code: String,
25    pub country: String,
26    pub tel: String,
27    pub email: String,
28}
29
30/// Metadata fields managed internally by the library: the guid, timestamps and
31/// local sync state. These are automatically set on `add_address` and updated on
32/// operations like `touch` and `update_address`. Not included in
33/// `UpdatableAddressFields`; use `add_address_with_meta` when importing records
34/// that already have metadata.
35#[derive(Debug, Clone, Default)]
36pub struct AddressMeta {
37    pub guid: String,
38    pub time_created: i64,
39    pub time_last_used: Option<i64>,
40    pub time_last_modified: i64,
41    pub times_used: i64,
42    /// Local changes not yet uploaded; 0 means it matches what was last synced.
43    pub sync_change_counter: i64,
44}
45
46/// A tombstone for a record deleted locally but not yet uploaded, supplied to
47/// `add_many_address_tombstones` when migrating from another store.
48#[derive(Debug, Clone, Default)]
49pub struct AddressTombstone {
50    pub guid: String,
51    pub time_deleted: i64,
52}
53
54/// Per-record result of `add_many_address_tombstones`.
55#[derive(Debug)]
56pub enum AddressBulkTombstoneResultEntry {
57    Success { guid: String },
58    Error { message: String },
59}
60
61/// An address together with its metadata, passed to `add_address_with_meta` and
62/// `update_address_with_meta` when importing a record from another store.
63#[derive(Debug, Clone, Default)]
64pub struct UpdatableAddressFieldsWithMeta {
65    pub fields: UpdatableAddressFields,
66    pub meta: AddressMeta,
67}
68
69/// A bulk insert result entry, returned per input record by
70/// `add_many_addresses_with_meta` so that one record failing does not abort the
71/// batch. Note that although the success case is much larger than the error
72/// case, this is negligible in real life, as we expect a very small
73/// success/error ratio.
74#[allow(clippy::large_enum_variant)]
75#[derive(Debug)]
76pub enum AddressBulkResultEntry {
77    Success { address: Address },
78    Error { message: String },
79}
80
81// "Address" is what we return to consumers and has most of the metadata.
82#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
83pub struct Address {
84    pub guid: String,
85    pub name: String,
86    pub organization: String,
87    pub street_address: String,
88    pub address_level3: String,
89    pub address_level2: String,
90    pub address_level1: String,
91    pub postal_code: String,
92    pub country: String,
93    pub tel: String,
94    pub email: String,
95    // We expose some of the metadata
96    pub time_created: i64,
97    pub time_last_used: Option<i64>,
98    pub time_last_modified: i64,
99    pub times_used: i64,
100}
101
102// This is used to "externalize" an address, suitable for handing back to
103// consumers.
104impl From<InternalAddress> for Address {
105    fn from(ia: InternalAddress) -> Self {
106        Address {
107            guid: ia.guid.to_string(),
108            name: ia.name,
109            organization: ia.organization,
110            street_address: ia.street_address,
111            address_level3: ia.address_level3,
112            address_level2: ia.address_level2,
113            address_level1: ia.address_level1,
114            postal_code: ia.postal_code,
115            country: ia.country,
116            tel: ia.tel,
117            email: ia.email,
118            // note we can't use u64 in uniffi
119            time_created: u64::from(ia.metadata.time_created) as i64,
120            time_last_used: if ia.metadata.time_last_used.0 == 0 {
121                None
122            } else {
123                Some(ia.metadata.time_last_used.0 as i64)
124            },
125            time_last_modified: u64::from(ia.metadata.time_last_modified) as i64,
126            times_used: ia.metadata.times_used,
127        }
128    }
129}
130
131// An "internal" address is used by the public APIs and by sync. No `PartialEq`
132// because it's impossible to do it meaningfully for credit-cards and we'd like
133// to keep the API symmetric
134#[derive(Default, Debug, Clone)]
135pub struct InternalAddress {
136    pub guid: Guid,
137    pub name: String,
138    pub organization: String,
139    pub street_address: String,
140    pub address_level3: String,
141    pub address_level2: String,
142    pub address_level1: String,
143    pub postal_code: String,
144    pub country: String,
145    pub tel: String,
146    pub email: String,
147    pub metadata: Metadata,
148}
149
150impl InternalAddress {
151    pub fn from_row(row: &Row<'_>) -> Result<InternalAddress, rusqlite::Error> {
152        Ok(Self {
153            guid: row.get("guid")?,
154            name: row.get("name")?,
155            organization: row.get("organization")?,
156            street_address: row.get("street_address")?,
157            address_level3: row.get("address_level3")?,
158            address_level2: row.get("address_level2")?,
159            address_level1: row.get("address_level1")?,
160            postal_code: row.get("postal_code")?,
161            country: row.get("country")?,
162            tel: row.get("tel")?,
163            email: row.get("email")?,
164            metadata: Metadata {
165                time_created: row.get("time_created")?,
166                time_last_used: row.get("time_last_used")?,
167                time_last_modified: row.get("time_last_modified")?,
168                times_used: row.get("times_used")?,
169                sync_change_counter: row.get("sync_change_counter")?,
170            },
171        })
172    }
173}