use async_trait::async_trait;
use credit_card::CreditCard;
use crate::traits::{DataVault, PoolErrors};
use crate::config::{DeadpoolPostgresConfig};
use crate::encryption::traits::Encryption;
use crate::tokenizer::{Tokenizer};
use deadpool_postgres::{tokio_postgres};
use std::error;
pub struct PostgresDataVault<E, T> {
pool: deadpool_postgres::Pool,
encryption: E,
tokenizer: T,
}
const SELECT_CREDIT_CARD: &str = "SELECT credit_card FROM data_vault WHERE token = $1";
const INSERT_CREDIT_CARD: &str = "INSERT INTO data_vault VALUES (token, credit_card) ($1, $2)";
const UPDATE_CREDIT_CARD: &str = "UPDATE data_vault SET credit_card = $2 WHERE token = $1";
const UPSERT_CREDIT_CARD: &str = "INSERT INTO data_vault (token, credit_card) VALUES ($1, $2) ON CONFLICT (token) DO UPDATE SET credit_card = EXCLUDED.credit_card";
const DELETE_CREDIT_CARD: &str = "DELETE FROM data_vault WHERE token = $1";
#[async_trait]
impl<E, T> DataVault for PostgresDataVault<E, T>
where
E: Encryption + std::marker::Sync + std::marker::Send,
T: Tokenizer + std::marker::Sync + std::marker::Send,
{
fn new() -> Result<Self, Box<dyn error::Error>> {
let cfg = DeadpoolPostgresConfig::from_env()?;
let pool = cfg.postgres.create_pool(tokio_postgres::NoTls)?;
let postgres_data_vault = PostgresDataVault {
pool,
encryption: E::new(),
tokenizer: T::new()
};
Ok(postgres_data_vault)
}
async fn store(&self, token: &String, string: &String) -> Result<(), PoolErrors> {
let client = self.pool.get().await?;
let encrypted_json = self.encryption.encrypt(string.as_bytes());
let stmt = client.prepare(UPSERT_CREDIT_CARD).await.unwrap();
let _ = client.query(&stmt, &[&token, &encrypted_json]).await.unwrap();
Ok(())
}
async fn store_credit_card(&self, credit_card: &CreditCard) -> Result<String, PoolErrors> {
let token = self.tokenizer.generate(&credit_card);
let credit_card_json = serde_json::to_string(&credit_card).unwrap();
let _:() = self.store(&token, &credit_card_json).await?;
Ok(token)
}
async fn retrieve(&self, token: &String) -> Result<String, PoolErrors> {
let client = self.pool.get().await?;
let stmt = client.prepare(SELECT_CREDIT_CARD).await.unwrap();
let row_result = client.query_one(&stmt, &[&token]).await;
let mut encrypted_credit_card_json: Vec<u8> = Vec::new();
if row_result.is_ok() {
let row = row_result.unwrap();
encrypted_credit_card_json = row.get("credit_card");
}
Ok(self.encryption.decrypt(encrypted_credit_card_json.as_slice()))
}
async fn retrieve_credit_card(&self, token: &String) -> Result<CreditCard, PoolErrors> {
let credit_card_json = self.retrieve(token).await?;
Ok(serde_json::from_str(&credit_card_json).unwrap_or_default())
}
}