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