use super::Message;
use super::Service;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use std::error::Error;
use std::io;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
use std::time::Duration;
use surrealdb::engine::remote::ws::{Client, Ws};
use surrealdb::sql::Thing;
use surrealdb::Surreal;
#[derive(Debug, Deserialize)]
pub struct Record {
id: Thing,
}
pub trait Entry {}
pub enum DatabaseType {
MEMORY,
LOCAL,
REMOTE,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum Command<'a> {
SELECT(&'a str),
CREATE(&'a str),
QUERY(&'a str),
}
impl<'a> Command<'a> {
pub fn to_json(&self) -> String {
serde_json::to_string(self).expect(&format!("failed to convert {:?} to json", self))
}
}
pub struct Database {
pub name: String,
rx: Option<Receiver<Message>>,
tx: Option<Sender<Message>>,
db: Surreal<Client>,
}
impl Database {
pub async fn new(
namespace: &str,
database_name: &str,
db_type: DatabaseType,
) -> Result<Self, Box<dyn Error>> {
let db = match db_type {
DatabaseType::MEMORY => {
return Err(Box::new(io::Error::new(
io::ErrorKind::Other,
"Cannot support memory databases yet",
)));
}
DatabaseType::LOCAL => {
let client = match Surreal::new::<Ws>("127.0.0.1:8000").await {
Ok(c) => c,
Err(e) => return Err(Box::new(e)), };
client.use_ns(namespace).use_db(database_name).await?;
client
}
DatabaseType::REMOTE => {
return Err(Box::new(io::Error::new(
io::ErrorKind::Other,
"Cannot support remote databases yet",
)));
}
};
Ok(Database {
name: database_name.to_string(),
rx: None,
tx: None,
db: db,
})
}
}
impl Service for Database {
fn run(&self) -> Result<(), Box<dyn Error>> {
loop {
if let Some(rx) = &self.rx {
if let Some(message) = rx.try_recv().ok() {
if let Ok(command) = serde_json::from_str::<Command>(&message.content) {
match command {
Command::CREATE(what) => {
}
Command::SELECT(what) => {}
Command::QUERY(what) => {}
}
}
}
}
}
}
fn send(&self, msg: Message) -> Result<(), Box<dyn Error>> {
if let Some(tx) = &self.tx {
tx.send(msg);
return Ok(());
}
Ok(())
}
fn identify(&self) -> String {
self.name.to_string()
}
fn set_comm_pair(&mut self, tx: Sender<Message>, rx: Receiver<Message>) -> () {
self.tx = Some(tx);
self.rx = Some(rx);
}
}
#[cfg(test)]
mod tests {
#[allow(dead_code)]
use super::*;
use std::{fs, path::Path};
#[allow(dead_code)]
fn clean() -> Result<(), Box<dyn std::error::Error>> {
let log_dir_path = Path::new("logs/tests");
if log_dir_path.exists() {
fs::remove_dir_all(log_dir_path)?;
println!("Successfully removed {}", log_dir_path.display());
} else {
println!("Directory {} does not exist.", log_dir_path.display());
}
Ok(())
}
#[tokio::test]
async fn test_db() {
let db_type = DatabaseType::LOCAL;
let db = Database::new("test", "test", db_type).await;
if let Err(e) = db {
panic!("test_db: {}", e)
}
}
#[test]
fn test_command() {
let c = Command::CREATE("test");
let cstr = c.to_json();
println!("{}", cstr);
let w: Command = serde_json::from_str(&cstr).expect("unable to deserialize");
assert_eq!(w, Command::CREATE("test"));
}
}