diff --git a/.gitignore b/.gitignore
index 184752b7..ef70b169 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@
**/.DS_Store
**/node_modules
packages/wasm/pkg
+packages/wasm/index.*
**/yarn-error.log
diff --git a/Cargo.lock b/Cargo.lock
index b11246d5..9f0637c0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,17 @@
# It is not intended for manual editing.
version = 3
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
[[package]]
name = "aho-corasick"
version = "0.7.18"
@@ -29,6 +40,128 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
+[[package]]
+name = "async-attributes"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-channel"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
+dependencies = [
+ "concurrent-queue",
+ "event-listener",
+ "futures-core",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+ "slab",
+]
+
+[[package]]
+name = "async-global-executor"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c290043c9a95b05d45e952fb6383c67bcb61471f60cfa21e890dba6654234f43"
+dependencies = [
+ "async-channel",
+ "async-executor",
+ "async-io",
+ "async-mutex",
+ "blocking",
+ "futures-lite",
+ "num_cpus",
+ "once_cell",
+]
+
+[[package]]
+name = "async-io"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b"
+dependencies = [
+ "concurrent-queue",
+ "futures-lite",
+ "libc",
+ "log",
+ "once_cell",
+ "parking",
+ "polling",
+ "slab",
+ "socket2",
+ "waker-fn",
+ "winapi",
+]
+
+[[package]]
+name = "async-lock"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-mutex"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-std"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c"
+dependencies = [
+ "async-attributes",
+ "async-channel",
+ "async-global-executor",
+ "async-io",
+ "async-lock",
+ "crossbeam-utils",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-lite",
+ "gloo-timers",
+ "kv-log-macro",
+ "log",
+ "memchr",
+ "num_cpus",
+ "once_cell",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "async-task"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9"
+
[[package]]
name = "async-trait"
version = "0.1.53"
@@ -40,6 +173,12 @@ dependencies = [
"syn",
]
+[[package]]
+name = "atomic-waker"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
+
[[package]]
name = "autocfg"
version = "1.1.0"
@@ -102,6 +241,20 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "blocking"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "atomic-waker",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+]
+
[[package]]
name = "bumpalo"
version = "3.9.1"
@@ -120,13 +273,19 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
+[[package]]
+name = "cache-padded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
+
[[package]]
name = "cached"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af4dfac631a8e77b2f327f7852bb6172771f5279c4512efe79fad6067b37be3d"
dependencies = [
- "hashbrown",
+ "hashbrown 0.11.2",
"once_cell",
]
@@ -168,6 +327,15 @@ dependencies = [
"unsigned-varint",
]
+[[package]]
+name = "concurrent-queue"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
+dependencies = [
+ "cache-padded",
+]
+
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@@ -192,6 +360,16 @@ dependencies = [
"libc",
]
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
+dependencies = [
+ "cfg-if",
+ "lazy_static",
+]
+
[[package]]
name = "crypto-common"
version = "0.1.3"
@@ -202,6 +380,16 @@ dependencies = [
"typenum",
]
+[[package]]
+name = "ctor"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
+dependencies = [
+ "quote",
+ "syn",
+]
+
[[package]]
name = "data-encoding"
version = "2.3.2"
@@ -244,6 +432,12 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+[[package]]
+name = "event-listener"
+version = "2.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
+
[[package]]
name = "fastrand"
version = "1.7.0"
@@ -266,13 +460,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
-name = "fs"
-version = "0.1.0"
+name = "futures-channel"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
- "chrono",
- "libipld",
- "multihash",
- "semver",
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+
+[[package]]
+name = "futures-io"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
+
+[[package]]
+name = "futures-lite"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite",
+ "waker-fn",
]
[[package]]
@@ -285,12 +505,44 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "getrandom"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gloo-timers"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d12a7f4e95cfe710f1d624fb1210b7d961a5fb05c4fd942f4feab06e61f590e"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+[[package]]
+name = "hashbrown"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758"
+dependencies = [
+ "ahash",
+]
+
[[package]]
name = "heck"
version = "0.3.3"
@@ -300,6 +552,15 @@ dependencies = [
"unicode-segmentation",
]
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "indexmap"
version = "1.8.1"
@@ -307,7 +568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
"autocfg",
- "hashbrown",
+ "hashbrown 0.11.2",
]
[[package]]
@@ -349,6 +610,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
+[[package]]
+name = "kv-log-macro"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
+dependencies = [
+ "log",
+]
+
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -470,6 +740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
dependencies = [
"cfg-if",
+ "value-bag",
]
[[package]]
@@ -545,12 +816,28 @@ dependencies = [
"autocfg",
]
+[[package]]
+name = "num_cpus"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
[[package]]
name = "once_cell"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
+[[package]]
+name = "parking"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
+
[[package]]
name = "parking_lot"
version = "0.12.0"
@@ -584,6 +871,31 @@ dependencies = [
"indexmap",
]
+[[package]]
+name = "pin-project-lite"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "polling"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "log",
+ "wepoll-ffi",
+ "winapi",
+]
+
[[package]]
name = "proc-macro-crate"
version = "1.1.3"
@@ -794,12 +1106,28 @@ dependencies = [
"keccak",
]
+[[package]]
+name = "slab"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
+
[[package]]
name = "smallvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
+[[package]]
+name = "socket2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "syn"
version = "1.0.90"
@@ -901,29 +1229,34 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836"
+[[package]]
+name = "value-bag"
+version = "1.0.0-alpha.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f"
+dependencies = [
+ "ctor",
+ "version_check",
+]
+
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+[[package]]
+name = "waker-fn"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
+
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
-[[package]]
-name = "wasm"
-version = "0.1.0"
-dependencies = [
- "fs",
- "js-sys",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "web-sys",
-]
-
[[package]]
name = "wasm-bindgen"
version = "0.2.79"
@@ -990,6 +1323,19 @@ version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
+[[package]]
+name = "wasm-wnfs"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wnfs",
+]
+
[[package]]
name = "web-sys"
version = "0.3.56"
@@ -1001,8 +1347,13 @@ dependencies = [
]
[[package]]
-name = "webnative-tests"
-version = "0.1.0"
+name = "wepoll-ffi"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
+dependencies = [
+ "cc",
+]
[[package]]
name = "which"
@@ -1079,3 +1430,28 @@ name = "windows_x86_64_msvc"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
+
+[[package]]
+name = "wnfs"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-std",
+ "async-trait",
+ "chrono",
+ "hashbrown 0.12.0",
+ "libipld",
+ "multihash",
+ "semver",
+]
+
+[[package]]
+name = "wnfs-tests"
+version = "0.1.0"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wnfs",
+]
diff --git a/README.md b/README.md
index 2042b53f..fc5ad729 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
WebNative FileSystem (WNFS)
diff --git a/packages/fs/Cargo.toml b/packages/fs/Cargo.toml
index 0a8b5e1b..371105c9 100644
--- a/packages/fs/Cargo.toml
+++ b/packages/fs/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "fs"
+name = "wnfs"
version = "0.1.0"
description = "WebNative filesystem core implementation"
keywords = ["wnfs", "webnative", "ipfs", "decentralisation"]
@@ -8,7 +8,7 @@ categories = [
"cryptography",
"web programming",
]
-license-file = "LICENSE"
+license-file = "../../LICENSE"
readme = "README.md"
edition = "2021"
repository = "https://github.com/fission-suite/rs-wnfs"
@@ -20,11 +20,15 @@ libipld = "0.13.1"
multihash = "0.16.2"
semver = "1.0.7"
chrono = "0.4.19"
+anyhow = "1.0.56"
+hashbrown = "0.12.0"
+async-trait = "0.1.53"
+async-std = { version = "1.11.0", features = ["attributes"] }
[lib]
path = "lib.rs"
+crate-type = ["cdylib" , "rlib"]
[features]
default = []
wasm = []
-wasm-bindgen = ["wasm"]
diff --git a/packages/fs/common/blockstore.rs b/packages/fs/common/blockstore.rs
new file mode 100644
index 00000000..acc5d11c
--- /dev/null
+++ b/packages/fs/common/blockstore.rs
@@ -0,0 +1,91 @@
+//! Block store traits.
+
+use super::FsError;
+use anyhow::Result;
+use async_trait::async_trait;
+use hashbrown::HashMap;
+use libipld::{
+ cbor::DagCborCodec,
+ cid::Version,
+ codec::{Codec, Decode},
+ Cid, IpldCodec,
+};
+use multihash::{Code, MultihashDigest};
+use std::borrow::Cow;
+
+//--------------------------------------------------------------------------------------------------
+// Type Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// For types that implement getting a block from a CID.
+#[async_trait]
+pub trait BlockStoreLookup {
+ async fn get_block<'a>(&'a self, cid: &Cid) -> Result
>;
+}
+
+/// For types that implement loading a cbor model from a blockstore using a CID.
+#[async_trait]
+pub trait BlockStoreCidLoad {
+ /// Loads a cbor model from the store with provided CID.
+ async fn load>(&self, cid: &Cid) -> Result;
+}
+
+/// For types that implement block store operations.
+#[async_trait]
+pub trait BlockStore: BlockStoreLookup + BlockStoreCidLoad {
+ async fn put_block(&mut self, bytes: Vec, codec: IpldCodec) -> Result;
+}
+
+/// An in-memory block store to simulate IPFS. IPFS is basically an glorified HashMap.
+#[derive(Debug, Default)]
+pub struct MemoryBlockStore(HashMap>);
+
+//--------------------------------------------------------------------------------------------------
+// Implementations
+//--------------------------------------------------------------------------------------------------
+
+impl MemoryBlockStore {
+ /// Creates a new in-memory block store.
+ pub fn new() -> Self {
+ Self::default()
+ }
+}
+
+#[async_trait]
+impl BlockStore for MemoryBlockStore {
+ /// Stores an array of bytes in the block store.
+ async fn put_block(&mut self, bytes: Vec, codec: IpldCodec) -> Result {
+ let hash = Code::Sha2_256.digest(&bytes);
+ let cid = Cid::new(Version::V1, codec.into(), hash)?;
+
+ self.0.insert((&cid).to_string(), bytes);
+
+ Ok(cid)
+ }
+}
+
+#[async_trait]
+impl BlockStoreLookup for MemoryBlockStore {
+ /// Retrieves an array of bytes from the block store with given CID.
+ async fn get_block<'a>(&'a self, cid: &Cid) -> Result> {
+ let bytes = self
+ .0
+ .get(&cid.to_string())
+ .ok_or(FsError::CIDNotFoundInBlockstore)?;
+
+ Ok(Cow::Borrowed(bytes))
+ }
+}
+
+#[async_trait]
+impl BlockStoreCidLoad for MemoryBlockStore {
+ /// Loads a cbor-encoded data from the store with provided CID.
+ async fn load>(&self, cid: &Cid) -> Result {
+ let bytes = self.get_block(cid).await?;
+ let decoded = DagCborCodec.decode(bytes.as_ref())?;
+ Ok(decoded)
+ }
+}
+
+#[cfg(test)]
+mod blockstore_tests {}
diff --git a/packages/fs/common/error.rs b/packages/fs/common/error.rs
new file mode 100644
index 00000000..8079f961
--- /dev/null
+++ b/packages/fs/common/error.rs
@@ -0,0 +1,26 @@
+//! File system errors.
+
+use anyhow::Result;
+use std::{
+ error::Error,
+ fmt::{Debug, Display},
+};
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum FsError {
+ CIDNotFoundInBlockstore,
+ InvalidPath,
+ NodeNotFound,
+}
+
+impl std::error::Error for FsError {}
+
+impl Display for FsError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{:?}", self)
+ }
+}
+
+pub fn error(err: impl Error + Send + Sync + 'static) -> Result {
+ Err(err.into())
+}
diff --git a/packages/fs/common/metadata.rs b/packages/fs/common/metadata.rs
index a40d8997..a6288563 100644
--- a/packages/fs/common/metadata.rs
+++ b/packages/fs/common/metadata.rs
@@ -1,7 +1,13 @@
+//! File system metadata.
+
+use chrono::{DateTime, Utc};
use semver::Version;
-pub enum UnixNodeKind {
- // See https://docs.ipfs.io/concepts/file-systems/#unix-file-system-unixfs
+/// Represents the type of node in the UnixFS file system.
+///
+/// See https://docs.ipfs.io/concepts/file-systems/#unix-file-system-unixfs
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum UnixFsNodeKind {
Raw,
File,
Dir,
@@ -10,16 +16,67 @@ pub enum UnixNodeKind {
HAMTShard,
}
-pub struct UnixMetadata {
- // See https://docs.ipfs.io/concepts/file-systems/#unix-file-system-unixfs
- mtime: u64,
- ctime: u64,
- mode: u32,
- kind: UnixNodeKind,
+/// Mode represents the Unix permissions for a UnixFS node.
+///
+/// See
+/// - https://docs.ipfs.io/concepts/file-systems/#unix-file-system-unixfs
+/// - https://en.wikipedia.org/wiki/File-system_permissions#Numeric_notation
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum UnixFsMode {
+ NoPermissions = 0,
+ OwnerReadWriteExecute = 700,
+ OwnerGroupReadWriteExecute = 770,
+ AllReadWriteExecute = 777,
+ AllExecute = 111,
+ AllWrite = 222,
+ AllWriteExecute = 333,
+ AllRead = 444,
+ AllReadExecute = 555,
+ AllReadWrite = 666,
+ OwnerReadWriteExecuteGroupRead = 740,
+ OwnerReadWriteExecuteGroupOthersReadExecute = 755,
+ OwnerReadWriteGroupOthersRead = 644,
+}
+
+/// The metadata of a node in the UnixFS file system.
+///
+/// See https://docs.ipfs.io/concepts/file-systems/#unix-file-system-unixfs
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct UnixFsMetadata {
+ created: DateTime,
+ modified: DateTime,
+ mode: UnixFsMode,
+ kind: UnixFsNodeKind,
}
+/// The metadata of a node on the WNFS file system.
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Metadata {
- unix_metadata: UnixMetadata,
- is_file: bool, // TODO: Already in UnixMetadata?
+ unixfs_metadata: UnixFsMetadata,
version: Version,
}
+
+impl Metadata {
+ /// Creates a new metadata representing a UnixFS node.
+ pub fn new(time: DateTime, kind: UnixFsNodeKind) -> Self {
+ let mode =
+ if matches!(kind, UnixFsNodeKind::Dir) || matches!(kind, UnixFsNodeKind::HAMTShard) {
+ UnixFsMode::OwnerReadWriteGroupOthersRead
+ } else {
+ UnixFsMode::OwnerReadWriteExecuteGroupOthersReadExecute
+ };
+
+ Self {
+ unixfs_metadata: UnixFsMetadata {
+ created: time,
+ modified: time,
+ mode,
+ kind,
+ },
+ version: Version::new(1, 0, 0),
+ }
+ }
+}
+
+#[cfg(test)]
+mod metadata_tests {}
diff --git a/packages/fs/common/mod.rs b/packages/fs/common/mod.rs
index fe2907e2..fe8ec0a6 100644
--- a/packages/fs/common/mod.rs
+++ b/packages/fs/common/mod.rs
@@ -1,3 +1,7 @@
+mod blockstore;
+mod error;
mod metadata;
+pub use blockstore::*;
+pub(crate) use error::*;
pub use metadata::*;
diff --git a/packages/fs/lib.rs b/packages/fs/lib.rs
index 732bb824..7e89dab2 100644
--- a/packages/fs/lib.rs
+++ b/packages/fs/lib.rs
@@ -1,6 +1,10 @@
mod common;
pub mod public;
-// RE-EXPORTS
+pub use common::*;
-pub use libipld::Cid;
+//--------------------------------------------------------------------------------------------------
+// Re-exports
+//--------------------------------------------------------------------------------------------------
+
+pub use libipld::{Cid, IpldCodec};
diff --git a/packages/fs/public/filesystem.rs b/packages/fs/public/filesystem.rs
new file mode 100644
index 00000000..e2074d15
--- /dev/null
+++ b/packages/fs/public/filesystem.rs
@@ -0,0 +1,39 @@
+//! The public file system API.
+
+use super::PublicDirectory;
+use crate::common::BlockStore;
+use anyhow::Result;
+
+//--------------------------------------------------------------------------------------------------
+// Type Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// The WNFS public file system.
+struct PublicFileSystem<'s, T: BlockStore> {
+ blockstore: &'s T,
+ root_dir: PublicDirectory,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Implementations
+//--------------------------------------------------------------------------------------------------
+
+impl<'s, T: BlockStore> PublicFileSystem<'s, T> {
+ /// Creates a new WNFS public file system.
+ pub fn new(blockstore: &'s T, root_dir: PublicDirectory) -> Self {
+ Self {
+ blockstore,
+ root_dir,
+ }
+ }
+
+ /// Reads file content at the specified path from the file system.
+ pub async fn read(path_segments: &[String]) -> Result<()> {
+ todo!(
+ r#"
+ This is entering javascript land.
+ We can make writing file blocks to linear memory and return the base address.
+ "#
+ )
+ }
+}
diff --git a/packages/fs/public/mod.rs b/packages/fs/public/mod.rs
index 41b54b13..107486c4 100644
--- a/packages/fs/public/mod.rs
+++ b/packages/fs/public/mod.rs
@@ -1,3 +1,5 @@
+mod filesystem;
mod node;
+pub use filesystem::*;
pub use node::*;
diff --git a/packages/fs/public/node.rs b/packages/fs/public/node.rs
index 1d6a0a4a..acfcec6e 100644
--- a/packages/fs/public/node.rs
+++ b/packages/fs/public/node.rs
@@ -1,41 +1,192 @@
-use crate::common::Metadata;
-use libipld::Cid;
-use std::collections::HashMap;
+//! Public file system in-memory representation.
+use crate::common::{error, BlockStore, FsError, Metadata, UnixFsNodeKind};
+use anyhow::Result;
+use chrono::{DateTime, Utc};
+use hashbrown::HashMap;
+use libipld::{cbor::DagCborCodec, codec::Decode, Cid};
+use std::{ops::Deref, rc::Rc};
+
+//--------------------------------------------------------------------------------------------------
+// Type Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// A node in a WNFS public file system. This can either be a file or a directory.
+#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PublicNode {
File(PublicFile),
- Dir(PublicDirKind),
-}
-
-pub enum PublicDirKind {
- Link(PublicDir),
- Cid(PublicDir), // Persisted. TODO: what does that mean?
+ Dir(PublicDirectory),
}
-pub enum LinkKind {
+/// A link to another node in the WNFS public file system. It can be held as a simple serialised CID or as a reference to the node itself.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Link {
Cid(Cid),
- Node(PublicNode),
+ Node(Rc),
}
-pub struct PublicDir {
- metadata: Metadata,
- userland: HashMap, // TODO: Hashbrown?
- previous: Option,
+/// A directory in a WNFS public file system.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PublicDirectory {
+ // metadata: Metadata,
+ userland: HashMap,
+ // previous: Option,
}
+/// A file in a WNFS public file system.
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicFile {
metadata: Metadata,
userland: Cid,
previous: Option,
}
-pub enum OperationContext {
- AbortContext,
- BlockStore,
+//--------------------------------------------------------------------------------------------------
+// Implementations
+//--------------------------------------------------------------------------------------------------
+
+// TODO(appcypher)
+impl Decode for PublicNode {
+ fn decode(c: DagCborCodec, r: &mut R) -> Result {
+ todo!()
+ }
+}
+
+impl PublicNode {
+ /// Stores a WNFS node as block(s) in chosen block store.
+ pub async fn store(&self, store: &mut B) -> Cid {
+ match self {
+ PublicNode::File(file) => file.store(store).await,
+ PublicNode::Dir(dir) => dir.store(store).await,
+ }
+ }
+
+ /// Casts a node to a directory.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the node is not a directory.
+ pub fn as_dir(&self) -> &PublicDirectory {
+ match self {
+ PublicNode::Dir(dir) => dir,
+ _ => unreachable!(),
+ }
+ }
+}
+
+impl PublicDirectory {
+ /// Creates a new directory using the given metadata.
+ pub fn new(time: DateTime) -> Self {
+ Self {
+ // metadata: Metadata::new(time, UnixFsNodeKind::Dir),
+ userland: HashMap::new(),
+ // previous: None,
+ }
+ }
+
+ /// Follows a path and fetches the node at the end of the path.
+ pub async fn get_node(
+ &self,
+ path_segments: &[String],
+ store: &B,
+ ) -> Result> {
+ if path_segments.is_empty() {
+ return error(FsError::InvalidPath);
+ }
+
+ let mut working_node: Rc = Rc::new(PublicNode::Dir(self.clone()));
+
+ // Iterate over the path segments until we get the node of the last segment.
+ for (index, segment) in path_segments.iter().enumerate() {
+ // Cast working node to directory.
+ let dir = working_node.deref().as_dir();
+
+ // Fetch node representing path segment in working directory.
+ if let Some(node) = dir.lookup_node(segment, store).await? {
+ match node.as_ref() {
+ PublicNode::Dir(_) => {
+ // If the node is a directory, set it as the working node.
+ working_node = Rc::clone(&node);
+ }
+ PublicNode::File(_) => {
+ // If the node is a file, we return it if it's the last segment.
+ if index != path_segments.len() - 1 {
+ return error(FsError::InvalidPath);
+ }
+ working_node = Rc::clone(&node);
+ break;
+ }
+ }
+
+ // We continue loop after setting the working node to a directory node.
+ continue;
+ }
+
+ // If the node is not found, we return an error.
+ return error(FsError::NodeNotFound);
+ }
+
+ Ok(working_node)
+ }
+
+ /// Looks up a node by its path name in the current directory.
+ ///
+ /// TODO(appcypher): What is a valid path segment identifier?
+ pub async fn lookup_node(
+ &self,
+ path_segment: &str,
+ store: &B,
+ ) -> Result