From dfa96dfc00adbbacb4bedd9b131aa90cac908e3f Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Wed, 13 Jul 2022 00:14:00 -0400 Subject: [PATCH] Added fields to table. --- src/database/database.rs | 170 ++++++++++++++++++++++++++++++++------- 1 file changed, 139 insertions(+), 31 deletions(-) diff --git a/src/database/database.rs b/src/database/database.rs index c6c403a..fd01edd 100644 --- a/src/database/database.rs +++ b/src/database/database.rs @@ -1,31 +1,62 @@ use async_std::sync::{Arc, RwLock}; -use std::{collections::HashMap, fmt, str::FromStr}; +use std::{collections::HashMap, error::Error, fmt, str::FromStr}; -#[derive(Clone, PartialEq)] -pub enum Field { - Table, +#[derive(Debug)] +pub struct DBError { + detail: String, + source: Option>, } -impl fmt::Display for Field { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Field::Table => write!(f, "table"), +impl DBError { + fn new(detail: String) -> Self { + Self { + detail: detail.to_string(), + source: None, } } } -impl FromStr for Field { - type Err = (); - fn from_str(input: &str) -> Result { +impl fmt::Display for DBError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", &self.detail) + } +} + +impl Error for DBError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match &self.source { + Some(err) => Some(err), + None => None, + } + } +} + +#[derive(Clone, PartialEq)] +pub enum FieldType { + Table, +} + +impl fmt::Display for FieldType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + FieldType::Table => write!(f, "table"), + } + } +} + +impl FromStr for FieldType { + type Err = DBError; + + fn from_str(input: &str) -> Result { match input { - "table" => Ok(Field::Table), - _ => Err(()), + "table" => Ok(FieldType::Table), + _ => Err(DBError::new(format!("field type {} does not exist", input))), } } } pub struct Table { - fields: Arc>>, + fields: Arc>>, } impl Table { @@ -35,12 +66,29 @@ impl Table { } } - pub async fn add_field(&self, name: &str, ftype: &str) { + pub async fn add_field(&self, name: &str, ftype: &str) -> Result<(), Box> { + let ftype = match FieldType::from_str(ftype) { + Ok(field) => field, + Err(err) => { + let mut error = DBError::new(format!("failed to add field {}", name)); + error.source = Some(Box::new(err)); + return Err(Box::new(error)); + } + }; let mut fmap = self.fields.write().await; - fmap.insert(name.to_string(), Field::from_str(ftype).unwrap()); + match fmap.get(name) { + Some(_) => Err(Box::new(DBError::new(format!( + "field {} already exists", + name + )))), + None => { + fmap.insert(name.to_string(), ftype); + Ok(()) + } + } } - pub async fn fields(&self) -> HashMap { + pub async fn fields(&self) -> HashMap { let fmap = self.fields.read().await; fmap.clone() } @@ -70,14 +118,57 @@ mod tables { #[async_std::test] async fn add_field() { let table = Table::new().await; - let mut expected: HashMap = HashMap::new(); - expected.insert("stan".to_string(), Field::Table); - expected.insert("lee".to_string(), Field::Table); - table.add_field("stan", "table").await; - table.add_field("lee", "table").await; + let mut expected: HashMap = HashMap::new(); + expected.insert("stan".to_string(), FieldType::Table); + expected.insert("lee".to_string(), FieldType::Table); + table.add_field("stan", "table").await.unwrap(); + table.add_field("lee", "table").await.unwrap(); let output = table.fields().await; assert!(output == expected, "Table did not get the fields added."); } + + #[async_std::test] + async fn add_bad_field() -> Result<(), String> { + let table = Table::new().await; + let name = "failure"; + let bad_type = "ljksdbtt"; + let expected = format!("failed to add field {}", name); + let source = format!("field type {} does not exist", bad_type); + match table.add_field(name, bad_type).await { + Ok(_) => Err("A bad field type should not return successfully".to_string()), + Err(err) => { + if format!("{}", err) != expected { + Err(format!("Got: '{}' - Want: '{}'", err, expected)) + } else if format!("{}", err.source().unwrap()) != source { + Err(format!( + "Got: '{}' - Want: '{}'", + err.source().unwrap(), + source + )) + } else { + Ok(()) + } + } + } + } + + #[async_std::test] + async fn add_duplicate_field() -> Result<(), String> { + let table = Table::new().await; + let name = "twice"; + let expected = format!("field {} already exists", name); + table.add_field(name, "table").await.unwrap(); + match table.add_field(name, "table").await { + Ok(_) => Err(format!("Cannot have two fields with named '{}'", name)), + Err(err) => { + if format!("{}", err) == expected { + Ok(()) + } else { + Err(format!("Got: '{}' - Want: '{}'", err, expected)) + } + } + } + } } #[cfg(test)] @@ -97,34 +188,51 @@ mod databases { } #[cfg(test)] -mod fields { +mod fieldtypes { use super::*; - fn get_field_map() -> HashMap { - let mut fields: HashMap = HashMap::new(); - fields.insert("table".to_string(), Field::Table); + fn get_field_map() -> HashMap { + let mut fields: HashMap = HashMap::new(); + fields.insert("table".to_string(), FieldType::Table); return fields; } #[test] fn convert_to_string() { for (key, value) in get_field_map().iter() { - assert!(key == &value.to_string(), "\n\nGot: {}\nWant: {}\n\n", value.to_string(), key); + assert!( + key == &value.to_string(), + "\n\nGot: {}\nWant: {}\n\n", + value.to_string(), + key + ); } } #[test] fn convert_from_string() { for (key, value) in get_field_map().iter() { - assert!(&Field::from_str(key).unwrap() == value, "\n\nDid not return a Field::{}", key); + assert!( + &FieldType::from_str(key).unwrap() == value, + "\n\nDid not return a FieldType::{}", + key + ); } } #[test] fn convert_from_string_error() -> Result<(), String> { - match Field::from_str("jkljkl") { - Ok(_) => Err("Field jkljkl should not exist.".to_string()), - Err(_) => Ok(()), + let ftype = "lkjsdfh"; + let expected = format!("field type {} does not exist", ftype); + match FieldType::from_str(ftype) { + Ok(_) => Err(format!("Found field type {}", ftype)), + Err(err) => { + if format!("{}", err) == expected { + Ok(()) + } else { + Err(format!("Got: '{}' - Want: '{}'", err, expected)) + } + } } } }