Skip to content

Commit a7a3a94

Browse files
authored
Merge pull request #57 from CoLearn-Dev/storage-macro-fs
Storage Macro: file system
2 parents 7339794 + 404dbb9 commit a7a3a94

File tree

6 files changed

+156
-2
lines changed

6 files changed

+156
-2
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "colink"
3-
version = "0.3.4"
3+
version = "0.3.5"
44
edition = "2021"
55
description = "CoLink Rust SDK"
66
license = "MIT"

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ CoLink SDK helps both application and protocol developers access the functionali
99
Add this to your Cargo.toml:
1010
```toml
1111
[dependencies]
12-
colink = "0.3.4"
12+
colink = "0.3.5"
1313
```
1414

1515
## Getting Started

src/extensions/storage_macro.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod append;
22
mod chunk;
3+
mod fs;
34
mod redis;
45

56
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
@@ -39,6 +40,10 @@ impl crate::application::CoLink {
3940
self._create_entry_redis(&string_before, &string_after, payload)
4041
.await
4142
}
43+
"fs" => {
44+
self._create_entry_fs(&string_before, &string_after, payload)
45+
.await
46+
}
4247
_ => Err(format!(
4348
"invalid storage macro, found {} in key name {}",
4449
macro_type, key_name
@@ -52,6 +57,7 @@ impl crate::application::CoLink {
5257
match macro_type.as_str() {
5358
"chunk" => self._read_entry_chunk(&string_before).await,
5459
"redis" => self._read_entry_redis(&string_before, &string_after).await,
60+
"fs" => self._read_entry_fs(&string_before, &string_after).await,
5561
_ => Err(format!(
5662
"invalid storage macro, found {} in key name {}",
5763
macro_type, key_name
@@ -72,6 +78,10 @@ impl crate::application::CoLink {
7278
self._update_entry_redis(&string_before, &string_after, payload)
7379
.await
7480
}
81+
"fs" => {
82+
self._update_entry_fs(&string_before, &string_after, payload)
83+
.await
84+
}
7585
"append" => self._update_entry_append(&string_before, payload).await,
7686
_ => Err(format!(
7787
"invalid storage macro, found {} in key name {}",
@@ -89,6 +99,7 @@ impl crate::application::CoLink {
8999
self._delete_entry_redis(&string_before, &string_after)
90100
.await
91101
}
102+
"fs" => self._delete_entry_fs(&string_before, &string_after).await,
92103
_ => Err(format!(
93104
"invalid storage macro, found {} in key name {}",
94105
macro_type, key_name

src/extensions/storage_macro/append.rs

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ impl crate::application::CoLink {
2020
"chunk" => {
2121
return self._append_entry_chunk(&string_before, payload).await;
2222
}
23+
"fs" => {
24+
return self
25+
._append_entry_fs(&string_before, &string_after, payload)
26+
.await;
27+
}
2328
_ => {}
2429
}
2530
}

src/extensions/storage_macro/fs.rs

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use async_recursion::async_recursion;
2+
use std::path::PathBuf;
3+
use tokio::io::AsyncWriteExt;
4+
5+
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
6+
7+
impl crate::application::CoLink {
8+
async fn _sm_fs_get_path(
9+
&self,
10+
path_key_name: &str,
11+
path_suffix: &str,
12+
) -> Result<PathBuf, Error> {
13+
let path_key = format!("{}:path", path_key_name);
14+
let mut path = String::from_utf8(self.read_entry(&path_key).await?)?;
15+
if !path_suffix.is_empty() {
16+
let path_suffix = path_suffix.replace(':', "/");
17+
path += "/";
18+
path += &path_suffix;
19+
}
20+
println!("path: {}", path);
21+
let path = PathBuf::from(path);
22+
tokio::fs::create_dir_all(path.parent().unwrap()).await?;
23+
Ok(path)
24+
}
25+
26+
#[async_recursion]
27+
pub(crate) async fn _create_entry_fs(
28+
&self,
29+
path_key_name: &str,
30+
path_suffix: &str,
31+
payload: &[u8],
32+
) -> Result<String, Error> {
33+
let path = self._sm_fs_get_path(path_key_name, path_suffix).await?;
34+
let mut file = tokio::fs::OpenOptions::new()
35+
.write(true)
36+
.create_new(true)
37+
.open(path)
38+
.await?;
39+
file.write_all(payload).await?;
40+
Ok("ok".to_string())
41+
}
42+
43+
#[async_recursion]
44+
pub(crate) async fn _read_entry_fs(
45+
&self,
46+
path_key_name: &str,
47+
path_suffix: &str,
48+
) -> Result<Vec<u8>, Error> {
49+
let path = self._sm_fs_get_path(path_key_name, path_suffix).await?;
50+
let data = tokio::fs::read(path).await?;
51+
Ok(data)
52+
}
53+
54+
#[async_recursion]
55+
pub(crate) async fn _update_entry_fs(
56+
&self,
57+
path_key_name: &str,
58+
path_suffix: &str,
59+
payload: &[u8],
60+
) -> Result<String, Error> {
61+
let path = self._sm_fs_get_path(path_key_name, path_suffix).await?;
62+
let mut file = tokio::fs::File::create(path).await?;
63+
file.write_all(payload).await?;
64+
Ok("ok".to_string())
65+
}
66+
67+
#[async_recursion]
68+
pub(crate) async fn _append_entry_fs(
69+
&self,
70+
path_key_name: &str,
71+
path_suffix: &str,
72+
payload: &[u8],
73+
) -> Result<String, Error> {
74+
let path = self._sm_fs_get_path(path_key_name, path_suffix).await?;
75+
let lock_token = self.lock(&path.to_string_lossy()).await?;
76+
// use a closure to prevent locking forever caused by errors
77+
let res = async {
78+
let mut file = tokio::fs::OpenOptions::new()
79+
.append(true)
80+
.open(path)
81+
.await?;
82+
file.write_all(payload).await?;
83+
Ok::<String, Error>("ok".to_string())
84+
}
85+
.await;
86+
self.unlock(lock_token).await?;
87+
res
88+
}
89+
90+
#[async_recursion]
91+
pub(crate) async fn _delete_entry_fs(
92+
&self,
93+
path_key_name: &str,
94+
path_suffix: &str,
95+
) -> Result<String, Error> {
96+
let path = self._sm_fs_get_path(path_key_name, path_suffix).await?;
97+
tokio::fs::remove_file(path).await?;
98+
Ok("ok".to_string())
99+
}
100+
}

tests/test_storage_macro.rs

+38
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,28 @@ async fn test_storage_macro_redis() -> Result<(), Box<dyn std::error::Error + Se
2727
Ok(())
2828
}
2929

30+
#[tokio::test]
31+
async fn test_storage_macro_fs() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
32+
let (_ir, _is, cl) = set_up_test_env_single_user().await?;
33+
34+
cl.create_entry("test_storage_macro_fs:path", b"/tmp/colink-sm-fs-test/test")
35+
.await?;
36+
let key_name = "test_storage_macro_fs:$fs";
37+
test_crud(&cl, key_name).await?;
38+
39+
cl.create_entry(
40+
"test_storage_macro_fs_dir:path",
41+
b"/tmp/colink-sm-fs-test/test-dir",
42+
)
43+
.await?;
44+
let key_name = "test_storage_macro_fs_dir:$fs:test-file";
45+
test_crud(&cl, key_name).await?;
46+
let key_name = "test_storage_macro_fs_dir:$fs:test-dir:test-file";
47+
test_crud(&cl, key_name).await?;
48+
49+
Ok(())
50+
}
51+
3052
async fn test_crud(
3153
cl: &CoLink,
3254
key_name: &str,
@@ -114,6 +136,22 @@ async fn test_storage_macro_chunk_append(
114136
Ok(())
115137
}
116138

139+
#[tokio::test]
140+
async fn test_storage_macro_fs_append(
141+
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
142+
let (_ir, _is, cl) = set_up_test_env_single_user().await?;
143+
144+
cl.create_entry(
145+
"test_storage_macro_fs_append:path",
146+
b"/tmp/colink-sm-fs-test/append-test",
147+
)
148+
.await?;
149+
let key_name = "test_storage_macro_fs_append:$fs";
150+
test_append(&cl, key_name, 5e6 as usize).await?;
151+
152+
Ok(())
153+
}
154+
117155
#[ignore]
118156
#[tokio::test]
119157
async fn test_storage_macro_redis_chunk(

0 commit comments

Comments
 (0)