Skip to content

Commit

Permalink
#1: Add FileStorage struct (ARK-Builders#10)
Browse files Browse the repository at this point in the history
* Add FileStorage logic, example and documentation

Co-Authored-by: Ishan Bhanuka <[email protected]>
Co-Authored-by: Pushkar Mishra <[email protected]>
Co-Authored-by: Tarek <[email protected]>
Co-Authored-by: Kirill Taran <[email protected]>

* refactor done

Signed-off-by: Pushkar Mishra <[email protected]>

* fix cargo.toml

Signed-off-by: Pushkar Mishra <[email protected]>

* Update fs-storage/src/file_storage.rs

Co-authored-by: Tarek Elsayed <[email protected]>

* Update fs-storage/src/file_storage.rs

Co-authored-by: Tarek Elsayed <[email protected]>

* Update fs-storage/src/file_storage.rs

Co-authored-by: Tarek Elsayed <[email protected]>

* Add doc comment for erase

* feat(fs-storage): refactor CLI write cmd to accept key-value pairs

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

---------

Signed-off-by: Pushkar Mishra <[email protected]>
Signed-off-by: Tarek <[email protected]>
Co-authored-by: Pushkar Mishra <[email protected]>
Co-authored-by: Tarek <[email protected]>
Co-authored-by: Kirill Taran <[email protected]>
Co-authored-by: Tarek Elsayed <[email protected]>
  • Loading branch information
5 people committed Apr 6, 2024
1 parent 696a68d commit 077c021
Show file tree
Hide file tree
Showing 8 changed files with 414 additions and 2 deletions.
17 changes: 16 additions & 1 deletion data-error/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::str::Utf8Error;
use std::{convert::Infallible, str::Utf8Error};
use thiserror::Error;

pub type Result<T> = std::result::Result<T, ArklibError>;
Expand All @@ -15,6 +15,9 @@ pub enum ArklibError {
Parse,
#[error("Networking error")]
Network,
/// Storage error shows label and error message
#[error("Storage error: {0} {1}")]
Storage(String, String),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
Expand Down Expand Up @@ -48,3 +51,15 @@ impl From<Box<dyn std::error::Error>> for ArklibError {
Self::Other(anyhow::anyhow!(e.to_string()))
}
}

impl From<&str> for ArklibError {
fn from(e: &str) -> Self {
Self::Other(anyhow::anyhow!(e.to_string()))
}
}

impl From<Infallible> for ArklibError {
fn from(_: Infallible) -> Self {
Self::Parse
}
}
1 change: 0 additions & 1 deletion data-resource/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use anyhow::anyhow;
use crc32fast::Hasher;
use log;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
use std::fs;
Expand Down
11 changes: 11 additions & 0 deletions fs-storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,15 @@ name = "fs_storage"
crate-type = ["rlib"]
bench = false

[[example]]
name = "cli"

[dependencies]
data-error = { path = "../data-error" }
log = { version = "0.4.17", features = ["release_max_level_off"] }
serde_json = "1.0.82"
serde = { version = "1.0.138", features = ["derive"] }

[dev-dependencies]
anyhow = "1.0.81"
tempdir = "0.3.7"
46 changes: 46 additions & 0 deletions fs-storage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Ark file system storage

File system storage implementation for writing key value pairs to disk.

## Steps to use CLI

- Create a test.json file of key:values pairs you want to store.

```json
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
```

- Run Write Command

```bash
cargo run --example cli 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
```

- Run Read Command

```bash
cargo run --example cli read /tmp/z key1,key2
```

- Get Output

```bash
key1: value1
key2: value2
```

- To get all key value pairs

```bash
cargo run --example cli read /tmp/z
```
115 changes: 115 additions & 0 deletions fs-storage/examples/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use anyhow::{Context, Result};
use fs_storage::file_storage::FileStorage;
use serde_json::Value;
use std::collections::BTreeMap;
use std::env;
use std::fs;
use std::path::Path;

fn main() {
if let Err(e) = run() {
eprintln!("Error: {}", e);
}
}

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,...>");
return Ok(());
}

let command = &args[1];
let path = &args[2];
match command.as_str() {
"read" => read_command(&args, path),
"write" => write_command(&args, path),
_ => {
eprintln!("Invalid command. Use 'read' or 'write'.");
Ok(())
}
}
}

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

let mut fs = FileStorage::new("cli".to_string(), Path::new(path));
let map: BTreeMap<String, String> =
fs.read_file().context("Failed to read file")?;

if keys.is_empty() {
for (key, value) in map {
println!("{}: {}", key, value);
}
} else {
for key in &keys {
if let Some(value) = map.get(key) {
println!("{}: {}", key, value);
} else {
eprintln!("Key '{}' not found", key);
}
}
}

Ok(())
}

fn 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]");
return Ok(());
}

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

let mut kv_pairs = BTreeMap::new();
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 {
kv_pairs.insert(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 {
kv_pairs.insert(kv[0].to_string(), kv[1].to_string());
}
}
}

let mut fs = FileStorage::new("cli".to_string(), Path::new(path));
fs.write_file(&kv_pairs)
.context("Failed to write file")?;

Ok(())
}
Loading

0 comments on commit 077c021

Please sign in to comment.