From 2701729dcf25b8aac9256ee6cf9c5ae49f927005 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Sat, 28 Nov 2020 04:42:26 +0700 Subject: [PATCH] Document database's file.rs --- src/database/file.rs | 43 ++++++++++++++++++++++++++++++++++++++++++- src/database/io.rs | 2 +- src/database/mod.rs | 3 ++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/database/file.rs b/src/database/file.rs index a1e010d..ccc0038 100644 --- a/src/database/file.rs +++ b/src/database/file.rs @@ -9,14 +9,28 @@ const JSON_POINTER_SEPARATOR: &str = "/"; custom_error! { pub IncorrectPointer{pointer: String} = "Incorrect pointer is specified: {pointer}" } +/// This is a enum that used internally to refer to values inside of +/// JSON (their serde implementation) objects and arrays. +/// This enum helps to simplify module's code. +/// +/// For values inside of JSON object it stores object's +/// `Map` and name of referred value. +/// +/// For values inside JSON arrays it stores array's +/// `Vec` and referred index. +/// +/// `Invalid` can be used to return a failed state. enum ValueReference<'a> { Object(&'a mut serde_json::Map, String), Array(&'a mut Vec, usize), Invalid, } +/// Implements database's file by wrapping JSON value (`serde_json::Value`) +/// and providing several convenient accessor methods. #[derive(Debug)] pub struct File { + /// File's full contents, normally a JSON object. contents: serde_json::Value, } @@ -27,30 +41,49 @@ impl ToString for File { } impl File { + /// Creates an empty file that will contain an empty JSON object. pub fn empty() -> File { File { contents: json!({}), } } - pub fn new(file_contents: String) -> Result> { + /// Loads JSON value from the specified file. + pub fn load(file_contents: String) -> Result> { Ok(File { contents: serde_json::from_str(&file_contents)?, }) } + /// Returns file's "root", - JSON value contained inside it. pub fn root(&self) -> &serde_json::Value { &self.contents } + /// Attempts to return JSON value, corresponding to the given JSON pointer. + /// `None` if the value is missing. pub fn get(&self, pointer: &str) -> Option<&serde_json::Value> { self.contents.pointer(pointer) } + /// Checks if values at a given JSON pointer exists. + /// Returns `true` if it does. pub fn contains(&self, pointer: &str) -> bool { self.get(pointer) != None } + /// Inserts new JSON value inside this file + /// (possibly in some sub-object/array). + /// + /// Given pointer must point at new value: + /// 1. If it already exists, - it will be overwritten. + /// 2. If it does not exist, but it's parent object/array does - + /// it will be added. + /// 3. Otherwise an error will be raise. + /// + /// If array needs to be expanded, - missing values will be filled + /// with `json!(null)`, i.e. inserting `7` at index `5` in array `[1, 2, 3]` + /// will produce `[1, 2, 3, null, null, 7]`. pub fn insert( &mut self, pointer: &str, @@ -70,6 +103,8 @@ impl File { Ok(()) } + /// Removes (and returns) value specified by theJSON pointer. + /// If it did not exist - returns `None`. pub fn remove(&mut self, pointer: &str) -> Option { match self.pointer_to_reference(pointer) { ValueReference::Object(map, variable_name) => map.remove(&variable_name), @@ -83,6 +118,10 @@ impl File { } } + /// Helper method to create a value if missing (as `json!(null)`). + /// Can only be done if parent container already exists. + /// + /// For specifics refer to `insert()` method. fn touch(&mut self, pointer: &str) -> (Result<(), IncorrectPointer>) { // If value is present - we're done if pointer.is_empty() || self.contents.pointer_mut(pointer).is_some() { @@ -108,6 +147,7 @@ impl File { Ok(()) } + /// Helper method, - converts JSON pointer into auxiliary `ValueReference` enum. fn pointer_to_reference<'a>(&'a mut self, pointer: &str) -> ValueReference<'a> { if pointer.is_empty() { return ValueReference::Invalid; @@ -145,6 +185,7 @@ impl File { } } +// Helper function to disassemble JSON path. 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) { diff --git a/src/database/io.rs b/src/database/io.rs index 62decb1..c5a2f6f 100644 --- a/src/database/io.rs +++ b/src/database/io.rs @@ -121,7 +121,7 @@ fn read_group(group_path: &path::Path) -> Result, Box> } let file_name = get_file_name(path.as_path()); let file_contents = fs::read_to_string(&path)?; - files.insert(file_name, File::new(file_contents)?); + files.insert(file_name, File::load(file_contents)?); } if files.len() > 0 { return Ok(Some(Group { diff --git a/src/database/mod.rs b/src/database/mod.rs index 659120b..72b52b7 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -17,7 +17,8 @@ pub mod io; custom_error! { pub DBError InvalidEntityName{entity_name: String} = r#"Cannot use {entity_name} for file or group"#, NoGroup{group_name: String} = r#"Group "{group_name}" does not exist"#, - NoFile{group_name: String, file_name: String} = r#"There is no "{file_name}" file in group "{group_name}""#, + NoFile{group_name: String, file_name: String} = r#"There is no "{file_name}" file in group\ + "{group_name}""#, GroupAlreadyExists{group_name: String} = r#"Group "{group_name}" already exists"#, FileAlreadyExists{group_name: String, file_name: String} = r#"File "{file_name}" already exists\ in the group "{group_name}""#,