Implement get/set functionality for database
This commit is contained in:
		
							parent
							
								
									185f7ced8d
								
							
						
					
					
						commit
						1572d7fb5a
					
				| @ -1,52 +1,48 @@ | |||||||
|  | #[cfg(test)] | ||||||
|  | mod tests; | ||||||
|  | 
 | ||||||
| use log::warn; | use log::warn; | ||||||
| use serde_json; | use serde_json; | ||||||
|  | use serde_json::json; | ||||||
|  | use std::collections::HashMap; | ||||||
| use std::error::Error; | use std::error::Error; | ||||||
| use std::fs; |  | ||||||
| use std::fmt; | use std::fmt; | ||||||
|  | use std::fs; | ||||||
| use std::path; | use std::path; | ||||||
| 
 | 
 | ||||||
| extern crate custom_error; | extern crate custom_error; | ||||||
| use custom_error::custom_error; | use custom_error::custom_error; | ||||||
| 
 | 
 | ||||||
| custom_error! {DatabaseError | const JSON_POINTER_SEPARATOR: &str = "/"; | ||||||
|  | 
 | ||||||
|  | custom_error! {DBError | ||||||
|     NotDirectory{path: String} = "Path to database should point at the directory: {path}", |     NotDirectory{path: String} = "Path to database should point at the directory: {path}", | ||||||
|  |     NoFile{group_name: String, file_name: String} = r#"There is no "{file_name}" file in group "{group_name}"."#, | ||||||
|  |     IncorrectPointer{pointer: String} = "Incorrect pointer is specified: {pointer}.", | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct File { | enum ValueReference<'a> { | ||||||
|     name: String, |     Object(&'a mut serde_json::Map<String, serde_json::Value>, String), | ||||||
|     contents: serde_json::Value, |     Array(&'a mut Vec<serde_json::Value>, usize), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct Group { | type FileID<'a> = (&'a str, &'a str); | ||||||
|     name: String, |  | ||||||
|     files: Vec<File>, |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| struct Category { | pub struct Group { | ||||||
|     name: String, |     name: String, | ||||||
|     groups: Vec<Group>, |     files: HashMap<String, serde_json::Value>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct Database { | pub struct Database { | ||||||
|     storage: path::PathBuf, |     storage_path: path::PathBuf, | ||||||
|     contents: Vec<Category>, |     groups: Vec<Group>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Display for Group { | impl fmt::Display for Group { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         writeln!(f, "  ({})", self.name)?; |  | ||||||
|         for file in self.files.iter() { |  | ||||||
|             writeln!(f, r#"    File "{}": {}"#, file.name, file.contents.to_string())?; |  | ||||||
|         } |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl fmt::Display for Category { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         writeln!(f, "[{}]", self.name)?; |         writeln!(f, "[{}]", self.name)?; | ||||||
|         for g in self.groups.iter() { |         for (name, contents) in self.files.iter() { | ||||||
|             write!(f, "{}", g)?; |             writeln!(f, r#"  File "{}": {}"#, name, contents.to_string())?; | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @ -54,65 +50,153 @@ impl fmt::Display for Category { | |||||||
| 
 | 
 | ||||||
| impl fmt::Display for Database { | impl fmt::Display for Database { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         writeln!(f, "DB: {}", self.storage.display())?; |         writeln!(f, "DB: {}", self.storage_path.display())?; | ||||||
|         for c in self.contents.iter() { |         for g in self.groups.iter() { | ||||||
|             writeln!(f, "{}", c)?; |             writeln!(f, "{}", g)?; | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Database { | impl Database { | ||||||
|     pub fn new(storage: &path::Path) -> Result<Database, Box<dyn Error>> { |     pub fn new(storage_path: &path::Path) -> Result<Database, Box<dyn Error>> { | ||||||
|         if !storage.is_dir() { |         if !storage_path.is_dir() { | ||||||
|             return Err(Box::new(DatabaseError::NotDirectory { |             return Err(Box::new(DBError::NotDirectory { | ||||||
|                 path: storage.display().to_string(), |                 path: storage_path.display().to_string(), | ||||||
|             })); |             })); | ||||||
|         } |         } | ||||||
|         let mut contents = Vec::new(); |         let mut groups = Vec::new(); | ||||||
|         for entry in fs::read_dir(storage)? { |         for entry in fs::read_dir(storage_path)? { | ||||||
|             let entry = entry?; |             let entry = entry?; | ||||||
|             let path = entry.path(); |             let path = entry.path(); | ||||||
|             if !path.is_dir() { |             if !path.is_dir() { | ||||||
|                 warn!( |                 warn!( | ||||||
|                     r#"File {} found where only category directories are supposed to be"#, |                     r#"File {} found where only group directories are supposed to be"#, | ||||||
|                     path.display() |                     path.display() | ||||||
|                 ); |                 ); | ||||||
|             } else { |             } else { | ||||||
|                 let category = load_category(&path)?; |                 let group = load_group(&path)?; | ||||||
|                 contents.push(category); |                 groups.push(group); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         Ok(Database { |         Ok(Database { | ||||||
|             storage: storage.to_path_buf(), |             storage_path: storage_path.to_path_buf(), | ||||||
|             contents, |             groups, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| fn load_category(category_path: &path::Path) -> Result<Category, Box<dyn Error>> { |     pub fn group_names(&self) -> Vec<String> { | ||||||
|     let mut groups = Vec::new(); |         self.groups.iter().map(|x| x.name.clone()).collect() | ||||||
|     for entry in fs::read_dir(category_path)? { |     } | ||||||
|         let entry = entry?; | 
 | ||||||
|         let path = entry.path(); |     pub fn file_names_in(&self, group_name: &str) -> Vec<String> { | ||||||
|         if !path.is_dir() { |         match self.groups.iter().find(|x| x.name.eq(group_name)) { | ||||||
|             warn!( |             Some(group) => group.files.keys().map(|x| x.clone()).collect(), | ||||||
|                 r#"File {} found where only group directories are supposed to be"#, |             None => Vec::new(), | ||||||
|                 path.display() |  | ||||||
|             ); |  | ||||||
|         } else { |  | ||||||
|             let group = load_group(&path)?; |  | ||||||
|             groups.push(group); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     Ok(Category { | 
 | ||||||
|         name: get_file_name(category_path), |     fn group_index(&self, group_name: &str) -> Option<usize> { | ||||||
|         groups, |         self.groups.iter().position(|x| x.name.eq(group_name)) | ||||||
|     }) |     } | ||||||
|  | 
 | ||||||
|  |     fn as_json_mut(&mut self, (group_name, file_name): FileID) -> Option<&mut serde_json::Value> { | ||||||
|  |         match self.group_index(group_name) { | ||||||
|  |             Some(index) => self.groups[index].files.get_mut(&file_name.to_owned()), | ||||||
|  |             _ => None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn as_json(&self, (group_name, file_name): FileID) -> Option<&serde_json::Value> { | ||||||
|  |         match self.group_index(group_name) { | ||||||
|  |             Some(index) => self.groups[index].files.get(&file_name.to_owned()), | ||||||
|  |             _ => None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn as_string(&self, file_id: FileID) -> Option<String> { | ||||||
|  |         self.as_json(file_id).map(|x| x.to_string()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_json(&self, file_id: FileID, pointer: &str) -> Option<&serde_json::Value> { | ||||||
|  |         match self.as_json(file_id) { | ||||||
|  |             Some(v) => v.pointer(pointer), | ||||||
|  |             _ => None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_string(&self, file_id: FileID, pointer: &str) -> Option<String> { | ||||||
|  |         self.get_json(file_id, pointer).map(|x| x.to_string()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn contains(&self, file_id: FileID, pointer: &str) -> bool { | ||||||
|  |         self.get_json(file_id, pointer) != None | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_json( | ||||||
|  |         &mut self, | ||||||
|  |         (group_name, file_name): FileID, | ||||||
|  |         pointer: &str, | ||||||
|  |         new_value: serde_json::Value, | ||||||
|  |     ) -> Result<(), Box<dyn Error>> { | ||||||
|  |         let file_json = match self.as_json_mut((group_name, file_name)) { | ||||||
|  |             Some(file_json) => file_json, | ||||||
|  |             _ => { | ||||||
|  |                 return Err(Box::new(DBError::NoFile { | ||||||
|  |                     group_name: group_name.to_owned(), | ||||||
|  |                     file_name: file_name.to_owned(), | ||||||
|  |                 })) | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         touch(file_json, pointer)?; | ||||||
|  |         match file_json.pointer_mut(pointer) { | ||||||
|  |             Some(v) => *v = new_value, | ||||||
|  |             _ => { | ||||||
|  |                 //  If after `touch()` call we still don't have an existing value -
 | ||||||
|  |                 //  something is wrong with the `pointer`
 | ||||||
|  |                 return Err(Box::new(DBError::IncorrectPointer { | ||||||
|  |                     pointer: pointer.to_owned(), | ||||||
|  |                 })); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn remove( | ||||||
|  |         &mut self, | ||||||
|  |         (group_name, file_name): FileID, | ||||||
|  |         pointer: &str, | ||||||
|  |     ) -> Result<(), Box<dyn Error>> { | ||||||
|  |         let file_json = match self.as_json_mut((group_name, file_name)) { | ||||||
|  |             Some(file_json) => file_json, | ||||||
|  |             _ => { | ||||||
|  |                 return Err(Box::new(DBError::NoFile { | ||||||
|  |                     group_name: group_name.to_owned(), | ||||||
|  |                     file_name: file_name.to_owned(), | ||||||
|  |                 })) | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         match pointer_to_reference(file_json, pointer) { | ||||||
|  |             Some(ValueReference::Object(map, variable_name)) => { | ||||||
|  |                 map.remove(&variable_name); | ||||||
|  |             } | ||||||
|  |             Some(ValueReference::Array(vec, variable_index)) => { | ||||||
|  |                 if variable_index < vec.len() { | ||||||
|  |                     vec.remove(variable_index); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             _ => { | ||||||
|  |                 return Err(Box::new(DBError::IncorrectPointer { | ||||||
|  |                     pointer: pointer.to_owned(), | ||||||
|  |                 })) | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn load_group(group_path: &path::Path) -> Result<Group, Box<dyn Error>> { | fn load_group(group_path: &path::Path) -> Result<Group, Box<dyn Error>> { | ||||||
|     let mut files = Vec::new(); |     let mut files = HashMap::new(); | ||||||
|     for entry in fs::read_dir(group_path)? { |     for entry in fs::read_dir(group_path)? { | ||||||
|         let entry = entry?; |         let entry = entry?; | ||||||
|         let path = entry.path(); |         let path = entry.path(); | ||||||
| @ -123,10 +207,9 @@ fn load_group(group_path: &path::Path) -> Result<Group, Box<dyn Error>> { | |||||||
|             ); |             ); | ||||||
|         } else { |         } else { | ||||||
|             let file_contents = fs::read_to_string(&path)?; |             let file_contents = fs::read_to_string(&path)?; | ||||||
|             files.push(File { |             let file_name = get_file_name(path.as_path()); | ||||||
|                 name: get_file_name(path.as_path()), |             let file_contents = serde_json::from_str(&file_contents)?; | ||||||
|                 contents: serde_json::from_str(&file_contents)?, |             files.insert(file_name, file_contents); | ||||||
|             }); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     Ok(Group { |     Ok(Group { | ||||||
| @ -135,9 +218,87 @@ fn load_group(group_path: &path::Path) -> Result<Group, Box<dyn Error>> { | |||||||
|     }) |     }) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn touch(json_root: &mut serde_json::Value, pointer: &str) -> (Result<(), Box<dyn Error>>) { | ||||||
|  |     if pointer.is_empty() || json_root.pointer_mut(pointer).is_some() { | ||||||
|  |         return Ok(()); | ||||||
|  |     } | ||||||
|  |     match pointer_to_reference(json_root, pointer) { | ||||||
|  |         Some(ValueReference::Object(map, variable_name)) => { | ||||||
|  |             map.insert(variable_name, json!(null)); | ||||||
|  |         } | ||||||
|  |         Some(ValueReference::Array(vec, variable_index)) => { | ||||||
|  |             //  Since values at the index does not exist - resize will increase the sizeof the array
 | ||||||
|  |             vec.resize(variable_index + 1, json!(null)); | ||||||
|  |         } | ||||||
|  |         _ => { | ||||||
|  |             return Err(Box::new(DBError::IncorrectPointer { | ||||||
|  |                 pointer: pointer.to_owned(), | ||||||
|  |             })) | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn pointer_to_reference<'a>( | ||||||
|  |     json_root: &'a mut serde_json::Value, | ||||||
|  |     pointer: &str, | ||||||
|  | ) -> Option<ValueReference<'a>> { | ||||||
|  |     if pointer.is_empty() { | ||||||
|  |         return None; | ||||||
|  |     } | ||||||
|  |     let container_variable_pair = | ||||||
|  |         pop_json_pointer(pointer).and_then(move |(path, variable_name)| { | ||||||
|  |             match json_root.pointer_mut(&path) { | ||||||
|  |                 Some(v) => Some((v, variable_name)), | ||||||
|  |                 _ => None, | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     let (json_container, variable_name) = match container_variable_pair { | ||||||
|  |         Some(v) => v, | ||||||
|  |         _ => return None, | ||||||
|  |     }; | ||||||
|  |     match json_container { | ||||||
|  |         serde_json::Value::Object(map) => Some(ValueReference::Object(map, variable_name)), | ||||||
|  |         serde_json::Value::Array(vec) => { | ||||||
|  |             let index: usize = match variable_name.parse() { | ||||||
|  |                 Ok(v) => v, | ||||||
|  |                 _ => return None, | ||||||
|  |             }; | ||||||
|  |             Some(ValueReference::Array(vec, index)) | ||||||
|  |         } | ||||||
|  |         _ => None, | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn pop_json_pointer(pointer: &str) -> Option<(String, String)> { | ||||||
|  |     let mut pointer = pointer.to_string(); | ||||||
|  |     let last_separator_index = match pointer.rfind(JSON_POINTER_SEPARATOR) { | ||||||
|  |         Some(v) => v, | ||||||
|  |         _ => { | ||||||
|  |             return None; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     if last_separator_index >= pointer.len() { | ||||||
|  |         pointer.pop(); | ||||||
|  |         return Some((pointer, String::new())); | ||||||
|  |     } | ||||||
|  |     let var_name = pointer.split_off(last_separator_index + 1); | ||||||
|  |     pointer.pop(); | ||||||
|  |     Some((pointer, var_name)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| fn get_file_name(path: &path::Path) -> String { | fn get_file_name(path: &path::Path) -> String { | ||||||
|     path.file_stem() |     path.file_stem() | ||||||
|         .and_then(|x| x.to_str()) |         .and_then(|x| x.to_str()) | ||||||
|         .unwrap_or_default() |         .unwrap_or_default() | ||||||
|         .to_string() |         .to_string() | ||||||
| } | } | ||||||
|  | // TODO add tests for remove
 | ||||||
|  | // TODO add tests for panics (both add and remove)
 | ||||||
|  | // TODO add file addition/removal
 | ||||||
|  | // TODO add db saving
 | ||||||
|  | 
 | ||||||
|  | // TODO make sure file's main value not being an object won't break anything
 | ||||||
|  | // TODO check that file name is appropriate
 | ||||||
|  | // TODO handle parsing errors differently
 | ||||||
|  | // TODO add logs
 | ||||||
|  | |||||||
							
								
								
									
										160
									
								
								src/database/tests.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								src/database/tests.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,160 @@ | |||||||
|  | use super::*; | ||||||
|  | use serde_json::json; | ||||||
|  | use std::path; | ||||||
|  | 
 | ||||||
|  | const TEST_DB_PATH: &str = "./fixtures/database"; | ||||||
|  | 
 | ||||||
|  | const NO_DB_MESSAGE: &str = "Can not find/load test database"; | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn group_names() { | ||||||
|  |     let db = Database::new(path::Path::new(TEST_DB_PATH)).expect(NO_DB_MESSAGE); | ||||||
|  | 
 | ||||||
|  |     let names = db.group_names(); | ||||||
|  |     assert!(names.contains(&"administration".to_owned())); | ||||||
|  |     assert!(names.contains(&"game".to_owned())); | ||||||
|  |     assert_eq!(names.len(), 2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn file_names() { | ||||||
|  |     let db = Database::new(path::Path::new(TEST_DB_PATH)).expect(NO_DB_MESSAGE); | ||||||
|  | 
 | ||||||
|  |     let names_admin = db.file_names_in("administration"); | ||||||
|  |     let names_game = db.file_names_in("game"); | ||||||
|  |     assert!(names_admin.contains(&"registered".to_owned())); | ||||||
|  |     assert!(names_game.contains(&"general".to_owned())); | ||||||
|  |     assert!(names_game.contains(&"perks".to_owned())); | ||||||
|  |     assert_eq!(names_admin.len(), 1); | ||||||
|  |     assert_eq!(names_game.len(), 2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn db_json_contents() { | ||||||
|  |     let db = Database::new(path::Path::new(TEST_DB_PATH)).expect(NO_DB_MESSAGE); | ||||||
|  |     let registered = db.as_json(("administration", "registered")).unwrap(); | ||||||
|  |     let user_map = registered | ||||||
|  |         .as_object() | ||||||
|  |         .expect("Read value is not an object."); | ||||||
|  |     assert_eq!(user_map.len(), 2); | ||||||
|  |     assert!(user_map.contains_key("76561198025127722")); | ||||||
|  |     let user_record = user_map | ||||||
|  |         .get("76561198044316328") | ||||||
|  |         .unwrap() | ||||||
|  |         .as_object() | ||||||
|  |         .unwrap(); | ||||||
|  |     assert_eq!(user_record.len(), 3); | ||||||
|  |     assert_eq!(user_record.get("ip_lock").unwrap().as_bool(), Some(false)); | ||||||
|  |     assert_eq!( | ||||||
|  |         user_record.get("password_hash").unwrap().as_str(), | ||||||
|  |         Some("fce798e0804dfb217f929bdba26745024f37f6b6ba7406f3775176e20dd5089d") | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     let groups_arrays = user_record.get("groups").unwrap().as_array().unwrap(); | ||||||
|  |     assert_eq!(groups_arrays.len(), 1); | ||||||
|  |     assert_eq!(groups_arrays[0], "admin"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn db_string_contents() { | ||||||
|  |     let db = Database::new(path::Path::new(TEST_DB_PATH)).expect(NO_DB_MESSAGE); | ||||||
|  |     let general = db.as_string(("game", "general")).unwrap(); | ||||||
|  |     assert!(general.contains(r#""dosh_thrown":527624"#)); | ||||||
|  |     assert!(general | ||||||
|  |         .contains(r#""achievements":["kf:LabCleaner","kf:ChickenFarmer","scrn:playedscrn"]"#)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn db_json_sub_contents() { | ||||||
|  |     let db = Database::new(path::Path::new(TEST_DB_PATH)).expect(NO_DB_MESSAGE); | ||||||
|  |     //  Test empty path
 | ||||||
|  |     let file_id = ("administration", "registered"); | ||||||
|  |     assert_eq!(db.as_json(file_id), db.get_json(file_id, "")); | ||||||
|  |     //  Test complex path
 | ||||||
|  |     let received = db | ||||||
|  |         .get_json(file_id, "/76561198025127722/allowed_ips/1") | ||||||
|  |         .unwrap(); | ||||||
|  |     assert_eq!(received.as_str().unwrap(), "192.168.0.100"); | ||||||
|  |     //  Test bad paths
 | ||||||
|  |     assert!(db.get_json(file_id, "/777") == None); | ||||||
|  |     assert!(db.get_json(file_id, "/76561198025127722/allowed_ips/2") == None); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn db_string_sub_contents() { | ||||||
|  |     let db = Database::new(path::Path::new(TEST_DB_PATH)).expect(NO_DB_MESSAGE); | ||||||
|  |     //  Test empty path
 | ||||||
|  |     let file_id = ("administration", "registered"); | ||||||
|  |     assert_eq!(db.as_string(file_id), db.get_string(file_id, "")); | ||||||
|  |     //  Test complex path
 | ||||||
|  |     let received = db | ||||||
|  |         .get_string(file_id, "/76561198025127722/allowed_ips/0") | ||||||
|  |         .unwrap(); | ||||||
|  |     assert_eq!(received, r#""127.0.0.1""#); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn db_contains_check() { | ||||||
|  |     let db = Database::new(path::Path::new(TEST_DB_PATH)).expect(NO_DB_MESSAGE); | ||||||
|  |     let registered_id = ("administration", "registered"); | ||||||
|  |     let perks_id = ("game", "perks"); | ||||||
|  |     //  These exist
 | ||||||
|  |     assert!(db.contains(registered_id, "/76561198025127722/password_hash")); | ||||||
|  |     assert!(db.contains(registered_id, "/76561198044316328/groups")); | ||||||
|  |     assert!(db.contains(perks_id, "/76561198025127722/headshots")); | ||||||
|  |     assert!(db.contains(perks_id, "/76561198044316328")); | ||||||
|  |     //  These do not exist
 | ||||||
|  |     assert!(!db.contains(registered_id, "/76561198025127722/password/")); | ||||||
|  |     assert!(!db.contains(registered_id, "/76561198044316328/groups/2")); | ||||||
|  |     assert!(!db.contains(perks_id, "/76561198025127722/assault_rifle_damage/9067")); | ||||||
|  |     assert!(!db.contains(perks_id, "/76561198044316328/headshots")); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn db_set_success() -> Result<(), Box<dyn Error>> { | ||||||
|  |     let mut db = Database::new(path::Path::new(TEST_DB_PATH)).expect(NO_DB_MESSAGE); | ||||||
|  |     let file_id = ("administration", "registered"); | ||||||
|  |     //  Modify existing
 | ||||||
|  |     db.set_json(file_id, "/76561198025127722/ip_lock", json!(false))?; | ||||||
|  |     assert_eq!( | ||||||
|  |         db.get_string(file_id, "/76561198025127722/ip_lock") | ||||||
|  |             .unwrap(), | ||||||
|  |         "false" | ||||||
|  |     ); | ||||||
|  |     db.set_json( | ||||||
|  |         file_id, | ||||||
|  |         "/76561198044316328/password_hash", | ||||||
|  |         json!({"var":13524}), | ||||||
|  |     )?; | ||||||
|  |     assert_eq!( | ||||||
|  |         db.get_string(file_id, "/76561198044316328/password_hash") | ||||||
|  |             .unwrap(), | ||||||
|  |         r#"{"var":13524}"# | ||||||
|  |     ); | ||||||
|  |     //  Reset whole file
 | ||||||
|  |     db.set_json(file_id, "", json!({}))?; | ||||||
|  |     assert_eq!(db.as_json(file_id).unwrap().to_string(), "{}"); | ||||||
|  |     //  Add new values
 | ||||||
|  |     db.set_json(file_id, "/new_var", json!([42, {"word":"life"}, null]))?; | ||||||
|  |     assert_eq!( | ||||||
|  |         db.as_json(file_id).unwrap().to_string(), | ||||||
|  |         r#"{"new_var":[42,{"word":"life"},null]}"# | ||||||
|  |     ); | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn test_pop_json_pointer() { | ||||||
|  |     assert_eq!( | ||||||
|  |         pop_json_pointer("/a/b/c/d"), | ||||||
|  |         Some(("/a/b/c".to_owned(), "d".to_owned())) | ||||||
|  |     ); | ||||||
|  |     assert_eq!(pop_json_pointer("/"), Some(("".to_owned(), "".to_owned()))); | ||||||
|  |     assert_eq!( | ||||||
|  |         pop_json_pointer("/a/b/"), | ||||||
|  |         Some(("/a/b".to_owned(), "".to_owned())) | ||||||
|  |     ); | ||||||
|  |     assert_eq!(pop_json_pointer(""), None); | ||||||
|  |     //  This pointer is incorrect
 | ||||||
|  |     assert_eq!(pop_json_pointer("var"), None); | ||||||
|  | } | ||||||
| @ -5,8 +5,8 @@ mod database; | |||||||
| fn main() { | fn main() { | ||||||
|     let args: Vec<String> = env::args().collect(); |     let args: Vec<String> = env::args().collect(); | ||||||
|     let filename = &args[1]; |     let filename = &args[1]; | ||||||
|     let config = database::Database::new(Path::new(filename)); |     let db = database::Database::new(Path::new(filename)); | ||||||
|     match config { |     match db { | ||||||
|         Ok(db) => print!("{}", db), |         Ok(db) => print!("{}", db), | ||||||
|         Err(error) => println!("OH NO: {}", error), |         Err(error) => println!("OH NO: {}", error), | ||||||
|     } |     } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user