commit 6a5cc7e7251db3ccd0be39355336c5601345d14c
parent 21d82c8b7c83fd7e70caa8f05873d456a6e7e4b6
Author: Jackson G. Kaindume <kaindume@kwatafana.org>
Date: Wed, 31 Aug 2022 19:46:27 +0200
[phora] initial database integration
Diffstat:
10 files changed, 255 insertions(+), 2 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,3 +1,4 @@
*~
phora/target
/Cargo.lock
+test-data
diff --git a/phora/Cargo.lock b/phora/Cargo.lock
@@ -12,6 +12,17 @@ dependencies = [
]
[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
name = "anyhow"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -30,6 +41,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851"
[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -127,6 +144,7 @@ dependencies = [
"base64",
"chacha20poly1305",
"ring",
+ "rusqlite",
"scrypt",
"serde",
]
@@ -143,6 +161,18 @@ dependencies = [
]
[[package]]
+name = "fallible-iterator"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
name = "generic-array"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -164,6 +194,24 @@ dependencies = [
]
[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d452c155cb93fecdfb02a73dd57b5d8e442c2063bd7aac72f1bc5e4263a43086"
+dependencies = [
+ "hashbrown",
+]
+
+[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -197,6 +245,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64de3cc433455c14174d42e554d4027ee631c4d046d43e3ecc6efc4636cdc7a7"
[[package]]
+name = "libsqlite3-sys"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f0455f2c1bc9a7caa792907026e469c1d91761fb0ea37cbb16427c77280cf35"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -238,6 +296,12 @@ dependencies = [
]
[[package]]
+name = "pkg-config"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
+
+[[package]]
name = "poly1305"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -291,6 +355,20 @@ dependencies = [
]
[[package]]
+name = "rusqlite"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a"
+dependencies = [
+ "bitflags",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+]
+
+[[package]]
name = "salsa20"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -344,6 +422,12 @@ dependencies = [
]
[[package]]
+name = "smallvec"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
+
+[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -395,6 +479,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/phora/Cargo.toml b/phora/Cargo.toml
@@ -9,4 +9,8 @@ ring = "0.16.20"
chacha20poly1305 = "0.9.0"
base64 = "0.13.0"
scrypt = "0.10.0"
-serde = { version = "1.0.144", features = ["derive"] }
-\ No newline at end of file
+serde = { version = "1.0.144", features = ["derive"] }
+rusqlite = { version = "0.28.0", optional = true }
+
+[features]
+sqlite = ["dep:rusqlite"]
+\ No newline at end of file
diff --git a/phora/src/database/#error.rs# b/phora/src/database/#error.rs#
@@ -0,0 +1,22 @@
+use std::fmt;
+
+#[derive(Debug)]
+pub enum DatabaseError {
+ ConnectionError,
+}
+
+impl std::error::Error for DatabaseError {}
+
+impl fmt::Display for DatabaseError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ DatabaseError::ConnectionError => write!(f, "Database connection error"),
+ }
+ }
+}
+
+impl From<rusqlite::> for CrawlError {
+ fn from(_: web3::error::Error) -> Self {
+ CrawlError::RPCConnection
+ }
+}
diff --git a/phora/src/database/error.rs b/phora/src/database/error.rs
@@ -0,0 +1,22 @@
+use std::fmt;
+
+#[derive(Debug)]
+pub enum DatabaseError {
+ ConnectionError,
+}
+
+impl std::error::Error for DatabaseError {}
+
+impl fmt::Display for DatabaseError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ DatabaseError::ConnectionError => write!(f, "Database connection error"),
+ }
+ }
+}
+
+impl From<rusqlite::Error> for DatabaseError {
+ fn from(_: rusqlite::Error) -> Self {
+ DatabaseError::ConnectionError
+ }
+}
diff --git a/phora/src/database/mod.rs b/phora/src/database/mod.rs
@@ -0,0 +1,12 @@
+use error::DatabaseError;
+pub mod error;
+
+pub trait DB {
+ /// New database instance
+ fn new(path: &str) -> Self;
+ /// Connect to database
+ fn connect(&mut self) -> Result<(), DatabaseError>;
+}
+
+#[cfg(feature = "sqlite")]
+pub mod sqlite;
diff --git a/phora/src/database/sqlite.rs b/phora/src/database/sqlite.rs
@@ -0,0 +1,72 @@
+use super::{error::DatabaseError, DB};
+use rusqlite::{params, Connection, Result};
+use std::fmt;
+
+pub mod accounts_sql {
+ /// Cyrtophora Account Schema
+ pub const CREATE_ACCOUNTS_TABLE: &str = "
+ CREATE TABLE IF NOT EXISTS accounts (
+ id INTEGER PRIMARY KEY, -- The Identifier of the User, the Rust Type is `i64`
+ name TEXT, -- Fullname of the account
+ username TEXT UNIQUE NOT NULL, -- The username of the User
+ email TEXT UNIQUE, -- Users's email address
+ password TEXT NOT NULL, -- The user's login password
+ joined TEXT DEFAULT(date('now')) NOT NULL) -- The date when the user joined, the Rust Type is `chrono::DateTime`";
+
+ /// Insert a user in the users table
+ pub const CREATE_ACCOUNT: &str = "
+ INSERT INTO accounts (
+ name,
+ username,
+ password,
+ email
+ )
+ VALUES (?1, ?2, ?3, ?4)";
+
+ /// Get by username
+ pub const GET_ACCOUNT: &str = "SELECT * FROM accounts WHERE username = :username;";
+
+ /// Drop accounts table
+ pub const DESTROY_ACCOUNTS: &str = "DROP table accounts";
+}
+
+/// database controller
+pub struct SqliteDB {
+ /// Database file path
+ path: String,
+ /// An SQLite connection handle
+ conn: Option<Connection>,
+}
+
+impl DB for SqliteDB {
+ fn new(path: &str) -> Self {
+ SqliteDB {
+ path: path.to_string(),
+ conn: None,
+ }
+ }
+
+ fn connect(&mut self) -> Result<(), DatabaseError> {
+ let conn = Connection::open(self.path.clone())?;
+ self.conn = Some(conn);
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn connect_db() {
+ let mut db = SqliteDB::new("test-data/ACCOUNTS.db");
+
+ // Connect to database
+ db.connect().unwrap();
+
+ match db.conn {
+ Some(_conn) => assert!(true),
+ _ => assert!(false),
+ }
+ }
+}
diff --git a/phora/src/lib.rs b/phora/src/lib.rs
@@ -2,4 +2,5 @@ pub mod account;
pub mod api;
pub mod crypto;
pub mod data;
+pub mod database;
pub mod validate;
diff --git a/spec/database.md b/spec/database.md
@@ -0,0 +1,12 @@
+---
+title: Database
+subtitle:
+author: Jackson G. Kaindume
+date: 2022-08-31
+...
+---
+
+Cyrtophora stores structured data in a database. The following data is
+is stored:
+
+1. Accounts
diff --git a/spec/sqlite-support.md b/spec/sqlite-support.md
@@ -0,0 +1,17 @@
+---
+title: Sqlite Support
+subtitle:
+author: Jackson G. Kaindume
+date: 2022-08-31
+...
+---
+
+Sqlite is supported in cyrtophora as an optional feature:
+
+```toml
+cyrtophora = { path = "../../cyrtophora/phora", features = ["sqlite"] }
+```
+When the sqlite feature is enabled user account data will be saved in
+a sqlite database.
+
+