/ src / profile / identity / database.rs
database.rs
  1  use anyhow::Result;
  2  use r2d2::Pool;
  3  use r2d2_sqlite::SqliteConnectionManager;
  4  use sqlite::Transaction;
  5  
  6  pub struct Table {
  7      pub id: i64,
  8      //pub profile_id: i64,
  9      pub pem: String,
 10  }
 11  
 12  /// Storage for Gemini auth certificates
 13  pub struct Database {
 14      pool: Pool<SqliteConnectionManager>,
 15      profile_id: i64,
 16  }
 17  
 18  impl Database {
 19      // Constructors
 20  
 21      /// Create new `Self`
 22      pub fn build(pool: &Pool<SqliteConnectionManager>, profile_id: i64) -> Self {
 23          Self {
 24              pool: pool.clone(),
 25              profile_id,
 26          }
 27      }
 28  
 29      // Actions
 30  
 31      /// Create new record in database
 32      pub fn add(&self, pem: &str) -> Result<i64> {
 33          // Begin new transaction
 34          let mut connection = self.pool.get()?;
 35          let tx = connection.transaction()?;
 36  
 37          // Create new record
 38          insert(&tx, self.profile_id, pem)?;
 39  
 40          // Hold insert ID for result
 41          let id = last_insert_id(&tx);
 42  
 43          // Done
 44          tx.commit()?;
 45          Ok(id)
 46      }
 47  
 48      /// Delete record with given `id` from database
 49      pub fn delete(&self, id: i64) -> Result<()> {
 50          // Begin new transaction
 51          let mut connection = self.pool.get()?;
 52          let tx = connection.transaction()?;
 53  
 54          // Create new record
 55          delete(&tx, id)?;
 56  
 57          // Done
 58          tx.commit()?;
 59          Ok(())
 60      }
 61  
 62      /// Get single record match `id`
 63      pub fn record(&self, id: i64) -> Result<Option<Table>> {
 64          let records = select(&self.pool.get()?.unchecked_transaction()?, self.profile_id)?; // @TODO single record query
 65          for record in records {
 66              if record.id == id {
 67                  return Ok(Some(record));
 68              }
 69          }
 70          Ok(None)
 71      }
 72  
 73      /// Get all records match current `profile_id`
 74      pub fn records(&self) -> Result<Vec<Table>> {
 75          select(&self.pool.get()?.unchecked_transaction()?, self.profile_id)
 76      }
 77  }
 78  
 79  // Low-level DB API
 80  
 81  pub fn init(tx: &Transaction) -> Result<usize> {
 82      Ok(tx.execute(
 83          "CREATE TABLE IF NOT EXISTS `profile_identity`
 84          (
 85              `id`         INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
 86              `profile_id` INTEGER NOT NULL,
 87              `pem`        TEXT NOT NULL,
 88  
 89              FOREIGN KEY (`profile_id`) REFERENCES `profile`(`id`)
 90          )",
 91          [],
 92      )?)
 93  }
 94  
 95  pub fn insert(tx: &Transaction, profile_id: i64, pem: &str) -> Result<usize> {
 96      Ok(tx.execute(
 97          "INSERT INTO `profile_identity` (
 98              `profile_id`,
 99              `pem`
100          ) VALUES (?, ?)",
101          (profile_id, pem),
102      )?)
103  }
104  
105  pub fn delete(tx: &Transaction, id: i64) -> Result<usize> {
106      Ok(tx.execute("DELETE FROM `profile_identity` WHERE `id` = ?", [id])?)
107  }
108  
109  pub fn select(tx: &Transaction, profile_id: i64) -> Result<Vec<Table>> {
110      let mut stmt = tx.prepare(
111          "SELECT `id`,
112                  `profile_id`,
113                  `pem`
114  
115          FROM `profile_identity` WHERE `profile_id` = ?",
116      )?;
117  
118      let result = stmt.query_map([profile_id], |row| {
119          Ok(Table {
120              id: row.get(0)?,
121              //profile_id: row.get(1)?,
122              pem: row.get(2)?,
123          })
124      })?;
125  
126      let mut records = Vec::new();
127  
128      for record in result {
129          let table = record?;
130          records.push(table);
131      }
132  
133      Ok(records)
134  }
135  
136  pub fn last_insert_id(tx: &Transaction) -> i64 {
137      tx.last_insert_rowid()
138  }