From 972c4686e006174e4a93be29be3809ea90cdef3a Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Thu, 28 Nov 2024 10:43:56 -0500 Subject: [PATCH] Added database incomplete error move. --- src/data/id.rs | 31 +++++++- src/data/mod.rs | 199 ++++++++++++++++++---------------------------- src/data/table.rs | 143 +++++++++++++++++++++++++++++++++ src/error.rs | 63 +++++++++++---- 4 files changed, 297 insertions(+), 139 deletions(-) create mode 100644 src/data/table.rs diff --git a/src/data/id.rs b/src/data/id.rs index 145e060..5614cae 100644 --- a/src/data/id.rs +++ b/src/data/id.rs @@ -5,6 +5,31 @@ use crate::{ use std::fmt; use uuid::Uuid; +#[derive(Clone, Debug)] +pub enum IDError { + InvalidUuid(String), +} + +impl fmt::Display for IDError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + IDError::InvalidUuid(data) => write!(f, "'{}' is not a valid uuid", data), + } + } +} + +#[cfg(test)] +mod dberrors { + use super::*; + + #[test] + fn invalid_uuid() { + let data = "notgood"; + let err = IDError::InvalidUuid(data.to_string()); + assert_eq!(err.to_string(), format!("'{}' is not a valid uuid", data)); + } +} + #[derive(Clone)] pub struct ID { data: Uuid, @@ -36,7 +61,7 @@ impl TryFrom<&str> for ID { fn try_from(value: &str) -> Result { match Uuid::try_from(value) { Ok(id) => Ok(ID::new(id)), - Err(_) => Err(MTTError::new(ErrorType::FieldIDInvalid(value.to_string()))), + Err(_) => Err(IDError::InvalidUuid(value.to_string()).into()), } } } @@ -72,7 +97,9 @@ mod id { match ID::try_from(bad) { Ok(_) => unreachable!("Should have failed to create an ID"), Err(err) => match err.get_code() { - ErrorType::FieldIDInvalid(id) => assert_eq!(id, bad), + ErrorType::IDErr(id) => match id { + IDError::InvalidUuid(data) => assert_eq!(data, bad), + } _ => unreachable!("Returned wrong error"), }, } diff --git a/src/data/mod.rs b/src/data/mod.rs index d2b1b4f..486acd1 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1,146 +1,99 @@ -mod id; +pub mod id; mod record; +mod table; use crate::{ - data::{ - id::ID, - record::{Field, Record}, - }, + data::table::Table, error::{ErrorType, MTTError}, }; -use std::collections::HashMap; +use std::{collections::HashMap, fmt, ops::Deref}; -enum FieldType { - ID, +#[derive(Debug, Clone)] +pub enum DBError { + DuplicateTable(String), } -struct FieldDef; - -impl FieldDef { - fn new() -> Self { - Self {} - } - - fn get_type(&self) -> FieldType { - FieldType::ID - } -} - -struct Table { - fields: HashMap, -} - -impl Table { - fn new() -> Self { - Self { - fields: HashMap::new(), +impl fmt::Display for DBError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DBError::DuplicateTable(data) => write!(f, "'{}' already exists", data), } } - - fn description(&self) -> &HashMap { - &self.fields - } - - fn add_field(&mut self, name: &str, field_type: FieldType) -> Result<(), MTTError> { - match self.fields.get(name) { - Some(_) => { - let err = ErrorType::TableAddFieldDuplicate(name.to_string()); - Err(MTTError::new(err)) - } - None => { - self.fields.insert(name.to_string(), FieldDef::new()); - Ok(()) - } - } - } - - fn add_record(&mut self, rec: Record) -> Result { - for (key, _) in rec.iter() { - match self.fields.get(key) { - Some(_) => {} - None => { - let err = ErrorType::TableRecordInvalidFieldName(key.to_string()); - return Err(MTTError::new(err)); - } - } - } - Ok(rec) - } } #[cfg(test)] -mod table { +mod errora { use super::*; #[test] - fn new_table() { + fn duplicate_table() { + let name = "fred"; + let err = DBError::DuplicateTable(name.to_string()); + assert_eq!(err.to_string(), format!("'{}' already exists", name)); + } +} + +struct Database { + tables: HashMap, +} + +impl Database { + fn new() -> Self { + Self { + tables: HashMap::new(), + } + } + + fn add_table(&mut self, name: &str, table: Table) -> Result<(), MTTError> { + match self.tables.get(name) { + Some(_) => { + let error = ErrorType::TableRecordInvalidFieldName(name.to_string()); + return Err(MTTError::new(error)); + } + None => {} + } + self.tables.insert(name.to_string(), table); + Ok(()) + } +} + +impl Deref for Database { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.tables + } +} + +#[cfg(test)] +mod databases { + use super::*; + + #[test] + fn create_new_database() { + let db = Database::new(); + assert_eq!(db.len(), 0); + } + + #[test] + fn add_table() { + let mut db = Database::new(); let tbl = Table::new(); - let data = tbl.description(); - assert_eq!(data.len(), 0); + let name = "Something"; + db.add_table(name, tbl).unwrap(); + assert_eq!(db.len(), 1); } #[test] - fn add_field() { - let mut tbl = Table::new(); - let field_name = "something "; - tbl.add_field(field_name, FieldType::ID); - let data = tbl.description(); - assert_eq!(data.len(), 1); - match data.get(field_name) { - Some(field_info) => match field_info.get_type() { - FieldType::ID => {} - _ => unreachable!("incorrect field type"), - }, - None => unreachable!("should return field definition"), - } - } - - #[test] - fn error_on_duplicate_name() { - let mut tbl = Table::new(); - let name = "one"; - tbl.add_field(name, FieldType::ID); - match tbl.add_field(name, FieldType::ID) { - Ok(_) => unreachable!(" Should not duplicates."), - Err(err) => match err.get_code() { - ErrorType::TableAddFieldDuplicate(result) => assert_eq!(result, name), - _ => unreachable!("should produce a duplicate name error"), - }, - } - } - - #[test] - fn add_record() { - let mut tbl = Table::new(); - let name = "id"; - let field_data = ID::random(); - tbl.add_field(name, FieldType::ID).unwrap(); - let mut data: HashMap = HashMap::new(); - data.insert(name.to_string(), field_data.clone().into()); - let rec = Record::new(data); - let result = tbl.add_record(rec).unwrap(); - assert_eq!( - result.get(name).unwrap().to_string(), - field_data.to_string() - ); - } - - #[test] - fn add_record_incorrect_field_name() { - let mut tbl = Table::new(); - let name = "id"; - let field_data = ID::random(); - let mut data: HashMap = HashMap::new(); - data.insert(name.to_string(), field_data.clone().into()); - let rec = Record::new(data); - match tbl.add_record(rec) { - Ok(_) => unreachable!("should have produced an error"), - Err(err) => match err.get_code() { - ErrorType::TableRecordInvalidFieldName(result) => { - assert_eq!(result, name); - } - _ => unreachable!("should have been invalid name error"), - }, + fn no_duplicate_names() { + let mut db = Database::new(); + let tbl1 = Table::new(); + let tbl2 = Table::new(); + let name = "Something"; + db.add_table(name, tbl1).unwrap(); + match db.add_table(name, tbl2) { + Ok(_) => unreachable!("Should have been an error"), + Err(err) => {} } } } diff --git a/src/data/table.rs b/src/data/table.rs new file mode 100644 index 0000000..ee40540 --- /dev/null +++ b/src/data/table.rs @@ -0,0 +1,143 @@ +use crate::{ + data::{ + id::ID, + record::{Field, Record}, + }, + error::{ErrorType, MTTError}, +}; +use std::collections::HashMap; + +enum FieldType { + ID, +} + +struct FieldDef; + +impl FieldDef { + fn new() -> Self { + Self {} + } + + fn get_type(&self) -> FieldType { + FieldType::ID + } +} + +pub struct Table { + fields: HashMap, +} + +impl Table { + pub fn new() -> Self { + Self { + fields: HashMap::new(), + } + } + + fn description(&self) -> &HashMap { + &self.fields + } + + fn add_field(&mut self, name: &str, field_type: FieldType) -> Result<(), MTTError> { + match self.fields.get(name) { + Some(_) => { + let err = ErrorType::TableAddFieldDuplicate(name.to_string()); + Err(MTTError::new(err)) + } + None => { + self.fields.insert(name.to_string(), FieldDef::new()); + Ok(()) + } + } + } + + fn add_record(&mut self, rec: Record) -> Result { + for (key, _) in rec.iter() { + match self.fields.get(key) { + Some(_) => {} + None => { + let err = ErrorType::TableRecordInvalidFieldName(key.to_string()); + return Err(MTTError::new(err)); + } + } + } + Ok(rec) + } +} + +#[cfg(test)] +mod table { + use super::*; + + #[test] + fn new_table() { + let tbl = Table::new(); + let data = tbl.description(); + assert_eq!(data.len(), 0); + } + + #[test] + fn add_field() { + let mut tbl = Table::new(); + let field_name = "something "; + tbl.add_field(field_name, FieldType::ID); + let data = tbl.description(); + assert_eq!(data.len(), 1); + match data.get(field_name) { + Some(field_info) => match field_info.get_type() { + FieldType::ID => {} + _ => unreachable!("incorrect field type"), + }, + None => unreachable!("should return field definition"), + } + } + + #[test] + fn error_on_duplicate_name() { + let mut tbl = Table::new(); + let name = "one"; + tbl.add_field(name, FieldType::ID); + match tbl.add_field(name, FieldType::ID) { + Ok(_) => unreachable!(" Should not duplicates."), + Err(err) => match err.get_code() { + ErrorType::TableAddFieldDuplicate(result) => assert_eq!(result, name), + _ => unreachable!("should produce a duplicate name error"), + }, + } + } + + #[test] + fn add_record() { + let mut tbl = Table::new(); + let name = "id"; + let field_data = ID::random(); + tbl.add_field(name, FieldType::ID).unwrap(); + let mut data: HashMap = HashMap::new(); + data.insert(name.to_string(), field_data.clone().into()); + let rec = Record::new(data); + let result = tbl.add_record(rec).unwrap(); + assert_eq!( + result.get(name).unwrap().to_string(), + field_data.to_string() + ); + } + + #[test] + fn add_record_incorrect_field_name() { + let mut tbl = Table::new(); + let name = "id"; + let field_data = ID::random(); + let mut data: HashMap = HashMap::new(); + data.insert(name.to_string(), field_data.clone().into()); + let rec = Record::new(data); + match tbl.add_record(rec) { + Ok(_) => unreachable!("should have produced an error"), + Err(err) => match err.get_code() { + ErrorType::TableRecordInvalidFieldName(result) => { + assert_eq!(result, name); + } + _ => unreachable!("should have been invalid name error"), + }, + } + } +} diff --git a/src/error.rs b/src/error.rs index 9347691..c8a4954 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,16 +1,32 @@ +use crate::data::{DBError, id::IDError}; use std::{error::Error, fmt}; #[derive(Debug)] pub enum ErrorType { - FieldIDInvalid(String), + DBErr(DBError), + IDErr(IDError), TableAddFieldDuplicate(String), TableRecordInvalidFieldName(String), } +impl From for ErrorType { + fn from(value: DBError) -> Self { + ErrorType::DBErr(value) + } +} + +impl From for ErrorType { + fn from(value: IDError) -> Self { + ErrorType::IDErr(value) + } +} + impl fmt::Display for ErrorType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ErrorType::FieldIDInvalid(data) => write!(f, "'{}' is not a valid uuid", data), + ErrorType::DBErr(data) => write!(f, "database: {}", data), + ErrorType::IDErr(data) => write!(f, "id: {}", data), + ErrorType::TableAddFieldDuplicate(data) => write!(f, "field '{}' already exists", data), ErrorType::TableRecordInvalidFieldName(data) => { write!(f, "invalid field name '{}'", data) @@ -19,6 +35,25 @@ impl fmt::Display for ErrorType { } } +#[cfg(test)] +mod errortypes { + use super::*; + + #[test] + fn database_error() { + let err = DBError::DuplicateTable("something".to_string()); + let result = ErrorType::from(err.clone()); + assert_eq!(result.to_string(), format!("database: {}", err)); + } + + #[test] + fn id_error() { + let err = IDError::InvalidUuid("bad".to_string()); + let result = ErrorType::from(err.clone()); + assert_eq!(result.to_string(), format!("id: {}", err)); + } +} + #[derive(Debug)] pub struct MTTError { err: ErrorType, @@ -48,6 +83,14 @@ impl fmt::Display for MTTError { } } +impl From for MTTError { + fn from(value: IDError) -> Self { + Self { + err: value.into(), + } + } +} + #[cfg(test)] mod errors { use super::*; @@ -62,18 +105,10 @@ mod errors { } #[test] - fn from_invalid_id() { - let data = rand_str(); - let etype = ErrorType::FieldIDInvalid(data.clone()); - let result = MTTError::from(etype); - match result.get_code() { - ErrorType::FieldIDInvalid(txt) => assert_eq!(txt, &data), - _ => unreachable!("should have been ErrorType::FieldIDInvalid"), - } - assert_eq!( - result.to_string(), - format!("'{}' is not a valid uuid", data) - ); + fn from_id_error() { + let error = IDError::InvalidUuid(rand_str()); + let err = MTTError::from(error.clone()); + assert_eq!(err.to_string(), ErrorType::IDErr(error).to_string()); } #[test]