use sqlite::{Connection, Error, Transaction}; use std::{rc::Rc, sync::RwLock}; pub const NAME_MAX_LEN: i32 = 36; pub struct Table { pub id: i64, //pub profile_identity_id: i64, pub pem: String, pub name: Option, } /// Storage for Gemini auth certificates pub struct Database { connection: Rc>, profile_identity_id: Rc, // multi-profile relationship } impl Database { // Constructors /// Create new `Self` pub fn new(connection: Rc>, profile_identity_id: Rc) -> Self { Self { connection, profile_identity_id, } } // Actions /// Create new record in database pub fn add(&self, pem: &str, name: Option<&str>) -> Result { // Begin new transaction let mut writable = self.connection.write().unwrap(); // @TODO let tx = writable.transaction()?; // Create new record insert(&tx, *self.profile_identity_id, pem, name)?; // Hold insert ID for result let id = last_insert_id(&tx); // Done match tx.commit() { Ok(_) => Ok(id), Err(reason) => Err(reason), } } /// Get all records match current `profile_identity_id` pub fn records(&self) -> Result, Error> { let readable = self.connection.read().unwrap(); // @TODO let tx = readable.unchecked_transaction()?; select(&tx, *self.profile_identity_id) } } // Low-level DB API pub fn init(tx: &Transaction) -> Result { tx.execute( format!( "CREATE TABLE IF NOT EXISTS `profile_identity_gemini` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `profile_identity_id` INTEGER NOT NULL, `pem` TEXT NOT NULL, `name` VARCHAR({}), FOREIGN KEY (`profile_identity_id`) REFERENCES `profile_identity`(`id`) )", NAME_MAX_LEN ) .as_str(), [], ) } pub fn insert( tx: &Transaction, profile_identity_id: i64, pem: &str, name: Option<&str>, ) -> Result { tx.execute( "INSERT INTO `profile_identity_gemini` ( `profile_identity_id`, `pem`, `name` ) VALUES (?, ?, ?)", (profile_identity_id, pem, name), ) } pub fn select(tx: &Transaction, profile_identity_id: i64) -> Result, Error> { let mut stmt = tx.prepare( "SELECT `id`, `profile_identity_id`, `pem`, `name` FROM `profile_identity_gemini` WHERE `profile_identity_id` = ?", )?; let result = stmt.query_map([profile_identity_id], |row| { Ok(Table { id: row.get(0)?, //profile_identity_id: row.get(1)?, pem: row.get(2)?, name: row.get(3)?, }) })?; let mut records = Vec::new(); for record in result { let table = record?; records.push(table); } Ok(records) } pub fn last_insert_id(tx: &Transaction) -> i64 { tx.last_insert_rowid() }