Document database's file.rs
This commit is contained in:
parent
64b5553863
commit
2701729dcf
@ -9,14 +9,28 @@ const JSON_POINTER_SEPARATOR: &str = "/";
|
|||||||
|
|
||||||
custom_error! { pub IncorrectPointer{pointer: String} = "Incorrect pointer is specified: {pointer}" }
|
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<String, serde_json::Value>` and name of referred value.
|
||||||
|
///
|
||||||
|
/// For values inside JSON arrays it stores array's
|
||||||
|
/// `Vec<serde_json::Value>` and referred index.
|
||||||
|
///
|
||||||
|
/// `Invalid` can be used to return a failed state.
|
||||||
enum ValueReference<'a> {
|
enum ValueReference<'a> {
|
||||||
Object(&'a mut serde_json::Map<String, serde_json::Value>, String),
|
Object(&'a mut serde_json::Map<String, serde_json::Value>, String),
|
||||||
Array(&'a mut Vec<serde_json::Value>, usize),
|
Array(&'a mut Vec<serde_json::Value>, usize),
|
||||||
Invalid,
|
Invalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements database's file by wrapping JSON value (`serde_json::Value`)
|
||||||
|
/// and providing several convenient accessor methods.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct File {
|
pub struct File {
|
||||||
|
/// File's full contents, normally a JSON object.
|
||||||
contents: serde_json::Value,
|
contents: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,30 +41,49 @@ impl ToString for File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
|
/// Creates an empty file that will contain an empty JSON object.
|
||||||
pub fn empty() -> File {
|
pub fn empty() -> File {
|
||||||
File {
|
File {
|
||||||
contents: json!({}),
|
contents: json!({}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(file_contents: String) -> Result<File, Box<dyn Error>> {
|
/// Loads JSON value from the specified file.
|
||||||
|
pub fn load(file_contents: String) -> Result<File, Box<dyn Error>> {
|
||||||
Ok(File {
|
Ok(File {
|
||||||
contents: serde_json::from_str(&file_contents)?,
|
contents: serde_json::from_str(&file_contents)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns file's "root", - JSON value contained inside it.
|
||||||
pub fn root(&self) -> &serde_json::Value {
|
pub fn root(&self) -> &serde_json::Value {
|
||||||
&self.contents
|
&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> {
|
pub fn get(&self, pointer: &str) -> Option<&serde_json::Value> {
|
||||||
self.contents.pointer(pointer)
|
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 {
|
pub fn contains(&self, pointer: &str) -> bool {
|
||||||
self.get(pointer) != None
|
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(
|
pub fn insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
pointer: &str,
|
pointer: &str,
|
||||||
@ -70,6 +103,8 @@ impl File {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes (and returns) value specified by theJSON pointer.
|
||||||
|
/// If it did not exist - returns `None`.
|
||||||
pub fn remove(&mut self, pointer: &str) -> Option<serde_json::Value> {
|
pub fn remove(&mut self, pointer: &str) -> Option<serde_json::Value> {
|
||||||
match self.pointer_to_reference(pointer) {
|
match self.pointer_to_reference(pointer) {
|
||||||
ValueReference::Object(map, variable_name) => map.remove(&variable_name),
|
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>) {
|
fn touch(&mut self, pointer: &str) -> (Result<(), IncorrectPointer>) {
|
||||||
// If value is present - we're done
|
// If value is present - we're done
|
||||||
if pointer.is_empty() || self.contents.pointer_mut(pointer).is_some() {
|
if pointer.is_empty() || self.contents.pointer_mut(pointer).is_some() {
|
||||||
@ -108,6 +147,7 @@ impl File {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper method, - converts JSON pointer into auxiliary `ValueReference` enum.
|
||||||
fn pointer_to_reference<'a>(&'a mut self, pointer: &str) -> ValueReference<'a> {
|
fn pointer_to_reference<'a>(&'a mut self, pointer: &str) -> ValueReference<'a> {
|
||||||
if pointer.is_empty() {
|
if pointer.is_empty() {
|
||||||
return ValueReference::Invalid;
|
return ValueReference::Invalid;
|
||||||
@ -145,6 +185,7 @@ impl File {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to disassemble JSON path.
|
||||||
fn pop_json_pointer(pointer: &str) -> Option<(String, String)> {
|
fn pop_json_pointer(pointer: &str) -> Option<(String, String)> {
|
||||||
let mut pointer = pointer.to_string();
|
let mut pointer = pointer.to_string();
|
||||||
let last_separator_index = match pointer.rfind(JSON_POINTER_SEPARATOR) {
|
let last_separator_index = match pointer.rfind(JSON_POINTER_SEPARATOR) {
|
||||||
|
@ -121,7 +121,7 @@ fn read_group(group_path: &path::Path) -> Result<Option<Group>, Box<dyn Error>>
|
|||||||
}
|
}
|
||||||
let file_name = get_file_name(path.as_path());
|
let file_name = get_file_name(path.as_path());
|
||||||
let file_contents = fs::read_to_string(&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 {
|
if files.len() > 0 {
|
||||||
return Ok(Some(Group {
|
return Ok(Some(Group {
|
||||||
|
@ -17,7 +17,8 @@ pub mod io;
|
|||||||
custom_error! { pub DBError
|
custom_error! { pub DBError
|
||||||
InvalidEntityName{entity_name: String} = r#"Cannot use {entity_name} for file or group"#,
|
InvalidEntityName{entity_name: String} = r#"Cannot use {entity_name} for file or group"#,
|
||||||
NoGroup{group_name: String} = r#"Group "{group_name}" does not exist"#,
|
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"#,
|
GroupAlreadyExists{group_name: String} = r#"Group "{group_name}" already exists"#,
|
||||||
FileAlreadyExists{group_name: String, file_name: String} = r#"File "{file_name}" already exists\
|
FileAlreadyExists{group_name: String, file_name: String} = r#"File "{file_name}" already exists\
|
||||||
in the group "{group_name}""#,
|
in the group "{group_name}""#,
|
||||||
|
Loading…
Reference in New Issue
Block a user