Skip to content

Commit

Permalink
fs-storage : Add FolderStorage struct (ARK-Builders#81)
Browse files Browse the repository at this point in the history
* init

Signed-off-by: pushkarm029 <[email protected]>

* fix

Signed-off-by: pushkarm029 <[email protected]>

* fix

Signed-off-by: pushkarm029 <[email protected]>

* fix

Signed-off-by: pushkarm029 <[email protected]>

* added tests

Signed-off-by: pushkarm029 <[email protected]>

* fix

Signed-off-by: pushkarm029 <[email protected]>

* fix

Signed-off-by: pushkarm029 <[email protected]>

* few fixes

Signed-off-by: pushkarm029 <[email protected]>

* fmt

Signed-off-by: pushkarm029 <[email protected]>

* Add property based testing for FolderStorage (ARK-Builders#86)

* Add property test

* Use print statements

* experimental changes

Signed-off-by: pushkarm029 <[email protected]>

* fix

Signed-off-by: pushkarm029 <[email protected]>

* fix

Signed-off-by: pushkarm029 <[email protected]>

* Refactor to remove unwraps

* Fix folderstorage model with strong deletes

---------

Signed-off-by: pushkarm029 <[email protected]>
Co-authored-by: pushkarm029 <[email protected]>

* Fix formatting

* fix

Signed-off-by: pushkarm029 <[email protected]>

* fix

Signed-off-by: pushkarm029 <[email protected]>

* remove bincode

Signed-off-by: pushkarm029 <[email protected]>

* Change folderstorage file extension to json

* fix

Signed-off-by: pushkarm029 <[email protected]>

---------

Signed-off-by: pushkarm029 <[email protected]>
Co-authored-by: Ishan Bhanuka <[email protected]>
  • Loading branch information
Pushkarm029 and twitu authored Sep 2, 2024
1 parent 06fadc6 commit 6c4d8a0
Show file tree
Hide file tree
Showing 7 changed files with 935 additions and 21 deletions.
3 changes: 2 additions & 1 deletion fs-storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ serde_json = "1.0.82"
serde = { version = "1.0.138", features = ["derive"] }
jni = { version = "0.21.1", optional = true }
jnix = { version = "0.5.1", features = ["derive"], optional = true }

data-error = { path = "../data-error" }


[dev-dependencies]
anyhow = "1.0.81"
quickcheck = { version = "1.0.3", features = ["use_logging"] }
quickcheck_macros = "1.0.0"
tempdir = "0.3.7"

[features]
Expand Down
12 changes: 8 additions & 4 deletions fs-storage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,26 @@ File system storage implementation for writing key value pairs to disk.
}
```

- Select between two storage options:
- file: Stores multiple key-value pairs in a single file.
- folder: Stores each value in a separate file within a folder.

- Run Write Command

```bash
cargo run --example cli write /tmp/z test.json
cargo run --example cli [file|folder] write /tmp/z test.json
```

Alternatively, you can directly provide the input data as a comma-separated list of key-value pairs

```bash
cargo run --example cli write /tmp/z a:1,b:2,c:3
cargo run --example cli [file|folder] write /tmp/z a:1,b:2,c:3
```

- Run Read Command

```bash
cargo run --example cli read /tmp/z key1,key2
cargo run --example cli [file|folder] read /tmp/z key1,key2
```

- Get Output
Expand All @@ -42,5 +46,5 @@ key2: value2
- To get all key value pairs

```bash
cargo run --example cli read /tmp/z
cargo run --example cli [file|folder] read /tmp/z
```
128 changes: 114 additions & 14 deletions fs-storage/examples/cli.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use anyhow::{Context, Result};
use fs_storage::{base_storage::BaseStorage, file_storage::FileStorage};
use fs_storage::{
base_storage::BaseStorage, file_storage::FileStorage,
folder_storage::FolderStorage,
};
use serde_json::Value;
use std::{env, fs, path::Path};

Expand All @@ -13,26 +16,41 @@ fn run() -> Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
println!("Usage:");
println!(" cargo run --example cli write <path> [JSON_FILE_PATH | KEY_VALUE_PAIRS]");
println!(" cargo run --example cli read <path> <key1,key2,...>");
println!(" cargo run --example cli [file|folder] write <path> [JSON_FILE_PATH | KEY_VALUE_PAIRS]");
println!(" cargo run --example cli [file|folder] read <path> <key1,key2,...>");
return Ok(());
}

let command = &args[1];
let path = &args[2];
match command.as_str() {
"read" => read_command(&args, path),
"write" => write_command(&args, path),
let storage_type = &args[1];
let command = &args[2];
let path = &args[3];
match storage_type.as_str() {
"file" => match command.as_str() {
"read" => file_read_command(&args, path),
"write" => file_write_command(&args, path),
_ => {
eprintln!("Invalid command. Use 'read' or 'write'.");
Ok(())
}
},
"folder" => match command.as_str() {
"read" => folder_read_command(&args, path),
"write" => folder_write_command(&args, path),
_ => {
eprintln!("Invalid command. Use 'read' or 'write'.");
Ok(())
}
},
_ => {
eprintln!("Invalid command. Use 'read' or 'write'.");
eprintln!("Invalid storage. Use 'file' or 'folder'.");
Ok(())
}
}
}

fn read_command(args: &[String], path: &str) -> Result<()> {
fn file_read_command(args: &[String], path: &str) -> Result<()> {
let keys = if args.len() > 3 {
args[3]
args[4]
.split(',')
.map(|s| s.to_string())
.collect::<Vec<String>>()
Expand Down Expand Up @@ -63,13 +81,13 @@ fn read_command(args: &[String], path: &str) -> Result<()> {
Ok(())
}

fn write_command(args: &[String], path: &str) -> Result<()> {
fn file_write_command(args: &[String], path: &str) -> Result<()> {
if args.len() < 4 {
println!("Usage: cargo run --example cli write <path> [JSON_FILE_PATH | KEY_VALUE_PAIRS]");
println!("Usage: cargo run --example cli file write <path> [JSON_FILE_PATH | KEY_VALUE_PAIRS]");
return Ok(());
}

let content = &args[3];
let content = &args[4];
// Check if the content is a JSON file path
let content_json = Path::new(content)
.extension()
Expand Down Expand Up @@ -107,5 +125,87 @@ fn write_command(args: &[String], path: &str) -> Result<()> {
}
}
}
fs.write_fs().expect("Failed to write to file");
Ok(())
}

fn folder_read_command(args: &[String], path: &str) -> Result<()> {
let keys = if args.len() > 4 {
args[4]
.split(',')
.map(|s| s.to_string())
.collect::<Vec<String>>()
} else {
vec![]
};

let mut fs: FolderStorage<String, String> =
FolderStorage::new("cli".to_string(), Path::new(path))
.context("Failed to create FolderStorage")?;

let map = fs
.read_fs()
.expect("No Data is present on this path");
if keys.is_empty() {
for (key, value) in map {
println!("{}: {}", key, value);
}
}
for key in &keys {
if let Some(value) = map.get(key) {
println!("{}: {}", key, value);
} else {
eprintln!("Key '{}' not found", key);
}
}

Ok(())
}

fn folder_write_command(args: &[String], path: &str) -> Result<()> {
if args.len() < 4 {
println!("Usage: cargo run --example cli folder write <path> [JSON_FILE_PATH | KEY_VALUE_PAIRS]");
return Ok(());
}

let content = &args[4];
// Check if the content is a JSON file path
let content_json = Path::new(content)
.extension()
.map_or(false, |ext| ext == "json");

let mut fs: FolderStorage<String, String> =
FolderStorage::new("cli".to_string(), Path::new(path))
.context("Failed to create FolderStorage")?;
if content_json {
let content =
fs::read_to_string(content).context("Failed to read JSON file")?;
let json: Value =
serde_json::from_str(&content).context("Failed to parse JSON")?;
if let Value::Object(object) = json {
for (key, value) in object {
if let Value::String(value_str) = value {
fs.set(key, value_str);
} else {
println!(
"Warning: Skipping non-string value for key '{}'",
key
);
}
}
} else {
println!("JSON value is not an object");
return Ok(());
}
} else {
let pairs = content.split(',');
for pair in pairs {
let kv: Vec<&str> = pair.split(':').collect();
if kv.len() == 2 {
fs.set(kv[0].to_string(), kv[1].to_string());
}
}
}
fs.write_fs().expect("Failed to write to folder");
Ok(())
}
2 changes: 1 addition & 1 deletion fs-storage/src/base_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub trait BaseStorage<K, V>: AsRef<BTreeMap<K, V>> {
fn remove(&mut self, id: &K) -> Result<()>;

/// Get [`SyncStatus`] of the storage
fn sync_status(&self) -> Result<SyncStatus>;
fn sync_status(&mut self) -> Result<SyncStatus>;

/// Sync the in-memory storage with the storage on disk
fn sync(&mut self) -> Result<()>;
Expand Down
2 changes: 1 addition & 1 deletion fs-storage/src/file_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ where
/// Compare the timestamp of the storage file
/// with the timestamp of the in-memory storage and the last written
/// to time to determine if either of the two requires syncing.
fn sync_status(&self) -> Result<SyncStatus> {
fn sync_status(&mut self) -> Result<SyncStatus> {
let file_updated = fs::metadata(&self.path)?.modified()?;

// Determine the synchronization status based on the modification times
Expand Down
Loading

0 comments on commit 6c4d8a0

Please sign in to comment.