Quellcode durchsuchen

walletdb: WIP abstraction

parazyd vor 3 Jahren
Ursprung
Commit
9ec42c941a
1 geänderte Dateien mit 103 neuen und 1 gelöschten Zeilen
  1. 103 1
      src/wallet/walletdb.rs

+ 103 - 1
src/wallet/walletdb.rs

@@ -16,7 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::path::PathBuf;
+use std::{any::Any, path::PathBuf};
 
 use async_std::sync::{Arc, Mutex};
 use log::{debug, info};
@@ -55,6 +55,25 @@ impl From<u8> for QueryType {
     }
 }
 
+#[derive(Debug)]
+pub enum SqlType {
+    Integer(i64),
+    Text(String),
+    Blob(Vec<u8>),
+    Null,
+}
+
+impl SqlType {
+    pub fn inner<T: 'static>(&self) -> Option<&T> {
+        match self {
+            SqlType::Integer(v) => (v as &dyn Any).downcast_ref::<T>(),
+            SqlType::Text(v) => (v as &dyn Any).downcast_ref::<T>(),
+            SqlType::Blob(v) => (v as &dyn Any).downcast_ref::<T>(),
+            SqlType::Null => None,
+        }
+    }
+}
+
 /// Structure representing base wallet operations.
 /// Additional operations can be implemented by trait extensions.
 pub struct WalletDb {
@@ -86,6 +105,57 @@ impl WalletDb {
         let _ = self.conn.lock().await.execute(query, ())?;
         Ok(())
     }
+
+    pub async fn query_single(
+        &self,
+        table: &str,
+        col_names: Vec<&str>,
+        where_queries: Option<Vec<(&str, SqlType)>>,
+    ) -> Result<Vec<SqlType>> {
+        let mut query = format!("SELECT {} FROM {}", col_names.join(", "), table);
+
+        if let Some(wq) = where_queries.as_ref() {
+            let where_str: Vec<String> = wq.iter().map(|(k, _)| format!("{} = ?", k)).collect();
+            query.push_str(&format!(" WHERE {}", where_str.join(" AND ")));
+        }
+
+        let params: Vec<rusqlite::types::ToSqlOutput> = where_queries.map_or(Vec::new(), |wq| {
+            wq.into_iter()
+                .map(|(_, v)| match v {
+                    SqlType::Integer(i) => rusqlite::types::ToSqlOutput::from(i),
+                    SqlType::Text(t) => rusqlite::types::ToSqlOutput::from(t),
+                    SqlType::Blob(b) => rusqlite::types::ToSqlOutput::from(b),
+                    SqlType::Null => rusqlite::types::ToSqlOutput::from(rusqlite::types::Null),
+                })
+                .collect::<Vec<_>>()
+        });
+
+        let wallet_conn = self.conn.lock().await;
+        let mut stmt = wallet_conn.prepare(&query)?;
+        let params_as_slice: Vec<&dyn rusqlite::ToSql> =
+            params.iter().map(|x| x as &dyn rusqlite::ToSql).collect();
+        let mut rows = stmt.query(params_as_slice.as_slice())?;
+
+        let row = match rows.next()? {
+            Some(row_result) => row_result,
+            None => return Ok(vec![]),
+        };
+
+        let mut result = vec![];
+        for (idx, _) in col_names.iter().enumerate() {
+            let value: SqlType = match row.get_ref(idx)?.data_type() {
+                rusqlite::types::Type::Integer => SqlType::Integer(row.get(idx)?),
+                rusqlite::types::Type::Text => SqlType::Text(row.get(idx)?),
+                rusqlite::types::Type::Blob => SqlType::Blob(row.get(idx)?),
+                rusqlite::types::Type::Null => SqlType::Null,
+                _ => unimplemented!(),
+            };
+
+            result.push(value);
+        }
+
+        Ok(result)
+    }
 }
 
 #[cfg(test)]
@@ -104,4 +174,36 @@ mod tests {
         stmt.finalize().unwrap();
         assert!(numba == 42);
     }
+
+    #[async_std::test]
+    async fn test_query_single() {
+        let wallet = WalletDb::new(None, None).unwrap();
+        wallet
+            .exec_sql("CREATE TABLE mista ( why INTEGER, are TEXT, you INTEGER, gae BLOB );")
+            .await
+            .unwrap();
+
+        let why = 42;
+        let are = "are".to_string();
+        let you = 69;
+        let gae = vec![42u8; 32];
+
+        let query_str =
+            format!("INSERT INTO mista ( why, are, you, gae ) VALUES (?1, ?2, ?3, ?4);");
+
+        let wallet_conn = wallet.conn.lock().await;
+        let mut stmt = wallet_conn.prepare(&query_str).unwrap();
+        stmt.execute(rusqlite::params![why, are, you, gae]).unwrap();
+        stmt.finalize().unwrap();
+        drop(wallet_conn);
+
+        let ret =
+            wallet.query_single("mista", vec!["why", "are", "you", "gae"], None).await.unwrap();
+        assert!(ret.len() == 4);
+
+        assert!(ret[0].inner::<i64>().unwrap() == &why);
+        assert!(ret[1].inner::<String>().unwrap() == &are);
+        assert!(ret[2].inner::<i64>().unwrap() == &you);
+        assert!(ret[3].inner::<Vec<u8>>().unwrap() == &gae);
+    }
 }