Compare commits
	
		
			2 Commits
		
	
	
		
			829b7242bd
			...
			460ca4b5a7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 460ca4b5a7 | |||
| 45522660bb | 
							
								
								
									
										11
									
								
								.gitea/workflows/build.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.gitea/workflows/build.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | --- | ||||||
|  | name: MoreThanText build | ||||||
|  | run-name: ${{ gitea.actor }} runner | ||||||
|  | on: push | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   Build-MoreThanText: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - run: cargo test | ||||||
|  |       - run: cargo build --release | ||||||
							
								
								
									
										89
									
								
								src/morethantext/fieldtype/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/morethantext/fieldtype/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | |||||||
|  | mod static_string; | ||||||
|  |  | ||||||
|  | use crate::morethantext::error::MTTError; | ||||||
|  | use static_string::StaticString; | ||||||
|  | use std::fmt; | ||||||
|  |  | ||||||
|  | pub enum FieldType { | ||||||
|  |     StaticString(StaticString), | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl FieldType { | ||||||
|  |     fn new(ftype: &str, data: &str) -> Result<Self, MTTError> { | ||||||
|  |         let field = match ftype { | ||||||
|  |             "StaticString" => StaticString::new(data), | ||||||
|  |             _ => Err(MTTError::new(format!( | ||||||
|  |                 "field type {} does not exist", | ||||||
|  |                 ftype | ||||||
|  |             ))), | ||||||
|  |         }; | ||||||
|  |         match field { | ||||||
|  |             Ok(fld) => Ok(fld.into()), | ||||||
|  |             Err(e) => Err(e), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl fmt::Display for FieldType { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             FieldType::StaticString(data) => write!(f, "{}", data), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<StaticString> for FieldType { | ||||||
|  |     fn from(data: StaticString) -> Self { | ||||||
|  |         FieldType::StaticString(data) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod converstion { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn from_static_string() { | ||||||
|  |         let data = "a static string"; | ||||||
|  |         let field = StaticString::new(data).unwrap(); | ||||||
|  |         let ftype: FieldType = field.into(); | ||||||
|  |         assert!( | ||||||
|  |             ftype.to_string() == data, | ||||||
|  |             "\n\nGot:  {}\nWant: {}", | ||||||
|  |             ftype.to_string(), | ||||||
|  |             data | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn bad_field_type() -> Result<(), String> { | ||||||
|  |         let field_type = "dragon"; | ||||||
|  |         let err_msg = format!("field type {} does not exist", field_type); | ||||||
|  |         match FieldType::new(field_type, "marmalade") { | ||||||
|  |             Ok(_) => Err("Should have returned an error.".to_string()), | ||||||
|  |             Err(err) => { | ||||||
|  |                 if err.to_string() == err_msg { | ||||||
|  |                     Ok(()) | ||||||
|  |                 } else { | ||||||
|  |                     Err(format!( | ||||||
|  |                         "Error message is incorrect: Got: '{}' Want: '{}'", | ||||||
|  |                         err.to_string(), | ||||||
|  |                         err_msg | ||||||
|  |                     )) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn new_static_string() { | ||||||
|  |         let data = "This is a test."; | ||||||
|  |         let field = FieldType::new("StaticString", data).unwrap(); | ||||||
|  |         assert!( | ||||||
|  |             field.to_string() == data, | ||||||
|  |             "\n\nGot:  {}\nWant: {}\n\n", | ||||||
|  |             field.to_string(), | ||||||
|  |             data | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								src/morethantext/fieldtype/static_string.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/morethantext/fieldtype/static_string.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | use crate::morethantext::error::MTTError; | ||||||
|  | use std::fmt; | ||||||
|  |  | ||||||
|  | pub struct StaticString { | ||||||
|  |     data: String, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl StaticString { | ||||||
|  |     pub fn new<S>(name: S) -> Result<Self, MTTError> | ||||||
|  |     where | ||||||
|  |         S: Into<String>, | ||||||
|  |     { | ||||||
|  |         Ok(Self { data: name.into() }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl fmt::Display for StaticString { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |         write!(f, "{}", &self.data) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod creation { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn new_accepts_str() { | ||||||
|  |         let data = "some data"; | ||||||
|  |         let field = StaticString::new(data).unwrap(); | ||||||
|  |         assert!( | ||||||
|  |             field.to_string() == data, | ||||||
|  |             "\n\nGot:  {}\nWant: {}", | ||||||
|  |             field.to_string(), | ||||||
|  |             data | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn new_accepts_string() { | ||||||
|  |         let data = "actual string"; | ||||||
|  |         let field = StaticString::new(data.to_string()).unwrap(); | ||||||
|  |         assert!( | ||||||
|  |             field.to_string() == data, | ||||||
|  |             "\n\nGot:  {}\nWant: {}", | ||||||
|  |             field.to_string(), | ||||||
|  |             data | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										179
									
								
								src/morethantext/graphql.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								src/morethantext/graphql.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | |||||||
|  | use async_graphql::{Context, EmptySubscription, Error, Object, Result, Schema}; | ||||||
|  | use async_std::sync::RwLock; | ||||||
|  | use serde_json; | ||||||
|  |  | ||||||
|  | mod database; | ||||||
|  |  | ||||||
|  | #[derive(Clone)] | ||||||
|  | struct Table { | ||||||
|  |     name: String, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Table { | ||||||
|  |     async fn new(name: String) -> Self { | ||||||
|  |         Self { name: name } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[Object] | ||||||
|  | impl Table { | ||||||
|  |     async fn name(&self) -> String { | ||||||
|  |         self.name.to_string() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn describe(&self) -> Vec<u64> { | ||||||
|  |         Vec::new() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct Query; | ||||||
|  |  | ||||||
|  | #[Object] | ||||||
|  | impl Query { | ||||||
|  |     async fn table(&self, ctx: &Context<'_>, name: String) -> Result<Option<Table>> { | ||||||
|  |         let tbls = ctx | ||||||
|  |             .data::<RwLock<Vec<Table>>>() | ||||||
|  |             .unwrap() | ||||||
|  |             .read() | ||||||
|  |             .await | ||||||
|  |             .to_vec(); | ||||||
|  |         match tbls.binary_search_by(|t| t.name.cmp(&name)) { | ||||||
|  |             Ok(idx) => Ok(Some(tbls[idx].clone())), | ||||||
|  |             Err(_) => Ok(None), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn tables(&self, ctx: &Context<'_>) -> Vec<Table> { | ||||||
|  |         ctx.data::<RwLock<Vec<Table>>>() | ||||||
|  |             .unwrap() | ||||||
|  |             .read() | ||||||
|  |             .await | ||||||
|  |             .to_vec() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct Mutation; | ||||||
|  |  | ||||||
|  | #[Object] | ||||||
|  | impl Mutation { | ||||||
|  |     async fn create_table(&self, ctx: &Context<'_>, name: String) -> Result<Option<Table>> { | ||||||
|  |         let mut tables = ctx.data::<RwLock<Vec<Table>>>().unwrap().write().await; | ||||||
|  |         match tables.binary_search_by(|t| t.name.cmp(&name)) { | ||||||
|  |             Ok(_) => Err(Error::new(format!("Table {} already exists.", &name))), | ||||||
|  |             Err(_) => { | ||||||
|  |                 let output = Table::new(name).await; | ||||||
|  |                 tables.push(output.clone()); | ||||||
|  |                 tables.sort_by_key(|k| k.name.clone()); | ||||||
|  |                 Ok(Some(output)) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct MoreThanText { | ||||||
|  |     schema: Schema<Query, Mutation, EmptySubscription>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl MoreThanText { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         let tables: Vec<Table> = Vec::new(); | ||||||
|  |         Self { | ||||||
|  |             schema: Schema::build(Query, Mutation, EmptySubscription) | ||||||
|  |                 .data(RwLock::new(tables)) | ||||||
|  |                 .finish(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn execute(&self, qry: &str) -> String { | ||||||
|  |         let res = self.schema.execute(qry).await; | ||||||
|  |         serde_json::to_string(&res).unwrap() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod support { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     pub fn compare(db: &MoreThanText, output: &str, expected: &str) { | ||||||
|  |         assert!( | ||||||
|  |             output == expected, | ||||||
|  |             "\n\n{}\nGot:  {}\nWant: {}\n\n", | ||||||
|  |             db.schema.sdl(), | ||||||
|  |             output, | ||||||
|  |             expected | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod queries { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn list_table() { | ||||||
|  |         let db = MoreThanText::new(); | ||||||
|  |         db.execute(r#"mutation {createTable(name: "wilma"){name}}"#) | ||||||
|  |             .await; | ||||||
|  |         db.execute(r#"mutation {createTable(name: "betty"){name}}"#) | ||||||
|  |             .await; | ||||||
|  |         let output = db.execute(r#"{table(name: "wilma"){name}}"#).await; | ||||||
|  |         let expected = r#"{"data":{"table":{"name":"wilma"}}}"#; | ||||||
|  |         support::compare(&db, &output, &expected); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn list_no_table() { | ||||||
|  |         let db = MoreThanText::new(); | ||||||
|  |         let output = db.execute(r#"{table(name: "slade"){name}}"#).await; | ||||||
|  |         let expected = r#"{"data":{"table":null}}"#; | ||||||
|  |         support::compare(&db, &output, &expected); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn list_tables() { | ||||||
|  |         let db = MoreThanText::new(); | ||||||
|  |         db.execute(r#"mutation {createTable(name: "fred"){name}}"#) | ||||||
|  |             .await; | ||||||
|  |         db.execute(r#"mutation {createTable(name: "barney"){name}}"#) | ||||||
|  |             .await; | ||||||
|  |         let output = db.execute(r#"{tables{name}}"#).await; | ||||||
|  |         let expected = r#"{"data":{"tables":[{"name":"barney"},{"name":"fred"}]}}"#; | ||||||
|  |         support::compare(&db, &output, &expected); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn empty_table_description() { | ||||||
|  |         let db = MoreThanText::new(); | ||||||
|  |         let output = db | ||||||
|  |             .execute(r#"mutation {createTable(name: "pebbles"){name describe}}"#) | ||||||
|  |             .await; | ||||||
|  |         let expected = r#"{"data":{"createTable":{"name":"pebbles","describe":[]}}}"#; | ||||||
|  |         support::compare(&db, &output, &expected); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod mutations { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn add_table() { | ||||||
|  |         let db = MoreThanText::new(); | ||||||
|  |         let output = db | ||||||
|  |             .execute(r#"mutation {createTable(name: "william"){name}}"#) | ||||||
|  |             .await; | ||||||
|  |         let expected = r#"{"data":{"createTable":{"name":"william"}}}"#; | ||||||
|  |         support::compare(&db, &output, &expected); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn cannot_add_duplicate_table() { | ||||||
|  |         let db = MoreThanText::new(); | ||||||
|  |         let qry = r#"mutation {createTable(name: "gadzoo"){name}}"#; | ||||||
|  |         db.execute(&qry).await; | ||||||
|  |         let output = db.execute(qry).await; | ||||||
|  |         let expected = r#"{"data":null,"errors":[{"message":"Table gadzoo already exists.","locations":[{"line":1,"column":11}],"path":["createTable"]}]}"#; | ||||||
|  |         support::compare(&db, &output, &expected); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								src/morethantext/mttsql.pest
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/morethantext/mttsql.pest
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | char = _{ ASCII_ALPHANUMERIC | "_" } | ||||||
|  | whitespace = _{" " | "\t" | "\r" | "\n"} | ||||||
|  |  | ||||||
|  | name = {char+} | ||||||
|  | command = {"create database" ~ whitespace+ ~ name ~ ";"} | ||||||
|  | script = {command+} | ||||||
							
								
								
									
										398
									
								
								src/morethantext/old-mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										398
									
								
								src/morethantext/old-mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,398 @@ | |||||||
|  | /* | ||||||
|  | use async_std::sync::{Arc, RwLock}; | ||||||
|  | use std::{collections::HashMap, error::Error, fmt, str::FromStr}; | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct DBError { | ||||||
|  |     detail: String, | ||||||
|  |     source: Option<Box<DBError>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl DBError { | ||||||
|  |     fn new(detail: String) -> Self { | ||||||
|  |         Self { | ||||||
|  |             detail: detail.to_string(), | ||||||
|  |             source: None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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<FieldType, Self::Err> { | ||||||
|  |         match input { | ||||||
|  |             "table" => Ok(FieldType::Table), | ||||||
|  |             _ => Err(DBError::new(format!("field type {} does not exist", input))), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub struct Table { | ||||||
|  |     fields: Arc<RwLock<HashMap<String, FieldType>>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Table { | ||||||
|  |     pub async fn new() -> Self { | ||||||
|  |         Self { | ||||||
|  |             fields: Arc::new(RwLock::new(HashMap::new())), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn update_field(&self, name: &str, ftype: &str) -> Result<(), Box<dyn Error>> { | ||||||
|  |         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; | ||||||
|  |         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<String, FieldType> { | ||||||
|  |         let fmap = self.fields.read().await; | ||||||
|  |         fmap.clone() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | */ | ||||||
|  | use async_std::sync::{Arc, RwLock}; | ||||||
|  | use std::collections::HashMap; | ||||||
|  |  | ||||||
|  | pub mod error; | ||||||
|  | mod fieldtype; | ||||||
|  |  | ||||||
|  | use error::MTTError; | ||||||
|  | use fieldtype::FieldType; | ||||||
|  |  | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct MoreThanText; | ||||||
|  |  | ||||||
|  | impl MoreThanText { | ||||||
|  |     pub async fn new() -> Self { | ||||||
|  |         Self {} | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn add_table(&self, name: &str) -> Table { | ||||||
|  |         Table::new() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn get_table(&self, name: &str) -> Table { | ||||||
|  |         Table::new() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Clone, PartialEq)] | ||||||
|  | struct FieldDef; | ||||||
|  |  | ||||||
|  | pub struct Table { | ||||||
|  |     fields: Arc<RwLock<HashMap<String, FieldDef>>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Table { | ||||||
|  |     fn new() -> Self { | ||||||
|  |         Self { | ||||||
|  |             fields: Arc::new(RwLock::new(HashMap::new())), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn add_field(&self, name: &str) { | ||||||
|  |         let mut field_defs = self.fields.write().await; | ||||||
|  |         field_defs.insert(name.to_string(), FieldDef {}); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn get_field(&self, name: &str) -> Option<FieldDef> { | ||||||
|  |         let field_defs = self.fields.read().await; | ||||||
|  |         match field_defs.get(name) { | ||||||
|  |             Some(def) => Some(def.clone()), | ||||||
|  |             None => None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn new_record(&self) -> Record { | ||||||
|  |         Record::new() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct Record { | ||||||
|  |     data: Arc<RwLock<HashMap<String, FieldType>>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Record { | ||||||
|  |     fn new() -> Self { | ||||||
|  |         Self { | ||||||
|  |             data: Arc::new(RwLock::new(HashMap::new())), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |     async fn update_field(&self, name: String, data: FieldType) { | ||||||
|  |         let mut map = self.data.write().await; | ||||||
|  |         map.insert(name, data); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn get_field(&self, name: &str) -> Option<FieldType> { | ||||||
|  |         let map = self.data.read().await; | ||||||
|  |         match map.get(name) { | ||||||
|  |             Some(field) => Some(field.clone()), | ||||||
|  |             None => None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod databases { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn new_database() { | ||||||
|  |         MoreThanText::new().await; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn add_table() { | ||||||
|  |         let db = MoreThanText::new().await; | ||||||
|  |         let name = "table"; | ||||||
|  |         db.add_table(name).await; | ||||||
|  |         db.get_table(name).await; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tables { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn new_table() { | ||||||
|  |         Table::new(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn add_field_definition() { | ||||||
|  |         let tbl = Table::new(); | ||||||
|  |         let name = "field"; | ||||||
|  |         let expected = FieldDef {}; | ||||||
|  |         tbl.add_field(name).await; | ||||||
|  |         let output = tbl.get_field(name).await.unwrap(); | ||||||
|  |         assert!(output == expected, "Did not return a field definition."); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn missing_field_definition() { | ||||||
|  |         let tbl = Table::new(); | ||||||
|  |         let output = tbl.get_field("missing").await; | ||||||
|  |         assert!( | ||||||
|  |             output == None, | ||||||
|  |             "Should return None if field does not exist." | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn get_empty_record() { | ||||||
|  |         let tbl = Table::new(); | ||||||
|  |         tbl.new_record().await; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod records { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn update_fields() { | ||||||
|  |         let rec = Record::new(); | ||||||
|  |         let name = "elephant"; | ||||||
|  |         let data = ""; | ||||||
|  |         let sstr = StaticString::new(); | ||||||
|  |         rec.update_field(name.to_string(), sstr).await; | ||||||
|  |         let output = rec.get_field(name).await.unwrap(); | ||||||
|  |         assert!( | ||||||
|  |             output.to_string() == data, | ||||||
|  |             "\n\nGot:  {}\nWant: {}\n\n", | ||||||
|  |             output.to_string(), | ||||||
|  |             data | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn empty_field() { | ||||||
|  |         let rec = Record::new(); | ||||||
|  |         let name = "mull"; | ||||||
|  |         let output = rec.get_field(name).await; | ||||||
|  |         assert!(output == None, "Should return an option."); | ||||||
|  |     } | ||||||
|  |     */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tables { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn new_table() { | ||||||
|  |         Table::new().await; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn update_field() { | ||||||
|  |         let table = Table::new().await; | ||||||
|  |         let mut expected: HashMap<String, FieldType> = HashMap::new(); | ||||||
|  |         expected.insert("stan".to_string(), FieldType::Table); | ||||||
|  |         expected.insert("lee".to_string(), FieldType::Table); | ||||||
|  |         table.update_field("stan", "table").await.unwrap(); | ||||||
|  |         table.update_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.update_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.update_field(name, "table").await.unwrap(); | ||||||
|  |         match table.update_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)] | ||||||
|  | mod databases { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn new_database() { | ||||||
|  |         MoreThanText::new().await; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn add_table() { | ||||||
|  |         let db = MoreThanText::new().await; | ||||||
|  |         db.add_table("fred".to_string()).await; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod fieldtypes { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     fn get_field_map() -> HashMap<String, FieldType> { | ||||||
|  |         let mut fields: HashMap<String, FieldType> = 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 | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn convert_from_string() { | ||||||
|  |         for (key, value) in get_field_map().iter() { | ||||||
|  |             assert!( | ||||||
|  |                 &FieldType::from_str(key).unwrap() == value, | ||||||
|  |                 "\n\nDid not return a FieldType::{}", | ||||||
|  |                 key | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn convert_from_string_error() -> Result<(), String> { | ||||||
|  |         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)) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | */ | ||||||
							
								
								
									
										120
									
								
								src/morethantext/old-mod2.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/morethantext/old-mod2.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | |||||||
|  | pub mod error; | ||||||
|  | pub mod fieldtype; | ||||||
|  |  | ||||||
|  | use async_std::sync::{Arc, RwLock}; | ||||||
|  | use error::MTTError; | ||||||
|  | use std::collections::HashMap; | ||||||
|  |  | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct MoreThanText { | ||||||
|  |     tables: Arc<RwLock<HashMap<String, Table>>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl MoreThanText { | ||||||
|  |     pub async fn new() -> Self { | ||||||
|  |         Self { | ||||||
|  |             tables: Arc::new(RwLock::new(HashMap::new())), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn new_table<S>(&self, tname: S) -> Result<Table, MTTError> | ||||||
|  |     where | ||||||
|  |         S: Into<String>, | ||||||
|  |     { | ||||||
|  |         let mut tables = self.tables.write().await; | ||||||
|  |         let name = tname.into(); | ||||||
|  |         match tables.get(&name) { | ||||||
|  |             Some(_) => Err(MTTError::new(format!("table {} already exists", name))), | ||||||
|  |             None => { | ||||||
|  |                 let table = Table::new().await; | ||||||
|  |                 tables.insert(name, table.clone()); | ||||||
|  |                 Ok(table) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub async fn get_table(&self, name: &str) -> Option<Table> { | ||||||
|  |         let tables = self.tables.read().await; | ||||||
|  |         match tables.get(name) { | ||||||
|  |             Some(tbl) => Some(tbl.clone()), | ||||||
|  |             None => None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct Table; | ||||||
|  |  | ||||||
|  | impl Table { | ||||||
|  |     pub async fn new() -> Self { | ||||||
|  |         Self {} | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn new_column(&self, _name: &str, _type: &str) {} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod database { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn create_table_with_str() { | ||||||
|  |         let db = MoreThanText::new().await; | ||||||
|  |         db.new_table("william").await.unwrap(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn create_table_with_string() { | ||||||
|  |         let db = MoreThanText::new().await; | ||||||
|  |         db.new_table("marvin".to_string()).await.unwrap(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn table_names_are_unique() -> Result<(), String> { | ||||||
|  |         let db = MoreThanText::new().await; | ||||||
|  |         let name = "alexandar"; | ||||||
|  |         let msg = format!("table {} already exists", name); | ||||||
|  |         db.new_table(name).await.unwrap(); | ||||||
|  |         match db.new_table(name).await { | ||||||
|  |             Ok(_) => Err("Duplicate table names are not allowed.".to_string()), | ||||||
|  |             Err(err) => { | ||||||
|  |                 if err.to_string() == msg { | ||||||
|  |                     Ok(()) | ||||||
|  |                 } else { | ||||||
|  |                     Err(format!( | ||||||
|  |                         "Error message is incorrect: Got: '{}' Want: '{}'", | ||||||
|  |                         err.to_string(), | ||||||
|  |                         msg | ||||||
|  |                     )) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn get_non_existant_table() { | ||||||
|  |         let db = MoreThanText::new().await; | ||||||
|  |         let table = db.get_table("missing").await; | ||||||
|  |         assert!(table.is_none(), "There should be no table."); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn get_a_table() { | ||||||
|  |         let db = MoreThanText::new().await; | ||||||
|  |         let name = "here"; | ||||||
|  |         db.new_table(name).await.unwrap(); | ||||||
|  |         let table = db.get_table(name).await; | ||||||
|  |         assert!(table.is_some(), "Table should be found."); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod table { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn add_column() { | ||||||
|  |         let tbl = Table::new().await; | ||||||
|  |         tbl.new_column("fred", "StaticString").await; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										153
									
								
								src/morethantext/old-mod3.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/morethantext/old-mod3.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | |||||||
|  | pub mod error; | ||||||
|  |  | ||||||
|  | use async_std::sync::{Arc, RwLock}; | ||||||
|  | use error::DBError; | ||||||
|  | use std::collections::HashMap; | ||||||
|  |  | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct MoreThanText { | ||||||
|  |     databases: Arc<RwLock<HashMap<String, Database>>>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl MoreThanText { | ||||||
|  |     pub async fn new() -> Self { | ||||||
|  |         Self { | ||||||
|  |             databases: Arc::new(RwLock::new(HashMap::new())), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn create_database(&self, name: &str) -> Result<(), DBError> { | ||||||
|  |         let mut databases = self.databases.write().await; | ||||||
|  |         match databases.get(name) { | ||||||
|  |             Some(_) => Err(DBError::new("duplicate database name")), | ||||||
|  |             None => { | ||||||
|  |                 let db = Database::new().await; | ||||||
|  |                 databases.insert(name.to_string(), db); | ||||||
|  |                 Ok(()) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn use_database(&self, name: &str) -> Result<Database, DBError> { | ||||||
|  |         let databases = self.databases.read().await; | ||||||
|  |         match databases.get(name) { | ||||||
|  |             Some(db) => Ok(db.clone()), | ||||||
|  |             None => Err(DBError::new("database name not found")), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Clone)] | ||||||
|  | struct Database; | ||||||
|  |  | ||||||
|  | impl Database { | ||||||
|  |     async fn new() -> Self { | ||||||
|  |         Self {} | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async fn add_table(&self, _name: &str) {} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct Table; | ||||||
|  |  | ||||||
|  | impl Table { | ||||||
|  |     async fn new() -> Self { | ||||||
|  |         Self {} | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod engine_functions { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn create_database() { | ||||||
|  |         let mtt = MoreThanText::new().await; | ||||||
|  |         mtt.create_database("smith").await.unwrap(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn database_names_must_be_unique() -> Result<(), DBError> { | ||||||
|  |         let mtt = MoreThanText::new().await; | ||||||
|  |         let msg = "duplicate database name"; | ||||||
|  |         mtt.create_database("john").await.unwrap(); | ||||||
|  |         match mtt.create_database("john").await { | ||||||
|  |             Ok(_) => Err(DBError::new("Duplicate names should cause error")), | ||||||
|  |             Err(err) => { | ||||||
|  |                 if err.to_string() == msg { | ||||||
|  |                     Ok(()) | ||||||
|  |                 } else { | ||||||
|  |                     Err(DBError::new(format!( | ||||||
|  |                         "incorrect err message: got: '{}' want: '{}'", | ||||||
|  |                         err.to_string(), | ||||||
|  |                         msg | ||||||
|  |                     ))) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn use_database() -> Result<(), DBError> { | ||||||
|  |         let mtt = MoreThanText::new().await; | ||||||
|  |         let dbname = "Johnson"; | ||||||
|  |         mtt.create_database(dbname).await.unwrap(); | ||||||
|  |         mtt.use_database(dbname).await.unwrap(); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn use_missing_database() -> Result<(), DBError> { | ||||||
|  |         let error = "database name not found"; | ||||||
|  |         let mtt = MoreThanText::new().await; | ||||||
|  |         match mtt.use_database("ssmith").await { | ||||||
|  |             Ok(_) => Err(DBError::new("Should raise database missing error")), | ||||||
|  |             Err(err) => { | ||||||
|  |                 if err.to_string() == error { | ||||||
|  |                     Ok(()) | ||||||
|  |                 } else { | ||||||
|  |                     Err(DBError::new(format!( | ||||||
|  |                         "Incorrect error message: Got '{}' Want '{}'", | ||||||
|  |                         err.to_string(), | ||||||
|  |                         error | ||||||
|  |                     ))) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn create_get_table() { | ||||||
|  |         let db = "thedatabase"; | ||||||
|  |         let mtt = MoreThanText::new().await; | ||||||
|  |         mtt.create_database(db).await.unwrap(); | ||||||
|  |         let dbase = mtt.use_database(db).await.unwrap(); | ||||||
|  |         dbase.add_table("melvin").await; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod database_functions { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn new_database() { | ||||||
|  |         Database::new().await; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn new_table() { | ||||||
|  |         let db = Database::new().await; | ||||||
|  |         db.add_table("fred").await; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod table_functions { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[async_std::test] | ||||||
|  |     async fn new_table() { | ||||||
|  |         Table::new().await; | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user