Refactored autofill component as prep for ffi layer

This commit is contained in:
lougeniac64 2020-11-09 19:02:44 -05:00
Родитель dc1430537c
Коммит 418b38a95e
15 изменённых файлов: 647 добавлений и 390 удалений

Просмотреть файл

@ -38,3 +38,10 @@
- We no longer discard the final path component from self-hosted sync tokenserver URLs.
([#3694](https://github.com/mozilla/application-services/pull/3694))
## Autofill
### What's Changed
- We added the `touch_address` and `touch_credit_card` store functions and refactored the component.([#3691](https://github.com/mozilla/application-services/pull/3691))

Просмотреть файл

@ -1,6 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
//! Work around the fact that `sqlcipher` might get enabled by a cargo feature
//! another crate in the workspace needs, without setting up nss. (This is a

Просмотреть файл

@ -1,6 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub mod addresses;
pub mod credit_cards;

Просмотреть файл

@ -1,118 +1,25 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
use crate::db::models::address::{Address, InternalAddress, NewAddressFields};
use crate::db::schema::ADDRESS_COMMON_COLS;
use crate::error::*;
use crate::schema::ADDRESS_COMMON_COLS;
use rusqlite::{Connection, Row, NO_PARAMS};
use serde::Serialize;
use serde_derive::*;
use rusqlite::{Connection, NO_PARAMS};
use sync_guid::Guid;
use types::Timestamp;
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct NewAddressFields {
pub given_name: String,
#[serde(default)]
pub additional_name: String,
pub family_name: String,
#[serde(default)]
pub organization: String,
pub street_address: String,
#[serde(default)]
pub address_level3: String,
#[serde(default)]
pub address_level2: String,
#[serde(default)]
pub address_level1: String,
#[serde(default)]
pub postal_code: String,
#[serde(default)]
pub country: String,
#[serde(default)]
pub tel: String,
#[serde(default)]
pub email: String,
}
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
pub struct Address {
pub guid: Guid,
pub fields: NewAddressFields,
#[serde(default)]
#[serde(rename = "timeCreated")]
pub time_created: Timestamp,
#[serde(default)]
#[serde(rename = "timeLastUsed")]
pub time_last_used: Timestamp,
#[serde(default)]
#[serde(rename = "timeLastModified")]
pub time_last_modified: Timestamp,
#[serde(default)]
#[serde(rename = "timesUsed")]
pub times_used: i64,
#[serde(default)]
#[serde(rename = "changeCounter")]
pub(crate) sync_change_counter: i64,
}
impl Address {
pub fn from_row(row: &Row<'_>) -> Result<Address, rusqlite::Error> {
let address_fields = NewAddressFields {
given_name: row.get("given_name")?,
additional_name: row.get("additional_name")?,
family_name: row.get("family_name")?,
organization: row.get("organization")?,
street_address: row.get("street_address")?,
address_level3: row.get("address_level3")?,
address_level2: row.get("address_level2")?,
address_level1: row.get("address_level1")?,
postal_code: row.get("postal_code")?,
country: row.get("country")?,
tel: row.get("tel")?,
email: row.get("email")?,
};
Ok(Address {
guid: Guid::from_string(row.get("guid")?),
fields: address_fields,
time_created: row.get("time_created")?,
time_last_used: row.get("time_last_used")?,
time_last_modified: row.get("time_last_modified")?,
times_used: row.get("times_used")?,
sync_change_counter: row.get("sync_change_counter")?,
})
}
}
#[allow(dead_code)]
pub fn add_address(conn: &mut Connection, new_address: NewAddressFields) -> Result<Address> {
let tx = conn.transaction()?;
pub fn add_address(conn: &Connection, new_address: NewAddressFields) -> Result<InternalAddress> {
let tx = conn.unchecked_transaction()?;
let address = Address {
let address = InternalAddress {
guid: Guid::random(),
fields: new_address,
time_created: Timestamp::now(),
time_last_used: Timestamp { 0: 0 },
time_last_used: Some(Timestamp::now()),
time_last_modified: Timestamp::now(),
times_used: 0,
sync_change_counter: 1,
@ -171,8 +78,8 @@ pub fn add_address(conn: &mut Connection, new_address: NewAddressFields) -> Resu
}
#[allow(dead_code)]
pub fn get_address(conn: &mut Connection, guid: &Guid) -> Result<Address> {
let tx = conn.transaction()?;
pub fn get_address(conn: &Connection, guid: String) -> Result<InternalAddress> {
let tx = conn.unchecked_transaction()?;
let sql = format!(
"SELECT
{common_cols}
@ -181,15 +88,17 @@ pub fn get_address(conn: &mut Connection, guid: &Guid) -> Result<Address> {
common_cols = ADDRESS_COMMON_COLS
);
let address = tx.query_row(&sql, &[guid.as_str()], |row| Ok(Address::from_row(row)?))?;
let address = tx.query_row(&sql, &[guid.as_str()], |row| {
Ok(InternalAddress::from_row(row)?)
})?;
tx.commit()?;
Ok(address)
}
#[allow(dead_code)]
pub fn get_all_addresses(conn: &mut Connection) -> Result<Vec<Address>> {
let tx = conn.transaction()?;
pub fn get_all_addresses(conn: &Connection) -> Result<Vec<InternalAddress>> {
let tx = conn.unchecked_transaction()?;
let mut addresses = Vec::new();
let sql = format!(
"SELECT
@ -200,7 +109,8 @@ pub fn get_all_addresses(conn: &mut Connection) -> Result<Vec<Address>> {
{
let mut stmt = tx.prepare(&sql)?;
let addresses_iter = stmt.query_map(NO_PARAMS, |row| Ok(Address::from_row(row)?))?;
let addresses_iter =
stmt.query_map(NO_PARAMS, |row| Ok(InternalAddress::from_row(row)?))?;
for address_result in addresses_iter {
addresses.push(address_result.expect("Should unwrap address"));
@ -212,8 +122,8 @@ pub fn get_all_addresses(conn: &mut Connection) -> Result<Vec<Address>> {
}
#[allow(dead_code)]
pub fn update_address(conn: &mut Connection, address: Address) -> Result<()> {
let tx = conn.transaction()?;
pub fn update_address(conn: &Connection, address: &Address) -> Result<()> {
let tx = conn.unchecked_transaction()?;
tx.execute_named(
"UPDATE addresses_data
SET given_name = :given_name,
@ -231,18 +141,18 @@ pub fn update_address(conn: &mut Connection, address: Address) -> Result<()> {
sync_change_counter = sync_change_counter + 1
WHERE guid = :guid",
rusqlite::named_params! {
":given_name": address.fields.given_name,
":additional_name": address.fields.additional_name,
":family_name": address.fields.family_name,
":organization": address.fields.organization,
":street_address": address.fields.street_address,
":address_level3": address.fields.address_level3,
":address_level2": address.fields.address_level2,
":address_level1": address.fields.address_level1,
":postal_code": address.fields.postal_code,
":country": address.fields.country,
":tel": address.fields.tel,
":email": address.fields.email,
":given_name": address.given_name,
":additional_name": address.additional_name,
":family_name": address.family_name,
":organization": address.organization,
":street_address": address.street_address,
":address_level3": address.address_level3,
":address_level2": address.address_level2,
":address_level1": address.address_level1,
":postal_code": address.postal_code,
":country": address.country,
":tel": address.tel,
":email": address.email,
":guid": address.guid,
},
)?;
@ -251,8 +161,8 @@ pub fn update_address(conn: &mut Connection, address: Address) -> Result<()> {
Ok(())
}
pub fn delete_address(conn: &mut Connection, guid: &Guid) -> Result<bool> {
let tx = conn.transaction()?;
pub fn delete_address(conn: &Connection, guid: String) -> Result<bool> {
let tx = conn.unchecked_transaction()?;
// check that guid exists
let exists = tx.query_row(
@ -298,6 +208,26 @@ pub fn delete_address(conn: &mut Connection, guid: &Guid) -> Result<bool> {
Ok(exists)
}
pub fn touch(conn: &Connection, guid: String) -> Result<()> {
let tx = conn.unchecked_transaction()?;
let now_ms = Timestamp::now();
tx.execute_named(
"UPDATE addresses_data
SET time_last_used = :time_last_used,
times_used = times_used + 1,
sync_change_counter = sync_change_counter + 1
WHERE guid = :guid",
rusqlite::named_params! {
":time_last_used": now_ms,
":guid": guid.as_str(),
},
)?;
tx.commit()?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
@ -305,10 +235,10 @@ mod tests {
#[test]
fn test_address_create_and_read() {
let mut db = new_mem_db();
let db = new_mem_db();
let saved_address = add_address(
&mut db,
&db,
NewAddressFields {
given_name: "jane".to_string(),
family_name: "doe".to_string(),
@ -328,7 +258,7 @@ mod tests {
assert_eq!(1, saved_address.sync_change_counter);
// get created address
let retrieved_address = get_address(&mut db, &saved_address.guid)
let retrieved_address = get_address(&db, saved_address.guid.to_string())
.expect("should contain optional retrieved address");
assert_eq!(saved_address.guid, retrieved_address.guid);
assert_eq!(
@ -353,19 +283,19 @@ mod tests {
);
// converting the created record into a tombstone to check that it's not returned on a second `get_address` call
let delete_result = delete_address(&mut db, &saved_address.guid);
let delete_result = delete_address(&db, saved_address.guid.to_string());
assert!(delete_result.is_ok());
assert!(delete_result.unwrap());
assert!(get_address(&mut db, &saved_address.guid).is_err());
assert!(get_address(&db, saved_address.guid.to_string()).is_err());
}
#[test]
fn test_address_read_all() {
let mut db = new_mem_db();
let db = new_mem_db();
let saved_address = add_address(
&mut db,
&db,
NewAddressFields {
given_name: "jane".to_string(),
family_name: "doe".to_string(),
@ -379,7 +309,7 @@ mod tests {
.expect("should contain saved address");
let saved_address2 = add_address(
&mut db,
&db,
NewAddressFields {
given_name: "john".to_string(),
family_name: "deer".to_string(),
@ -394,7 +324,7 @@ mod tests {
// creating a third address with a tombstone to ensure it's not retunred
let saved_address3 = add_address(
&mut db,
&db,
NewAddressFields {
given_name: "abraham".to_string(),
family_name: "lincoln".to_string(),
@ -407,12 +337,12 @@ mod tests {
)
.expect("should contain saved address");
let delete_result = delete_address(&mut db, &saved_address3.guid);
let delete_result = delete_address(&db, saved_address3.guid.to_string());
assert!(delete_result.is_ok());
assert!(delete_result.unwrap());
let retrieved_addresses =
get_all_addresses(&mut db).expect("Should contain all saved addresses");
get_all_addresses(&db).expect("Should contain all saved addresses");
assert!(!retrieved_addresses.is_empty());
let expected_number_of_addresses = 2;
@ -428,10 +358,10 @@ mod tests {
#[test]
fn test_address_update() {
let mut db = new_mem_db();
let db = new_mem_db();
let saved_address = add_address(
&mut db,
&db,
NewAddressFields {
given_name: "john".to_string(),
family_name: "doe".to_string(),
@ -446,25 +376,26 @@ mod tests {
let expected_additional_name = "paul".to_string();
let update_result = update_address(
&mut db,
Address {
guid: saved_address.guid.clone(),
fields: NewAddressFields {
given_name: "john".to_string(),
additional_name: expected_additional_name.clone(),
family_name: "deer".to_string(),
street_address: "123 First Avenue".to_string(),
country: "United States".to_string(),
..NewAddressFields::default()
},
..Address::default()
&db,
&Address {
guid: saved_address.guid.to_string(),
given_name: "john".to_string(),
additional_name: expected_additional_name.clone(),
family_name: "deer".to_string(),
organization: "".to_string(),
street_address: "123 First Avenue".to_string(),
address_level3: "".to_string(),
address_level2: "Denver, CO".to_string(),
address_level1: "".to_string(),
postal_code: "".to_string(),
country: "United States".to_string(),
tel: "".to_string(),
email: "".to_string(),
},
);
assert!(update_result.is_ok());
let updated_address = get_address(&mut db, &saved_address.guid)
let updated_address = get_address(&db, saved_address.guid.to_string())
.expect("should contain optional updated address");
assert_eq!(saved_address.guid, updated_address.guid);
@ -479,10 +410,10 @@ mod tests {
#[test]
fn test_address_delete() {
let mut db = new_mem_db();
let db = new_mem_db();
let saved_address = add_address(
&mut db,
&db,
NewAddressFields {
given_name: "jane".to_string(),
family_name: "doe".to_string(),
@ -495,7 +426,7 @@ mod tests {
)
.expect("should contain saved address");
let delete_result = delete_address(&mut db, &saved_address.guid);
let delete_result = delete_address(&db, saved_address.guid.to_string());
assert!(delete_result.is_ok());
assert!(delete_result.unwrap());
}
@ -522,7 +453,7 @@ mod tests {
assert!(tombstone_result.is_ok());
// create a new address with the tombstone's guid
let address = Address {
let address = InternalAddress {
guid,
fields: NewAddressFields {
given_name: "jane".to_string(),
@ -534,7 +465,7 @@ mod tests {
..NewAddressFields::default()
},
..Address::default()
..InternalAddress::default()
};
let add_address_result = db.execute_named(
@ -599,7 +530,7 @@ mod tests {
let guid = Guid::random();
// create an address
let address = Address {
let address = InternalAddress {
guid,
fields: NewAddressFields {
given_name: "jane".to_string(),
@ -611,7 +542,7 @@ mod tests {
..NewAddressFields::default()
},
..Address::default()
..InternalAddress::default()
};
let add_address_result = db.execute_named(
@ -685,4 +616,33 @@ mod tests {
tombstone_result.unwrap_err().to_string()
);
}
#[test]
fn test_address_touch() -> Result<()> {
let db = new_mem_db();
let saved_address = add_address(
&db,
NewAddressFields {
given_name: "jane".to_string(),
family_name: "doe".to_string(),
street_address: "123 Second Avenue".to_string(),
address_level2: "Chicago, IL".to_string(),
country: "United States".to_string(),
..NewAddressFields::default()
},
)?;
assert_eq!(saved_address.sync_change_counter, 1);
assert_eq!(saved_address.times_used, 0);
touch(&db, saved_address.guid.to_string())?;
let touched_address = get_address(&db, saved_address.guid.to_string())?;
assert_eq!(touched_address.sync_change_counter, 2);
assert_eq!(touched_address.times_used, 1);
Ok(())
}
}

Просмотреть файл

@ -1,89 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
use crate::db::models::credit_card::{CreditCard, InternalCreditCard, NewCreditCardFields};
use crate::db::schema::CREDIT_CARD_COMMON_COLS;
use crate::error::*;
use crate::schema::CREDIT_CARD_COMMON_COLS;
use rusqlite::{Connection, Row, NO_PARAMS};
use serde::Serialize;
use serde_derive::*;
use rusqlite::{Connection, NO_PARAMS};
use sync_guid::Guid;
use types::Timestamp;
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct NewCreditCardFields {
pub cc_name: String,
pub cc_number: String,
pub cc_exp_month: i64,
pub cc_exp_year: i64,
// Credit card types are a fixed set of strings as defined in the link below
// (https://searchfox.org/mozilla-central/rev/7ef5cefd0468b8f509efe38e0212de2398f4c8b3/toolkit/modules/CreditCard.jsm#9-22)
pub cc_type: String,
}
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
pub struct CreditCard {
pub guid: Guid,
pub fields: NewCreditCardFields,
#[serde(default)]
#[serde(rename = "timeCreated")]
pub time_created: Timestamp,
#[serde(default)]
#[serde(rename = "timeLastUsed")]
pub time_last_used: Option<Timestamp>,
#[serde(default)]
#[serde(rename = "timeLastModified")]
pub time_last_modified: Timestamp,
#[serde(default)]
#[serde(rename = "timesUsed")]
pub times_used: i64,
#[serde(default)]
#[serde(rename = "changeCounter")]
pub(crate) sync_change_counter: i64,
}
impl CreditCard {
pub fn from_row(row: &Row<'_>) -> Result<CreditCard, rusqlite::Error> {
let credit_card_fields = NewCreditCardFields {
cc_name: row.get("cc_name")?,
cc_number: row.get("cc_number")?,
cc_exp_month: row.get("cc_exp_month")?,
cc_exp_year: row.get("cc_exp_year")?,
cc_type: row.get("cc_type")?,
};
Ok(CreditCard {
guid: Guid::from_string(row.get("guid")?),
fields: credit_card_fields,
time_created: row.get("time_created")?,
time_last_used: row.get("time_last_used")?,
time_last_modified: row.get("time_last_modified")?,
times_used: row.get("times_used")?,
sync_change_counter: row.get("sync_change_counter")?,
})
}
}
#[allow(dead_code)]
pub fn add_credit_card(
conn: &mut Connection,
conn: &Connection,
new_credit_card_fields: NewCreditCardFields,
) -> Result<CreditCard> {
let tx = conn.transaction()?;
) -> Result<InternalCreditCard> {
let tx = conn.unchecked_transaction()?;
let credit_card = CreditCard {
let credit_card = InternalCreditCard {
guid: Guid::random(),
fields: new_credit_card_fields,
time_created: Timestamp::now(),
@ -131,9 +65,8 @@ pub fn add_credit_card(
Ok(credit_card)
}
#[allow(dead_code)]
pub fn get_credit_card(conn: &mut Connection, guid: &Guid) -> Result<CreditCard> {
let tx = conn.transaction()?;
pub fn get_credit_card(conn: &Connection, guid: String) -> Result<InternalCreditCard> {
let tx = conn.unchecked_transaction()?;
let sql = format!(
"SELECT
{common_cols}
@ -142,15 +75,14 @@ pub fn get_credit_card(conn: &mut Connection, guid: &Guid) -> Result<CreditCard>
common_cols = CREDIT_CARD_COMMON_COLS
);
let credit_card = tx.query_row(&sql, &[guid.as_str()], CreditCard::from_row)?;
let credit_card = tx.query_row(&sql, &[guid.as_str()], InternalCreditCard::from_row)?;
tx.commit()?;
Ok(credit_card)
}
#[allow(dead_code)]
pub fn get_all_credit_cards(conn: &mut Connection) -> Result<Vec<CreditCard>> {
let tx = conn.transaction()?;
pub fn get_all_credit_cards(conn: &Connection) -> Result<Vec<InternalCreditCard>> {
let tx = conn.unchecked_transaction()?;
let credit_cards;
let sql = format!(
"SELECT
@ -162,17 +94,16 @@ pub fn get_all_credit_cards(conn: &mut Connection) -> Result<Vec<CreditCard>> {
{
let mut stmt = tx.prepare(&sql)?;
credit_cards = stmt
.query_map(NO_PARAMS, CreditCard::from_row)?
.collect::<Result<Vec<CreditCard>, _>>()?;
.query_map(NO_PARAMS, InternalCreditCard::from_row)?
.collect::<Result<Vec<InternalCreditCard>, _>>()?;
}
tx.commit()?;
Ok(credit_cards)
}
#[allow(dead_code)]
pub fn update_credit_card(conn: &mut Connection, credit_card: CreditCard) -> Result<()> {
let tx = conn.transaction()?;
pub fn update_credit_card(conn: &Connection, credit_card: &CreditCard) -> Result<()> {
let tx = conn.unchecked_transaction()?;
tx.execute_named(
"UPDATE credit_cards_data
SET cc_name = :cc_name,
@ -180,23 +111,17 @@ pub fn update_credit_card(conn: &mut Connection, credit_card: CreditCard) -> Res
cc_exp_month = :cc_exp_month,
cc_exp_year = :cc_exp_year,
cc_type = :cc_type,
time_created = :time_created,
time_last_used = :time_last_used,
time_last_modified = :time_last_modified,
times_used = :times_used,
sync_change_counter = sync_change_counter + 1
WHERE guid = :guid",
rusqlite::named_params! {
":cc_name": credit_card.fields.cc_name,
":cc_number": credit_card.fields.cc_number,
":cc_exp_month": credit_card.fields.cc_exp_month,
":cc_exp_year": credit_card.fields.cc_exp_year,
":cc_type": credit_card.fields.cc_type,
":time_created": credit_card.time_created,
":time_last_used": credit_card.time_last_used,
":time_last_modified": credit_card.time_last_modified,
":times_used": credit_card.times_used,
":guid": credit_card.guid,
":cc_name": credit_card.cc_name,
":cc_number": credit_card.cc_number,
":cc_exp_month": credit_card.cc_exp_month,
":cc_exp_year": credit_card.cc_exp_year,
":cc_type": credit_card.cc_type,
":time_last_modified": Timestamp::now(),
":guid": credit_card.guid.as_str(),
},
)?;
@ -204,9 +129,8 @@ pub fn update_credit_card(conn: &mut Connection, credit_card: CreditCard) -> Res
Ok(())
}
#[allow(dead_code)]
pub fn delete_credit_card(conn: &mut Connection, guid: &Guid) -> Result<bool> {
let tx = conn.transaction()?;
pub fn delete_credit_card(conn: &Connection, guid: String) -> Result<bool> {
let tx = conn.unchecked_transaction()?;
// check that guid exists
let exists = tx.query_row(
@ -265,6 +189,26 @@ pub fn delete_credit_card(conn: &mut Connection, guid: &Guid) -> Result<bool> {
Ok(exists)
}
pub fn touch(conn: &Connection, guid: String) -> Result<()> {
let tx = conn.unchecked_transaction()?;
let now_ms = Timestamp::now();
tx.execute_named(
"UPDATE credit_cards_data
SET time_last_used = :time_last_used,
times_used = times_used + 1,
sync_change_counter = sync_change_counter + 1
WHERE guid = :guid",
rusqlite::named_params! {
":time_last_used": now_ms,
":guid": guid.as_str(),
},
)?;
tx.commit()?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
@ -272,10 +216,10 @@ mod tests {
#[test]
fn test_credit_card_create_and_read() -> Result<()> {
let mut db = new_mem_db();
let db = new_mem_db();
let saved_credit_card = add_credit_card(
&mut db,
&db,
NewCreditCardFields {
cc_name: "jane doe".to_string(),
cc_number: "2222333344445555".to_string(),
@ -292,7 +236,7 @@ mod tests {
assert_eq!(1, saved_credit_card.sync_change_counter);
// get created credit card
let retrieved_credit_card = get_credit_card(&mut db, &saved_credit_card.guid)?;
let retrieved_credit_card = get_credit_card(&db, saved_credit_card.guid.to_string())?;
assert_eq!(saved_credit_card.guid, retrieved_credit_card.guid);
assert_eq!(
@ -317,21 +261,21 @@ mod tests {
);
// converting the created record into a tombstone to check that it's not returned on a second `get_credit_card` call
let delete_result = delete_credit_card(&mut db, &saved_credit_card.guid);
let delete_result = delete_credit_card(&db, saved_credit_card.guid.to_string());
assert!(delete_result.is_ok());
assert!(delete_result?);
assert!(get_credit_card(&mut db, &saved_credit_card.guid).is_err());
assert!(get_credit_card(&db, saved_credit_card.guid.to_string()).is_err());
Ok(())
}
#[test]
fn test_credit_card_read_all() -> Result<()> {
let mut db = new_mem_db();
let db = new_mem_db();
let saved_credit_card = add_credit_card(
&mut db,
&db,
NewCreditCardFields {
cc_name: "jane doe".to_string(),
cc_number: "2222333344445555".to_string(),
@ -342,7 +286,7 @@ mod tests {
)?;
let saved_credit_card2 = add_credit_card(
&mut db,
&db,
NewCreditCardFields {
cc_name: "john deer".to_string(),
cc_number: "1111222233334444".to_string(),
@ -354,7 +298,7 @@ mod tests {
// creating a third credit card with a tombstone to ensure it's not retunred
let saved_credit_card3 = add_credit_card(
&mut db,
&db,
NewCreditCardFields {
cc_name: "abraham lincoln".to_string(),
cc_number: "1111222233334444".to_string(),
@ -364,11 +308,11 @@ mod tests {
},
)?;
let delete_result = delete_credit_card(&mut db, &saved_credit_card3.guid);
let delete_result = delete_credit_card(&db, saved_credit_card3.guid.to_string());
assert!(delete_result.is_ok());
assert!(delete_result?);
let retrieved_credit_cards = get_all_credit_cards(&mut db)?;
let retrieved_credit_cards = get_all_credit_cards(&db)?;
assert!(!retrieved_credit_cards.is_empty());
let expected_number_of_credit_cards = 2;
@ -389,10 +333,10 @@ mod tests {
#[test]
fn test_credit_card_update() -> Result<()> {
let mut db = new_mem_db();
let db = new_mem_db();
let saved_credit_card = add_credit_card(
&mut db,
&db,
NewCreditCardFields {
cc_name: "john deer".to_string(),
cc_number: "1111222233334444".to_string(),
@ -404,23 +348,19 @@ mod tests {
let expected_cc_name = "john doe".to_string();
let update_result = update_credit_card(
&mut db,
CreditCard {
guid: saved_credit_card.guid.clone(),
fields: NewCreditCardFields {
cc_name: expected_cc_name.clone(),
cc_number: "1111222233334444".to_string(),
cc_exp_month: 10,
cc_exp_year: 2025,
cc_type: "mastercard".to_string(),
},
..CreditCard::default()
&db,
&CreditCard {
guid: saved_credit_card.guid.to_string(),
cc_name: expected_cc_name.clone(),
cc_number: "1111222233334444".to_string(),
cc_type: "mastercard".to_string(),
cc_exp_month: 10,
cc_exp_year: 2025,
},
);
assert!(update_result.is_ok());
let updated_credit_card = get_credit_card(&mut db, &saved_credit_card.guid)?;
let updated_credit_card = get_credit_card(&db, saved_credit_card.guid.to_string())?;
assert_eq!(saved_credit_card.guid, updated_credit_card.guid);
assert_eq!(expected_cc_name, updated_credit_card.fields.cc_name);
@ -433,10 +373,10 @@ mod tests {
#[test]
fn test_credit_card_delete() -> Result<()> {
let mut db = new_mem_db();
let db = new_mem_db();
let saved_credit_card = add_credit_card(
&mut db,
&db,
NewCreditCardFields {
cc_name: "john deer".to_string(),
cc_number: "1111222233334444".to_string(),
@ -446,12 +386,12 @@ mod tests {
},
)?;
let delete_result = delete_credit_card(&mut db, &saved_credit_card.guid);
let delete_result = delete_credit_card(&db, saved_credit_card.guid.to_string());
assert!(delete_result.is_ok());
assert!(delete_result?);
let saved_credit_card2 = add_credit_card(
&mut db,
&db,
NewCreditCardFields {
cc_name: "john doe".to_string(),
cc_number: "5555666677778888".to_string(),
@ -500,7 +440,7 @@ mod tests {
},
)?;
let delete_result2 = delete_credit_card(&mut db, &saved_credit_card2.guid);
let delete_result2 = delete_credit_card(&db, saved_credit_card2.guid.to_string());
assert!(delete_result2.is_ok());
assert!(delete_result2?);
@ -550,7 +490,7 @@ mod tests {
assert!(tombstone_result.is_ok());
// create a new credit card with the tombstone's guid
let credit_card = CreditCard {
let credit_card = InternalCreditCard {
guid,
fields: NewCreditCardFields {
cc_name: "john deer".to_string(),
@ -560,7 +500,7 @@ mod tests {
cc_type: "mastercard".to_string(),
},
..CreditCard::default()
..InternalCreditCard::default()
};
let add_credit_card_result = db.execute_named(
@ -611,7 +551,7 @@ mod tests {
let guid = Guid::random();
// create an credit card
let credit_card = CreditCard {
let credit_card = InternalCreditCard {
guid,
fields: NewCreditCardFields {
cc_name: "jane doe".to_string(),
@ -621,7 +561,7 @@ mod tests {
cc_type: "visa".to_string(),
},
..CreditCard::default()
..InternalCreditCard::default()
};
let add_credit_card_result = db.execute_named(
@ -681,4 +621,31 @@ mod tests {
tombstone_result.unwrap_err().to_string()
);
}
#[test]
fn test_credit_card_touch() -> Result<()> {
let db = new_mem_db();
let saved_credit_card = add_credit_card(
&db,
NewCreditCardFields {
cc_name: "john doe".to_string(),
cc_number: "5555666677778888".to_string(),
cc_exp_month: 5,
cc_exp_year: 2024,
cc_type: "visa".to_string(),
},
)?;
assert_eq!(saved_credit_card.sync_change_counter, 1);
assert_eq!(saved_credit_card.times_used, 0);
touch(&db, saved_credit_card.guid.to_string())?;
let touched_credit_card = get_credit_card(&db, saved_credit_card.guid.to_string())?;
assert_eq!(touched_credit_card.sync_change_counter, 2);
assert_eq!(touched_credit_card.times_used, 1);
Ok(())
}
}

Просмотреть файл

@ -2,8 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub mod addresses;
pub mod credit_cards;
pub mod models;
pub mod schema;
pub mod store;
use crate::error::*;
use crate::schema;
use rusqlite::{Connection, OpenFlags};
use std::{

Просмотреть файл

@ -0,0 +1,145 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
use rusqlite::Row;
use serde::Serialize;
use serde_derive::*;
use sync_guid::Guid;
use types::Timestamp;
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct NewAddressFields {
pub given_name: String,
#[serde(default)]
pub additional_name: String,
pub family_name: String,
#[serde(default)]
pub organization: String,
pub street_address: String,
#[serde(default)]
pub address_level3: String,
#[serde(default)]
pub address_level2: String,
#[serde(default)]
pub address_level1: String,
#[serde(default)]
pub postal_code: String,
#[serde(default)]
pub country: String,
#[serde(default)]
pub tel: String,
#[serde(default)]
pub email: String,
}
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
pub struct InternalAddress {
pub guid: Guid,
pub fields: NewAddressFields,
#[serde(default)]
#[serde(rename = "timeCreated")]
pub time_created: Timestamp,
#[serde(default)]
#[serde(rename = "timeLastUsed")]
pub time_last_used: Option<Timestamp>,
#[serde(default)]
#[serde(rename = "timeLastModified")]
pub time_last_modified: Timestamp,
#[serde(default)]
#[serde(rename = "timesUsed")]
pub times_used: i64,
#[serde(default)]
#[serde(rename = "changeCounter")]
pub(crate) sync_change_counter: i64,
}
impl InternalAddress {
pub fn from_row(row: &Row<'_>) -> Result<InternalAddress, rusqlite::Error> {
let address_fields = NewAddressFields {
given_name: row.get("given_name")?,
additional_name: row.get("additional_name")?,
family_name: row.get("family_name")?,
organization: row.get("organization")?,
street_address: row.get("street_address")?,
address_level3: row.get("address_level3")?,
address_level2: row.get("address_level2")?,
address_level1: row.get("address_level1")?,
postal_code: row.get("postal_code")?,
country: row.get("country")?,
tel: row.get("tel")?,
email: row.get("email")?,
};
Ok(InternalAddress {
guid: Guid::from_string(row.get("guid")?),
fields: address_fields,
time_created: row.get("time_created")?,
time_last_used: row.get("time_last_used")?,
time_last_modified: row.get("time_last_modified")?,
times_used: row.get("times_used")?,
sync_change_counter: row.get("sync_change_counter")?,
})
}
}
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct Address {
pub guid: String,
pub given_name: String,
pub additional_name: String,
pub family_name: String,
pub organization: String,
pub street_address: String,
pub address_level3: String,
pub address_level2: String,
pub address_level1: String,
pub postal_code: String,
pub country: String,
pub tel: String,
pub email: String,
}
pub trait ExternalizeAddress {
fn to_external(&self) -> Address;
}
impl ExternalizeAddress for InternalAddress {
fn to_external(&self) -> Address {
Address {
guid: self.guid.to_string(),
given_name: self.fields.given_name.to_string(),
additional_name: self.fields.additional_name.to_string(),
family_name: self.fields.family_name.to_string(),
organization: self.fields.organization.to_string(),
street_address: self.fields.street_address.to_string(),
address_level3: self.fields.address_level3.to_string(),
address_level2: self.fields.address_level2.to_string(),
address_level1: self.fields.address_level1.to_string(),
postal_code: self.fields.postal_code.to_string(),
country: self.fields.country.to_string(),
tel: self.fields.tel.to_string(),
email: self.fields.email.to_string(),
}
}
}

Просмотреть файл

@ -0,0 +1,106 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
use rusqlite::Row;
use serde::Serialize;
use serde_derive::*;
use sync_guid::Guid;
use types::Timestamp;
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct NewCreditCardFields {
pub cc_name: String,
pub cc_number: String,
pub cc_exp_month: i64,
pub cc_exp_year: i64,
// Credit card types are a fixed set of strings as defined in the link below
// (https://searchfox.org/mozilla-central/rev/7ef5cefd0468b8f509efe38e0212de2398f4c8b3/toolkit/modules/CreditCard.jsm#9-22)
pub cc_type: String,
}
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
pub struct InternalCreditCard {
pub guid: Guid,
pub fields: NewCreditCardFields,
#[serde(default)]
#[serde(rename = "timeCreated")]
pub time_created: Timestamp,
#[serde(default)]
#[serde(rename = "timeLastUsed")]
pub time_last_used: Option<Timestamp>,
#[serde(default)]
#[serde(rename = "timeLastModified")]
pub time_last_modified: Timestamp,
#[serde(default)]
#[serde(rename = "timesUsed")]
pub times_used: i64,
#[serde(default)]
#[serde(rename = "changeCounter")]
pub(crate) sync_change_counter: i64,
}
impl InternalCreditCard {
pub fn from_row(row: &Row<'_>) -> Result<InternalCreditCard, rusqlite::Error> {
let credit_card_fields = NewCreditCardFields {
cc_name: row.get("cc_name")?,
cc_number: row.get("cc_number")?,
cc_exp_month: row.get("cc_exp_month")?,
cc_exp_year: row.get("cc_exp_year")?,
cc_type: row.get("cc_type")?,
};
Ok(InternalCreditCard {
guid: Guid::from_string(row.get("guid")?),
fields: credit_card_fields,
time_created: row.get("time_created")?,
time_last_used: row.get("time_last_used")?,
time_last_modified: row.get("time_last_modified")?,
times_used: row.get("times_used")?,
sync_change_counter: row.get("sync_change_counter")?,
})
}
}
#[derive(Debug, Clone, Hash, PartialEq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct CreditCard {
pub guid: String,
pub cc_name: String,
pub cc_number: String,
pub cc_exp_month: i64,
pub cc_exp_year: i64,
// Credit card types are a fixed set of strings as defined in the link below
// (https://searchfox.org/mozilla-central/rev/7ef5cefd0468b8f509efe38e0212de2398f4c8b3/toolkit/modules/CreditCard.jsm#9-22)
pub cc_type: String,
}
pub trait ExternalizeCreditCard {
fn to_external(&self) -> CreditCard;
}
impl ExternalizeCreditCard for InternalCreditCard {
fn to_external(&self) -> CreditCard {
CreditCard {
guid: self.guid.to_string(),
cc_name: self.clone().fields.cc_name,
cc_number: self.clone().fields.cc_number,
cc_exp_month: self.fields.cc_exp_month,
cc_exp_year: self.fields.cc_exp_year,
cc_type: self.clone().fields.cc_type,
}
}
}

Просмотреть файл

@ -0,0 +1,2 @@
pub mod address;
pub mod credit_card;

Просмотреть файл

@ -39,8 +39,8 @@ pub const CREDIT_CARD_COMMON_COLS: &str = "
sync_change_counter";
#[allow(dead_code)]
const CREATE_SHARED_SCHEMA_SQL: &str = include_str!("../sql/create_shared_schema.sql");
const CREATE_SHARED_TRIGGERS_SQL: &str = include_str!("../sql/create_shared_triggers.sql");
const CREATE_SHARED_SCHEMA_SQL: &str = include_str!("../../sql/create_shared_schema.sql");
const CREATE_SHARED_TRIGGERS_SQL: &str = include_str!("../../sql/create_shared_triggers.sql");
#[allow(dead_code)]
pub fn init(db: &Connection) -> Result<()> {

Просмотреть файл

@ -0,0 +1,104 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::db::models::address::{Address, ExternalizeAddress, NewAddressFields};
use crate::db::models::credit_card::{CreditCard, ExternalizeCreditCard, NewCreditCardFields};
use crate::db::{addresses, credit_cards, AutofillDb};
use crate::error::*;
use std::path::Path;
#[allow(dead_code)]
pub struct Store {
db: AutofillDb,
}
impl Store {
pub fn new(db_path: impl AsRef<Path>) -> Result<Self> {
Ok(Self {
db: AutofillDb::new(db_path)?,
})
}
/// Creates a store backed by an in-memory database.
#[cfg(test)]
pub fn new_memory(db_path: &str) -> Result<Self> {
Ok(Self {
db: AutofillDb::new_memory(db_path)?,
})
}
#[allow(dead_code)]
pub fn add_credit_card(
&self,
new_credit_card_fields: NewCreditCardFields,
) -> Result<CreditCard> {
let credit_card = credit_cards::add_credit_card(&self.db.writer, new_credit_card_fields)?;
Ok(credit_card.to_external())
}
#[allow(dead_code)]
pub fn get_credit_card(&self, guid: String) -> Result<CreditCard> {
let credit_card = credit_cards::get_credit_card(&self.db.writer, guid)?;
Ok(credit_card.to_external())
}
#[allow(dead_code)]
pub fn get_all_credit_cards(&self) -> Result<Vec<CreditCard>> {
let credit_cards = credit_cards::get_all_credit_cards(&self.db.writer)?
.iter()
.map(|x| x.to_external())
.collect();
Ok(credit_cards)
}
#[allow(dead_code)]
pub fn update_credit_card(&self, credit_card: &CreditCard) -> Result<()> {
credit_cards::update_credit_card(&self.db.writer, credit_card)
}
#[allow(dead_code)]
pub fn delete_credit_card(&self, guid: String) -> Result<bool> {
credit_cards::delete_credit_card(&self.db.writer, guid)
}
pub fn touch_credit_card(&self, guid: String) -> Result<()> {
credit_cards::touch(&self.db.writer, guid)
}
#[allow(dead_code)]
pub fn add_address(&self, new_address: NewAddressFields) -> Result<Address> {
let address = addresses::add_address(&self.db.writer, new_address)?;
Ok(address.to_external())
}
#[allow(dead_code)]
pub fn get_address(&self, guid: String) -> Result<Address> {
let address = addresses::get_address(&self.db.writer, guid)?;
Ok(address.to_external())
}
#[allow(dead_code)]
pub fn get_all_addresses(&self) -> Result<Vec<Address>> {
let addresses = addresses::get_all_addresses(&self.db.writer)?
.iter()
.map(|x| x.to_external())
.collect();
Ok(addresses)
}
#[allow(dead_code)]
pub fn update_address(&self, address: &Address) -> Result<()> {
addresses::update_address(&self.db.writer, address)
}
#[allow(dead_code)]
pub fn delete_address(&self, guid: String) -> Result<bool> {
addresses::delete_address(&self.db.writer, guid)
}
#[allow(dead_code)]
pub fn touch_address(&self, guid: String) -> Result<()> {
addresses::touch(&self.db.writer, guid)
}
}

Просмотреть файл

@ -1,6 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
use interrupt_support::Interrupted;

Просмотреть файл

@ -1,12 +1,10 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#![allow(unknown_lints)]
#![warn(rust_2018_idioms)]
pub mod api;
pub mod db;
pub mod error;
mod schema;
pub mod store;

Просмотреть файл

@ -1,28 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::db::AutofillDb;
use crate::error::*;
use std::path::Path;
#[allow(dead_code)]
pub struct Store {
db: AutofillDb,
}
impl Store {
pub fn new(db_path: impl AsRef<Path>) -> Result<Self> {
Ok(Self {
db: AutofillDb::new(db_path)?,
})
}
/// Creates a store backed by an in-memory database.
#[cfg(test)]
pub fn new_memory(db_path: &str) -> Result<Self> {
Ok(Self {
db: AutofillDb::new_memory(db_path)?,
})
}
}

Просмотреть файл

@ -1,15 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![warn(rust_2018_idioms)]
use anyhow::Result;
use autofill::api::{addresses, credit_cards};
use autofill::db::AutofillDb;
use autofill::db::{
models::{address, credit_card},
store::Store,
};
use std::{fs::File, io::BufReader};
use structopt::StructOpt;
use sync_guid::Guid;
// Note: this uses doc comments to generate the help text.
#[derive(Clone, Debug, StructOpt)]
@ -107,125 +108,119 @@ enum Command {
},
}
fn run_add_address(db: &mut AutofillDb, filename: String) -> Result<()> {
fn run_add_address(store: &Store, filename: String) -> Result<()> {
println!("Retrieving address data from {}", filename);
let file = File::open(filename)?;
let reader = BufReader::new(file);
let address_fields: addresses::NewAddressFields = serde_json::from_reader(reader)?;
let address_fields: address::NewAddressFields = serde_json::from_reader(reader)?;
println!("Making `add_address` api call");
let address = addresses::add_address(&mut db.writer, address_fields)?;
let address = Store::add_address(store, address_fields)?;
println!("Created address: {:#?}", address);
Ok(())
}
fn run_get_address(db: &mut AutofillDb, guid: String) -> Result<()> {
fn run_get_address(store: &Store, guid: String) -> Result<()> {
println!("Getting address for guid `{}`", guid);
let address = addresses::get_address(&mut db.writer, &Guid::from(guid))?;
let address = Store::get_address(store, guid)?;
println!("Retrieved address: {:#?}", address);
Ok(())
}
fn run_get_all_addresses(db: &mut AutofillDb) -> Result<()> {
fn run_get_all_addresses(store: &Store) -> Result<()> {
println!("Getting all addresses");
let addresses = addresses::get_all_addresses(&mut db.writer)?;
let addresses = Store::get_all_addresses(store)?;
println!("Retrieved addresses: {:#?}", addresses);
Ok(())
}
fn run_update_address(db: &mut AutofillDb, filename: String) -> Result<()> {
fn run_update_address(store: &Store, filename: String) -> Result<()> {
println!("Updating address data from {}", filename);
let file = File::open(filename)?;
let reader = BufReader::new(file);
let address_fields: addresses::Address = serde_json::from_reader(reader)?;
let address_fields: address::Address = serde_json::from_reader(reader)?;
let guid = address_fields.guid.clone();
println!(
"Making `update_address` api call for guid {}",
guid.to_string()
);
addresses::update_address(&mut db.writer, address_fields)?;
println!("Making `update_address` api call for guid {}", guid);
Store::update_address(store, &address_fields)?;
let address = addresses::get_address(&mut db.writer, &guid)?;
let address = Store::get_address(store, guid)?;
println!("Updated address: {:#?}", address);
Ok(())
}
fn run_delete_address(db: &mut AutofillDb, guid: String) -> Result<()> {
fn run_delete_address(store: &Store, guid: String) -> Result<()> {
println!("Deleting address for guid `{}`", guid);
addresses::delete_address(&mut db.writer, &Guid::from(guid))?;
Store::delete_address(store, guid)?;
println!("Successfully deleted address");
Ok(())
}
fn run_add_credit_card(db: &mut AutofillDb, filename: String) -> Result<()> {
fn run_add_credit_card(store: &Store, filename: String) -> Result<()> {
println!("Retrieving credit card data from {}", filename);
let file = File::open(filename)?;
let reader = BufReader::new(file);
let credit_card_fields: credit_cards::NewCreditCardFields = serde_json::from_reader(reader)?;
let credit_card_fields: credit_card::NewCreditCardFields = serde_json::from_reader(reader)?;
println!("Making `add_credit_card` api call");
let credit_card = credit_cards::add_credit_card(&mut db.writer, credit_card_fields)?;
let credit_card = Store::add_credit_card(store, credit_card_fields)?;
println!("Created credit card: {:#?}", credit_card);
Ok(())
}
fn run_get_credit_card(db: &mut AutofillDb, guid: String) -> Result<()> {
fn run_get_credit_card(store: &Store, guid: String) -> Result<()> {
println!("Getting credit card for guid `{}`", guid);
let credit_card = credit_cards::get_credit_card(&mut db.writer, &Guid::from(guid))?;
let credit_card = Store::get_credit_card(store, guid)?;
println!("Retrieved credit card: {:#?}", credit_card);
Ok(())
}
fn run_get_all_credit_cards(db: &mut AutofillDb) -> Result<()> {
fn run_get_all_credit_cards(store: &Store) -> Result<()> {
println!("Getting all credit cards");
let credit_cards = credit_cards::get_all_credit_cards(&mut db.writer)?;
let credit_cards = Store::get_all_credit_cards(store)?;
println!("Retrieved credit cards: {:#?}", credit_cards);
Ok(())
}
fn run_update_credit_card(db: &mut AutofillDb, filename: String) -> Result<()> {
fn run_update_credit_card(store: &Store, filename: String) -> Result<()> {
println!("Updating credit card data from {}", filename);
let file = File::open(filename)?;
let reader = BufReader::new(file);
let credit_card_fields: credit_cards::CreditCard = serde_json::from_reader(reader)?;
let credit_card_fields: credit_card::CreditCard = serde_json::from_reader(reader)?;
let guid = credit_card_fields.guid.clone();
println!(
"Making `update_credit_card` api call for guid {}",
guid.to_string()
);
credit_cards::update_credit_card(&mut db.writer, credit_card_fields)?;
println!("Making `update_credit_card` api call for guid {}", guid);
Store::update_credit_card(store, &credit_card_fields)?;
let credit_card = credit_cards::get_credit_card(&mut db.writer, &guid)?;
let credit_card = Store::get_credit_card(store, guid)?;
println!("Updated credit card: {:#?}", credit_card);
Ok(())
}
fn run_delete_credit_card(db: &mut AutofillDb, guid: String) -> Result<()> {
fn run_delete_credit_card(store: &Store, guid: String) -> Result<()> {
println!("Deleting credit card for guid `{}`", guid);
credit_cards::delete_credit_card(&mut db.writer, &Guid::from(guid))?;
Store::delete_credit_card(store, guid)?;
println!("Successfully deleted credit card");
Ok(())
@ -238,19 +233,19 @@ fn main() -> Result<()> {
}
let db_path = opts.database_path;
let mut db = AutofillDb::new(db_path)?;
let store = Store::new(db_path)?;
match opts.cmd {
Command::AddAddress { input_file } => run_add_address(&mut db, input_file),
Command::GetAddress { guid } => run_get_address(&mut db, guid),
Command::GetAllAddresses => run_get_all_addresses(&mut db),
Command::UpdateAddress { input_file } => run_update_address(&mut db, input_file),
Command::DeleteAddress { guid } => run_delete_address(&mut db, guid),
Command::AddAddress { input_file } => run_add_address(&store, input_file),
Command::GetAddress { guid } => run_get_address(&store, guid),
Command::GetAllAddresses => run_get_all_addresses(&store),
Command::UpdateAddress { input_file } => run_update_address(&store, input_file),
Command::DeleteAddress { guid } => run_delete_address(&store, guid),
Command::AddCreditCard { input_file } => run_add_credit_card(&mut db, input_file),
Command::GetCreditCard { guid } => run_get_credit_card(&mut db, guid),
Command::GetAllCreditCards => run_get_all_credit_cards(&mut db),
Command::UpdateCreditCard { input_file } => run_update_credit_card(&mut db, input_file),
Command::DeleteCreditCard { guid } => run_delete_credit_card(&mut db, guid),
Command::AddCreditCard { input_file } => run_add_credit_card(&store, input_file),
Command::GetCreditCard { guid } => run_get_credit_card(&store, guid),
Command::GetAllCreditCards => run_get_all_credit_cards(&store),
Command::UpdateCreditCard { input_file } => run_update_credit_card(&store, input_file),
Command::DeleteCreditCard { guid } => run_delete_credit_card(&store, guid),
}
}