From e5bc06249f4c54e9d1982825fa1be1bd81c7efe7 Mon Sep 17 00:00:00 2001
From: Stephen Akinyemi
Date: Fri, 20 May 2022 13:30:23 +0100
Subject: [PATCH 1/8] Prepare packages and add usage examples
---
Cargo.lock | 1 +
README.md | 90 +++-
crates/fs/Cargo.toml | 2 +-
crates/fs/lib.rs | 1 +
crates/fs/public/directory.rs | 724 ++++++++++++++++++++++++-----
crates/fs/public/file.rs | 40 ++
crates/fs/public/link.rs | 13 +-
crates/fs/public/node.rs | 18 +
crates/wasm/README.md | 88 +++-
crates/wasm/fs/public/directory.rs | 16 +-
crates/wasm/package.json | 22 +-
11 files changed, 875 insertions(+), 140 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 9bc6da68..9fef545d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1675,6 +1675,7 @@ dependencies = [
"chrono",
"field_names",
"futures",
+ "futures-util",
"hashbrown 0.12.0",
"libipld",
"multihash",
diff --git a/README.md b/README.md
index 231f4de6..f76a64f4 100644
--- a/README.md
+++ b/README.md
@@ -31,8 +31,92 @@
##
-This project will implement a pure rust crate for creating and manipulating IPLD graphs that encode WNFS.
-Its goal is to be as dependency-less as possible in order to be easily compiled to WebAssembly to be used in the browsers or other environments.
+This crate is a Rust implementation of the primitives for creating and manipulating IPLD graphs that encode WNFS.
+
+A goal of the project is to be easily compiled to WebAssembly to be used in the browsers or other environments.
+
+## Outline
+
+- [Usage](#usage)
+- [Building the Project](#building-the-project)
+- [Testing the Project](#testing-the-project)
+
+## Usage
+
+Creating a new public directory.
+
+```rs
+use wnfs::{PublicDirectory, Id};
+use chrono::Utc;
+
+let dir = PublicDirectory::new(Utc::now());
+println!("id = {}", dir.get_id());
+```
+
+The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, an object that implements the BlockStore trait like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/fs/common/blockstore.rs#L42-L62) can be used.
+
+```rs
+use wnfs::{PublicDirectory, MemoryBlockStore, ipld::Cid};
+use chrono::Utc;
+
+let dir = PublicDirectory::new(Utc::now());
+let store = MemoryBlockStore::default();
+
+// ...
+```
+
+The WNFS API is immutable, therefore, we need to keep track of the updated root directory after every change.
+
+Each fs operation returns a possibly updated root directory that subsequent changes can be applied on.
+
+```rs
+// ...
+
+let dir = Rc::new(dir);
+
+// Create a /pictures/cats directory.
+let OpResult { root_dir, .. } = dir
+ .mkdir(&["pictures".into(), "cats".into()], time, &store)
+ .await
+ .unwrap();
+
+// Get a sample CIDv1.
+let cid = Cid::default();
+
+// Add a file to /pictures/cats.
+let OpResult { root_dir, .. } = root_dir
+ .write(
+ &["pictures".into(), "cats".into(), "tabby.png".into()],
+ cid,
+ time,
+ &store,
+ )
+ .await
+ .unwrap();
+
+// Create and add a file to /pictures/dogs directory.
+let OpResult { root_dir, .. } = root_dir
+ .write(
+ &["pictures".into(), "cats".into(), "billie.jpeg".into()],
+ cid,
+ time,
+ &store,
+ )
+ .await
+ .unwrap();
+
+// Delete /pictures/cats directory.
+let OpResult { root_dir, .. } = root_dir
+ .rm(&["pictures".into(), "cats".into()], &store)
+ .await
+ .unwrap();
+
+// List all files in /pictures directory.
+let OpResult { result, .. } = root_dir
+ .ls(&["pictures".into()], &store)
+ .await
+ .unwrap();
+```
## Building the Project
@@ -44,7 +128,7 @@ Its goal is to be as dependency-less as possible in order to be easily compiled
- **The WebAssembly Toolchain**
- If yous are interested in compiling the project for WebAssembly, you can follow the instructions below.
+ If you are interested in compiling the project for WebAssembly, you can follow the instructions below.
Read more
diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml
index 8d08e03e..9d0bf5d2 100644
--- a/crates/fs/Cargo.toml
+++ b/crates/fs/Cargo.toml
@@ -29,7 +29,7 @@ async-recursion = "1.0.0"
field_names = "0.2.0"
futures = "0.3.21"
async-stream = "0.3.3"
-
+futures-util = "0.3.21"
[lib]
path = "lib.rs"
diff --git a/crates/fs/lib.rs b/crates/fs/lib.rs
index 718caec8..879b04a5 100644
--- a/crates/fs/lib.rs
+++ b/crates/fs/lib.rs
@@ -2,6 +2,7 @@ mod common;
pub mod public;
pub use common::*;
+pub use public::*;
//--------------------------------------------------------------------------------------------------
// Re-exports
diff --git a/crates/fs/public/directory.rs b/crates/fs/public/directory.rs
index 03c1cc60..7df59b4c 100644
--- a/crates/fs/public/directory.rs
+++ b/crates/fs/public/directory.rs
@@ -26,7 +26,18 @@ use super::{Id, Link, PublicFile, PublicNode};
// Type Definitions
//--------------------------------------------------------------------------------------------------
-/// A directory in a WNFS public file system.``
+/// A directory in a WNFS public file system.
+///
+/// # Examples
+///
+/// ```
+/// use wnfs::{PublicDirectory, Id};
+/// use chrono::Utc;
+///
+/// let dir = PublicDirectory::new(Utc::now());
+///
+/// println!("id = {}", dir.get_id());
+/// ```
#[derive(Debug, Clone, PartialEq, Eq, FieldNames)]
pub struct PublicDirectory {
pub(crate) metadata: Metadata,
@@ -34,7 +45,7 @@ pub struct PublicDirectory {
pub(crate) previous: Option,
}
-/// Represents a directory that has possibly diverged. It is the result of operating on a directory.
+/// The result of an operation applied to a directory.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OpResult {
// The root directory.
@@ -44,22 +55,81 @@ pub struct OpResult {
}
/// Represents the directory nodes along a path.
+///
+/// # Examples
+///
+/// ```
+/// use wnfs::{PublicDirectory, PathNodes};
+/// use std::rc::Rc;
+/// use chrono::Utc;
+///
+/// let nodes = PathNodes::new(
+/// Utc::now(),
+/// &["movies".into(), "anime".into()],
+/// Rc::new(PublicDirectory::new(Utc::now())),
+/// );
+///
+/// println!("path nodes = {:?}", nodes);
+/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PathNodes {
- path: Vec<(Rc, String)>,
- tail: Rc,
+ pub path: Vec<(Rc, String)>,
+ pub tail: Rc,
}
/// The kinds of outcome from getting a `PathNodes`.
-pub enum GetNodePathResult {
+///
+/// # Examples
+///
+/// ```
+/// use wnfs::{PublicDirectory, MemoryBlockStore, OpResult};
+/// use std::rc::Rc;
+/// use chrono::Utc;
+///
+/// #[async_std::main]
+/// async fn main() {
+/// let time = Utc::now();
+/// let dir = Rc::new(PublicDirectory::new(time));
+/// let store = MemoryBlockStore::default();
+///
+/// let OpResult { root_dir, result } = dir
+/// .ls(&[], &store)
+/// .await
+/// .unwrap();
+///
+/// println!("ls = {:?}", result);
+/// }
+/// ```
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum PathNodesResult {
Complete(PathNodes),
MissingLink(PathNodes, String),
NotADirectory(PathNodes, String),
}
+//--------------------------------------------------------------------------------------------------
+// Implementations
+//--------------------------------------------------------------------------------------------------
+
impl PathNodes {
/// Creates a new `PathNodes` that is not based on an existing file tree.
- fn new(time: DateTime, path_segments: &[String], tail: Rc) -> Self {
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, PathNodes};
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// let nodes = PathNodes::new(
+ /// Utc::now(),
+ /// &["movies".into(), "anime".into()],
+ /// Rc::new(PublicDirectory::new(Utc::now())),
+ /// );
+ ///
+ /// println!("path nodes = {:?}", nodes);
+ /// ```
+ pub fn new(time: DateTime, path_segments: &[String], tail: Rc) -> Self {
let path: Vec<(Rc, String)> = path_segments
.iter()
.map(|segment| (Rc::new(PublicDirectory::new(time)), segment.clone()))
@@ -69,29 +139,96 @@ impl PathNodes {
}
/// Constructs a diverged path nodes by fixing up links in a `PathNodes` and returning the resulting root node.
- fn reconstruct(self) -> Rc {
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, PathNodes};
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// let nodes = PathNodes::new(
+ /// Utc::now(),
+ /// &["movies".into(), "anime".into()],
+ /// Rc::new(PublicDirectory::new(Utc::now())),
+ /// );
+ ///
+ /// let new_root = nodes.reconstruct();
+ ///
+ /// println!("new_root = {:?}", new_root);
+ /// ```
+ pub fn reconstruct(self) -> Rc {
if self.path.is_empty() {
return self.tail;
}
- let mut working_node = self.tail;
+ let mut working_dir = self.tail;
for (dir, segment) in self.path.iter().rev() {
let mut dir = (**dir).clone();
- let link = Link::Node(PublicNode::Dir(working_node));
+ let link = Link::with_dir(working_dir);
dir.userland.insert(segment.clone(), link);
- working_node = Rc::new(dir);
+ working_dir = Rc::new(dir);
}
- working_node
+ working_dir
}
-}
-//--------------------------------------------------------------------------------------------------
-// Implementations
-//--------------------------------------------------------------------------------------------------
+ /// Returns the length of the path nodes.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, PathNodes};
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// let nodes = PathNodes::new(
+ /// Utc::now(),
+ /// &["movies".into(), "anime".into()],
+ /// Rc::new(PublicDirectory::new(Utc::now())),
+ /// );
+ ///
+ /// assert_eq!(nodes.len(), 2);
+ /// ```
+ pub fn len(&self) -> usize {
+ self.path.len()
+ }
+
+ /// Checks if the path nodes are empty.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, PathNodes};
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// let nodes = PathNodes::new(
+ /// Utc::now(),
+ /// &["movies".into(), "anime".into()],
+ /// Rc::new(PublicDirectory::new(Utc::now())),
+ /// );
+ ///
+ /// assert!(!nodes.is_empty());
+ /// ```
+ pub fn is_empty(&self) -> bool {
+ self.path.is_empty()
+ }
+}
impl PublicDirectory {
- /// Creates a new directory using the given metadata.
+ /// Creates a new directory with provided time.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, Id};
+ /// use chrono::Utc;
+ ///
+ /// let dir = PublicDirectory::new(Utc::now());
+ ///
+ /// println!("id = {}", dir.get_id());
+ /// ```
pub fn new(time: DateTime) -> Self {
Self {
metadata: Metadata::new(time, UnixFsNodeKind::Dir),
@@ -105,14 +242,15 @@ impl PublicDirectory {
self.previous
}
- /// Gets the directory nodes along a path and allows for cases where the path is nopt fully constructed.
- pub async fn get_node_path(
+ /// Gets the directory nodes along specified path.
+ ///
+ /// Supports cases where the entire path does not exist.
+ pub(crate) async fn get_path_nodes(
self: Rc,
path_segments: &[String],
store: &B,
- ) -> Result {
- use GetNodePathResult::*;
-
+ ) -> Result {
+ use PathNodesResult::*;
let mut working_node = self;
let mut path_nodes = Vec::with_capacity(path_segments.len());
@@ -148,16 +286,17 @@ impl PublicDirectory {
}
/// Gets the directory nodes along a path and also supports creating missing intermediate directories.
- pub async fn get_node_path_with_mkdir(
+ pub(crate) async fn get_path_nodes_or_create(
self: Rc,
path_segments: &[String],
time: DateTime,
store: &B,
) -> Result {
- match self.get_node_path(path_segments, store).await? {
- GetNodePathResult::Complete(path_nodes) => Ok(path_nodes),
- GetNodePathResult::NotADirectory(_, _) => error(FsError::InvalidPath),
- GetNodePathResult::MissingLink(path_so_far, missing_link) => {
+ use PathNodesResult::*;
+ match self.get_path_nodes(path_segments, store).await? {
+ Complete(path_nodes) => Ok(path_nodes),
+ NotADirectory(_, _) => error(FsError::InvalidPath),
+ MissingLink(path_so_far, missing_link) => {
let missing_path = path_segments.split_at(path_so_far.path.len() + 1).1;
let missing_path_nodes =
PathNodes::new(time, missing_path, Rc::new(PublicDirectory::new(time)));
@@ -176,25 +315,53 @@ impl PublicDirectory {
}
/// Follows a path and fetches the node at the end of the path.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, MemoryBlockStore, OpResult};
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let time = Utc::now();
+ /// let dir = Rc::new(PublicDirectory::new(time));
+ /// let store = MemoryBlockStore::default();
+ ///
+ /// let OpResult { root_dir, .. } = Rc::new(PublicDirectory::new(Utc::now()))
+ /// .mkdir(&["pictures".into(), "cats".into()], Utc::now(), &store)
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir, result } = root_dir
+ /// .get_node(&["pictures".into()], &store)
+ /// .await
+ /// .unwrap();
+ ///
+ /// assert!(result.is_some());
+ /// }
+ /// ```
pub async fn get_node(
self: Rc,
path_segments: &[String],
store: &B,
) -> Result>> {
+ use PathNodesResult::*;
let root_dir = Rc::clone(&self);
Ok(match path_segments.split_last() {
Some((path_segment, parent_path)) => {
- match self.get_node_path(parent_path, store).await? {
- GetNodePathResult::Complete(parent_path_nodes) => OpResult {
+ match self.get_path_nodes(parent_path, store).await? {
+ Complete(parent_path_nodes) => OpResult {
root_dir,
result: parent_path_nodes
.tail
.lookup_node(path_segment, store)
.await?,
},
- GetNodePathResult::MissingLink(_, _) => bail!(FsError::NotFound),
- GetNodePathResult::NotADirectory(_, _) => bail!(FsError::NotFound),
+ MissingLink(_, _) => bail!(FsError::NotFound),
+ NotADirectory(_, _) => bail!(FsError::NotFound),
}
}
None => OpResult {
@@ -205,6 +372,28 @@ impl PublicDirectory {
}
/// Looks up a node by its path name in the current directory.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, Id, MemoryBlockStore, OpResult};
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let mut store = MemoryBlockStore::default();
+ ///
+ /// let OpResult { root_dir, .. } = Rc::new(PublicDirectory::new(Utc::now()))
+ /// .mkdir(&["pictures".into(), "cats".into()], Utc::now(), &store)
+ /// .await
+ /// .unwrap();
+ ///
+ /// let node = root_dir.lookup_node("pictures", &store).await.unwrap();
+ ///
+ /// assert!(node.is_some());
+ /// }
+ /// ```
pub async fn lookup_node(
&self,
path_segment: &str,
@@ -216,16 +405,65 @@ impl PublicDirectory {
})
}
+ #[async_recursion(?Send)]
/// Stores directory in provided block store.
///
/// This function can be recursive if the directory contains other directories.
- #[async_recursion(?Send)]
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, Id, MemoryBlockStore};
+ /// use chrono::Utc;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let mut store = MemoryBlockStore::default();
+ /// let dir = PublicDirectory::new(Utc::now());
+ ///
+ /// dir.store(&mut store).await.unwrap();
+ /// }
+ /// ```
pub async fn store(&self, store: &mut B) -> Result {
let bytes = self.encode(store).await?;
store.put_block(bytes, IpldCodec::DagCbor).await
}
/// Reads specified file content from the directory.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, MemoryBlockStore, OpResult};
+ /// use libipld::cid::Cid;
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let time = Utc::now();
+ /// let dir = Rc::new(PublicDirectory::new(time));
+ /// let mut store = MemoryBlockStore::default();
+ /// let cid = Cid::default();
+ ///
+ /// let OpResult { root_dir, .. } = Rc::new(PublicDirectory::new(Utc::now()))
+ /// .write(
+ /// &["pictures".into(), "cats".into(), "tabby.png".into()],
+ /// cid,
+ /// Utc::now(),
+ /// &store
+ /// )
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir, result } = root_dir
+ /// .read(&["pictures".into(), "cats".into(), "tabby.png".into()], &mut store)
+ /// .await
+ /// .unwrap();
+ ///
+ /// assert_eq!(result, cid);
+ /// }
+ /// ```
pub async fn read(
self: Rc,
path_segments: &[String],
@@ -234,8 +472,8 @@ impl PublicDirectory {
let root_dir = Rc::clone(&self);
let (path, filename) = utils::split_last(path_segments)?;
- match self.get_node_path(path, store).await? {
- GetNodePathResult::Complete(node_path) => {
+ match self.get_path_nodes(path, store).await? {
+ PathNodesResult::Complete(node_path) => {
match node_path.tail.lookup_node(filename, store).await? {
Some(PublicNode::File(file)) => Ok(OpResult {
root_dir,
@@ -250,6 +488,32 @@ impl PublicDirectory {
}
/// Writes a file to the directory.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, MemoryBlockStore, OpResult};
+ /// use libipld::cid::Cid;
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let time = Utc::now();
+ /// let dir = Rc::new(PublicDirectory::new(time));
+ /// let store = MemoryBlockStore::default();
+ ///
+ /// let OpResult { root_dir, .. } = Rc::new(PublicDirectory::new(Utc::now()))
+ /// .write(
+ /// &["pictures".into(), "cats".into(), "tabby.png".into()],
+ /// Cid::default(),
+ /// Utc::now(),
+ /// &store
+ /// )
+ /// .await
+ /// .unwrap();
+ /// }
+ /// ```
pub async fn write(
self: Rc,
path_segments: &[String],
@@ -261,7 +525,7 @@ impl PublicDirectory {
// This will create directories if they don't exist yet
let mut directory_path_nodes = self
- .get_node_path_with_mkdir(directory_path, time, store)
+ .get_path_nodes_or_create(directory_path, time, store)
.await?;
let mut directory = (*directory_path_nodes.tail).clone();
@@ -279,10 +543,9 @@ impl PublicDirectory {
};
// insert the file into its parent directory
- directory.userland.insert(
- filename.to_string(),
- Link::Node(PublicNode::File(Rc::new(file))),
- );
+ directory
+ .userland
+ .insert(filename.to_string(), Link::with_file(Rc::new(file)));
directory_path_nodes.tail = Rc::new(directory);
// reconstruct the file path
@@ -294,6 +557,24 @@ impl PublicDirectory {
/// Creates a new directory at the specified path.
///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, Id, MemoryBlockStore, OpResult};
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let mut store = MemoryBlockStore::default();
+ ///
+ /// let OpResult { root_dir, .. } = Rc::new(PublicDirectory::new(Utc::now()))
+ /// .mkdir(&["pictures".into(), "cats".into()], Utc::now(), &store)
+ /// .await
+ /// .unwrap();
+ /// }
+ /// ```
+ ///
/// This method acts like `mkdir -p` in Unix because it creates intermediate directories if they do not exist.
pub async fn mkdir(
self: Rc,
@@ -301,27 +582,61 @@ impl PublicDirectory {
time: DateTime,
store: &B,
) -> Result> {
- let node_path_with_dirs = self
- .get_node_path_with_mkdir(path_segments, time, store)
+ let path_nodes = self
+ .get_path_nodes_or_create(path_segments, time, store)
.await?;
Ok(OpResult {
- root_dir: node_path_with_dirs.reconstruct(),
+ root_dir: path_nodes.reconstruct(),
result: (),
})
}
/// Returns the name and metadata of the direct children of a directory.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, MemoryBlockStore, OpResult};
+ /// use libipld::cid::Cid;
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let time = Utc::now();
+ /// let dir = Rc::new(PublicDirectory::new(time));
+ /// let store = MemoryBlockStore::default();
+ ///
+ /// let OpResult { root_dir, .. } = Rc::new(PublicDirectory::new(Utc::now()))
+ /// .write(
+ /// &["pictures".into(), "cats".into(), "tabby.png".into()],
+ /// Cid::default(),
+ /// Utc::now(),
+ /// &store
+ /// )
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir, result } = root_dir
+ /// .ls(&["pictures".into(), "cats".into()], &store)
+ /// .await
+ /// .unwrap();
+ ///
+ /// assert_eq!(result.len(), 1);
+ /// assert_eq!(result[0].0, "tabby.png");
+ /// }
+ /// ```
pub async fn ls(
self: Rc,
path_segments: &[String],
store: &B,
) -> Result>> {
let root_dir = Rc::clone(&self);
- match self.get_node_path(path_segments, store).await? {
- GetNodePathResult::Complete(node_path) => {
+ match self.get_path_nodes(path_segments, store).await? {
+ PathNodesResult::Complete(path_nodes) => {
let mut result = vec![];
- for (name, link) in node_path.tail.userland.iter() {
+ for (name, link) in path_nodes.tail.userland.iter() {
match link.resolve(store).await? {
PublicNode::File(file) => {
result.push((name.clone(), file.metadata.clone()));
@@ -338,6 +653,44 @@ impl PublicDirectory {
}
/// Removes a file or directory from the directory.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, MemoryBlockStore, OpResult};
+ /// use libipld::cid::Cid;
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let time = Utc::now();
+ /// let dir = Rc::new(PublicDirectory::new(time));
+ /// let store = MemoryBlockStore::default();
+ ///
+ /// let OpResult { root_dir, .. } = Rc::new(PublicDirectory::new(Utc::now()))
+ /// .write(
+ /// &["pictures".into(), "cats".into(), "tabby.png".into()],
+ /// Cid::default(),
+ /// Utc::now(),
+ /// &store
+ /// )
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir, .. } = root_dir
+ /// .rm(&["pictures".into(), "cats".into()], &store)
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir, result } = root_dir
+ /// .ls(&["pictures".into()], &store)
+ /// .await
+ /// .unwrap();
+ ///
+ /// assert_eq!(result.len(), 0);
+ /// }
+ /// ```
pub async fn rm(
self: Rc,
path_segments: &[String],
@@ -345,8 +698,8 @@ impl PublicDirectory {
) -> Result> {
let (directory_path, node_name) = utils::split_last(path_segments)?;
- let mut directory_node_path = match self.get_node_path(directory_path, store).await? {
- GetNodePathResult::Complete(node_path) => node_path,
+ let mut directory_node_path = match self.get_path_nodes(directory_path, store).await? {
+ PathNodesResult::Complete(node_path) => node_path,
_ => bail!(FsError::NotFound),
};
@@ -369,36 +722,81 @@ impl PublicDirectory {
/// Moves a file or directory from one path to another.
///
/// This function requires stating the destination name explicitly.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, MemoryBlockStore, OpResult};
+ /// use libipld::cid::Cid;
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let time = Utc::now();
+ /// let dir = Rc::new(PublicDirectory::new(time));
+ /// let store = MemoryBlockStore::default();
+ ///
+ /// let OpResult { root_dir, .. } = Rc::new(PublicDirectory::new(Utc::now()))
+ /// .write(
+ /// &["pictures".into(), "cats".into(), "tabby.png".into()],
+ /// Cid::default(),
+ /// Utc::now(),
+ /// &store
+ /// )
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir, .. } = root_dir
+ /// .basic_mv(
+ /// &["pictures".into(), "cats".into()],
+ /// &["cats".into()],
+ /// Utc::now(),
+ /// &store
+ /// )
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir, result } = root_dir
+ /// .ls(&[], &store)
+ /// .await
+ /// .unwrap();
+ ///
+ /// assert_eq!(result.len(), 2);
+ /// }
+ /// ```
pub async fn basic_mv(
self: Rc,
path_segments_from: &[String],
path_segments_to: &[String],
+ time: DateTime,
store: &B,
) -> Result> {
let root_dir = Rc::clone(&self);
- let (directory_path_nodes, tail) = utils::split_last(path_segments_to)?;
+ let (directory_path_nodes, filename) = utils::split_last(path_segments_to)?;
let OpResult {
root_dir,
result: removed_node,
} = root_dir.rm(path_segments_from, store).await?;
- let mut path_nodes = match root_dir.get_node_path(directory_path_nodes, store).await? {
- GetNodePathResult::Complete(node_path) => node_path,
+ let mut path_nodes = match root_dir.get_path_nodes(directory_path_nodes, store).await? {
+ PathNodesResult::Complete(node_path) => node_path,
_ => bail!(FsError::NotFound),
};
let mut directory = (*path_nodes.tail).clone();
ensure!(
- !directory.userland.contains_key(tail),
+ !directory.userland.contains_key(filename),
FsError::FileAlreadyExists
);
- // TODO(appcypher): We need to update the mtime of the moved node.
+ let removed_node = removed_node.update_mtime(time);
+
directory
.userland
- .insert(tail.clone(), Link::Node(removed_node));
+ .insert(filename.clone(), Link::Node(removed_node));
path_nodes.tail = Rc::new(directory);
@@ -408,58 +806,49 @@ impl PublicDirectory {
})
}
- /// Encode the directory as a CBOR object.
- pub async fn encode(&self, store: &mut B) -> Result> {
- let mut bytes = Vec::new();
-
- // Write the major of the section being written.
- encode::write_u64(
- &mut bytes,
- MajorKind::Map,
- PublicDirectory::FIELDS.len() as u64,
- )?;
-
- // Ordering the fields by name based on RFC-7049 which is also what libipld uses.
- let mut cbor_order: Vec<&'static str> = Vec::from_iter(PublicDirectory::FIELDS);
- cbor_order.sort_unstable_by(|&a, &b| match a.len().cmp(&b.len()) {
- Ordering::Greater => Ordering::Greater,
- Ordering::Less => Ordering::Less,
- Ordering::Equal => a.cmp(b),
- });
-
- // Iterate over the fields.
- for field in cbor_order.iter() {
- // Encode field name.
- field.encode(DagCborCodec, &mut bytes)?;
- // Encode field value.
- match *field {
- "metadata" => {
- self.metadata.encode(DagCborCodec, &mut bytes)?;
- }
- "userland" => {
- let new_userland = {
- let mut tmp = BTreeMap::new();
- for (k, link) in self.userland.iter() {
- let cid = link.seal(store).await?;
- tmp.insert(k.clone(), cid);
- }
- tmp
- };
-
- new_userland.encode(DagCborCodec, &mut bytes)?;
- }
- "previous" => {
- self.previous.encode(DagCborCodec, &mut bytes)?;
- }
- _ => unreachable!(),
- }
- }
-
- Ok(bytes)
- }
-
// TODO(appcypher): Make non recursive.
/// Constructs a tree from directory with `base` as the historical ancestor.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicDirectory, MemoryBlockStore, OpResult};
+ /// use libipld::cid::Cid;
+ /// use std::rc::Rc;
+ /// use chrono::Utc;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let time = Utc::now();
+ /// let dir = Rc::new(PublicDirectory::new(time));
+ /// let mut store = MemoryBlockStore::default();
+ ///
+ /// let OpResult { root_dir: base_root, .. } = Rc::new(PublicDirectory::new(Utc::now()))
+ /// .write(
+ /// &["pictures".into(), "cats".into(), "tabby.png".into()],
+ /// Cid::default(),
+ /// Utc::now(),
+ /// &store
+ /// )
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir: recent_root, .. } = Rc::clone(&base_root)
+ /// .write(
+ /// &["pictures".into(), "cats".into(), "katherine.png".into()],
+ /// Cid::default(),
+ /// Utc::now(),
+ /// &store
+ /// )
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir: derived_root, .. } = recent_root
+ /// .base_history_on(base_root, &mut store)
+ /// .await
+ /// .unwrap();
+ /// }
+ /// ```
pub async fn base_history_on(
self: Rc,
base: Rc,
@@ -493,7 +882,7 @@ impl PublicDirectory {
/// Constructs a tree from directory with `base` as the historical ancestor.
#[async_recursion(?Send)]
- pub async fn base_history_on_helper(
+ pub(crate) async fn base_history_on_helper(
link: &Link,
base_link: &Link,
store: &mut B,
@@ -514,7 +903,7 @@ impl PublicDirectory {
(PublicNode::File(file_rc), PublicNode::File(_)) => {
let mut file = (*file_rc).clone();
file.previous = Some(base_link.seal(store).await?);
- return Ok(Some(Link::Node(PublicNode::File(Rc::new(file)))));
+ return Ok(Some(Link::with_file(Rc::new(file))));
}
_ => {
// One is a file and the other is a directory
@@ -533,10 +922,62 @@ impl PublicDirectory {
}
}
- Ok(Some(Link::Node(PublicNode::Dir(Rc::new(dir)))))
+ Ok(Some(Link::with_dir(Rc::new(dir))))
}
- /// Gets the iterator for walking the history of a directory node.
+ /// Gets a stream for walking the history of a directory node.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::{rc::Rc, pin::Pin};
+ ///
+ /// use wnfs::{PublicDirectory, MemoryBlockStore, OpResult};
+ /// use libipld::cid::Cid;
+ /// use chrono::Utc;
+ /// use futures_util::pin_mut;
+ /// use async_std::stream::StreamExt;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let time = Utc::now();
+ /// let dir = Rc::new(PublicDirectory::new(time));
+ /// let mut store = MemoryBlockStore::default();
+ ///
+ /// let OpResult { root_dir: base_root, .. } = Rc::new(PublicDirectory::new(Utc::now()))
+ /// .write(
+ /// &["pictures".into(), "cats".into(), "tabby.png".into()],
+ /// Cid::default(),
+ /// Utc::now(),
+ /// &store
+ /// )
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir: recent_root, .. } = Rc::clone(&base_root)
+ /// .write(
+ /// &["pictures".into(), "cats".into(), "katherine.png".into()],
+ /// Cid::default(),
+ /// Utc::now(),
+ /// &store
+ /// )
+ /// .await
+ /// .unwrap();
+ ///
+ /// let OpResult { root_dir: derived_root, .. } = recent_root
+ /// .base_history_on(base_root, &mut store)
+ /// .await
+ /// .unwrap();
+ ///
+ /// let history = derived_root.get_history(&store);
+ ///
+ /// pin_mut!(history);
+ ///
+ /// while let Some(cid) = history.next().await {
+ /// println!("previous = {:?}", cid);
+ /// }
+ /// }
+ /// ```
pub fn get_history(
self: Rc,
store: &B,
@@ -549,6 +990,56 @@ impl PublicDirectory {
}
}
}
+
+ /// Encode the directory as a CBOR object.
+ pub(crate) async fn encode(&self, store: &mut B) -> Result> {
+ let mut bytes = Vec::new();
+
+ // Write the major of the section being written.
+ encode::write_u64(
+ &mut bytes,
+ MajorKind::Map,
+ PublicDirectory::FIELDS.len() as u64,
+ )?;
+
+ // Ordering the fields by name based on RFC-7049 which is also what libipld uses.
+ let mut cbor_order: Vec<&'static str> = Vec::from_iter(PublicDirectory::FIELDS);
+ cbor_order.sort_unstable_by(|&a, &b| match a.len().cmp(&b.len()) {
+ Ordering::Greater => Ordering::Greater,
+ Ordering::Less => Ordering::Less,
+ Ordering::Equal => a.cmp(b),
+ });
+
+ // Iterate over the fields.
+ for field in cbor_order.iter() {
+ // Encode field name.
+ field.encode(DagCborCodec, &mut bytes)?;
+ // Encode field value.
+ match *field {
+ "metadata" => {
+ self.metadata.encode(DagCborCodec, &mut bytes)?;
+ }
+ "userland" => {
+ let new_userland = {
+ let mut tmp = BTreeMap::new();
+ for (k, link) in self.userland.iter() {
+ let cid = link.seal(store).await?;
+ tmp.insert(k.clone(), cid);
+ }
+ tmp
+ };
+
+ new_userland.encode(DagCborCodec, &mut bytes)?;
+ }
+ "previous" => {
+ self.previous.encode(DagCborCodec, &mut bytes)?;
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ Ok(bytes)
+ }
}
impl Id for PublicDirectory {
@@ -716,14 +1207,11 @@ mod public_directory_tests {
let time = Utc::now();
let store = MemoryBlockStore::default();
- // on a fresh directory
let OpResult { root_dir, .. } = Rc::new(PublicDirectory::new(time))
- // create a new dir
.mkdir(&["tamedun".into(), "pictures".into()], time, &store)
.await
.unwrap();
- // get the node
let OpResult { result, .. } = root_dir
.get_node(&["tamedun".into(), "pictures".into()], &store)
.await
@@ -856,14 +1344,14 @@ mod public_directory_tests {
let reconstructed = path_nodes.clone().reconstruct();
let result = reconstructed
- .get_node_path(&["Documents".into(), "Apps".into()], &store)
+ .get_path_nodes(&["Documents".into(), "Apps".into()], &store)
.await
.unwrap();
match result {
- GetNodePathResult::MissingLink(_, segment) => panic!("MissingLink {segment}"),
- GetNodePathResult::NotADirectory(_, segment) => panic!("NotADirectory {segment}"),
- GetNodePathResult::Complete(path_nodes_2) => {
+ PathNodesResult::MissingLink(_, segment) => panic!("MissingLink {segment}"),
+ PathNodesResult::NotADirectory(_, segment) => panic!("NotADirectory {segment}"),
+ PathNodesResult::Complete(path_nodes_2) => {
assert_eq!(path_nodes.path.len(), path_nodes_2.path.len());
assert_eq!(path_nodes.path[0].1, path_nodes_2.path[0].1);
assert_eq!(path_nodes.path[1].1, path_nodes_2.path[1].1);
@@ -994,6 +1482,7 @@ mod public_directory_tests {
.basic_mv(
&["pictures".into(), "cats".into()],
&["images".into(), "cats".into()],
+ Utc::now(),
&store,
)
.await
@@ -1033,6 +1522,7 @@ mod public_directory_tests {
.basic_mv(
&["videos".into(), "movies".into()],
&["videos".into(), "movies".into(), "anime".into()],
+ Utc::now(),
&store,
)
.await;
@@ -1052,7 +1542,12 @@ mod public_directory_tests {
.unwrap();
let OpResult { root_dir, .. } = root_dir
- .basic_mv(&["file.txt".into()], &["renamed.txt".into()], &store)
+ .basic_mv(
+ &["file.txt".into()],
+ &["renamed.txt".into()],
+ Utc::now(),
+ &store,
+ )
.await
.unwrap();
@@ -1084,6 +1579,7 @@ mod public_directory_tests {
.basic_mv(
&["movies".into(), "ghibli".into()],
&["file.txt".into()],
+ Utc::now(),
&store,
)
.await;
diff --git a/crates/fs/public/file.rs b/crates/fs/public/file.rs
index d508c8e2..20276234 100644
--- a/crates/fs/public/file.rs
+++ b/crates/fs/public/file.rs
@@ -11,6 +11,18 @@ use crate::{BlockStore, Metadata, UnixFsNodeKind};
use super::Id;
/// A file in a WNFS public file system.
+///
+/// # Examples
+///
+/// ```
+/// use wnfs::{PublicFile, Id};
+/// use chrono::Utc;
+/// use libipld::Cid;
+///
+/// let file = PublicFile::new(Utc::now(), Cid::default());
+///
+/// println!("id = {}", file.get_id());
+/// ```
#[derive(Debug, Clone, PartialEq, Eq, DagCbor)]
pub struct PublicFile {
pub(crate) metadata: Metadata,
@@ -24,6 +36,18 @@ pub struct PublicFile {
impl PublicFile {
/// Creates a new file using the given metadata and CID.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicFile, Id};
+ /// use chrono::Utc;
+ /// use libipld::Cid;
+ ///
+ /// let file = PublicFile::new(Utc::now(), Cid::default());
+ ///
+ /// println!("id = {}", file.get_id());
+ /// ```
pub fn new(time: DateTime, userland: Cid) -> Self {
Self {
metadata: Metadata::new(time, UnixFsNodeKind::File),
@@ -38,6 +62,22 @@ impl PublicFile {
}
/// Stores file in provided block store.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use wnfs::{PublicFile, Id, MemoryBlockStore};
+ /// use chrono::Utc;
+ /// use libipld::Cid;
+ ///
+ /// #[async_std::main]
+ /// async fn main() {
+ /// let mut store = MemoryBlockStore::default();
+ /// let file = PublicFile::new(Utc::now(), Cid::default());
+ ///
+ /// file.store(&mut store).await.unwrap();
+ /// }
+ /// ```
pub async fn store(&self, store: &mut B) -> Result {
let bytes = {
let mut tmp = vec![];
diff --git a/crates/fs/public/link.rs b/crates/fs/public/link.rs
index ae91bba9..8ae6f675 100644
--- a/crates/fs/public/link.rs
+++ b/crates/fs/public/link.rs
@@ -18,16 +18,14 @@ pub enum Link {
}
impl Link {
- // TODO(appcypher): Remove.
/// Creates a new directory node link.
- pub fn with_dir(dir: PublicDirectory) -> Self {
- Link::Node(PublicNode::Dir(Rc::new(dir)))
+ pub fn with_dir(dir: Rc) -> Self {
+ Link::Node(PublicNode::Dir(dir))
}
- // TODO(appcypher): Remove.
/// Creates a new file node link.
- pub fn with_file(file: PublicFile) -> Self {
- Link::Node(PublicNode::File(Rc::new(file)))
+ pub fn with_file(file: Rc) -> Self {
+ Link::Node(PublicNode::File(file))
}
/// Resolves a CID linkin the file system to a node.
@@ -46,6 +44,7 @@ impl Link {
})
}
+ /// Checks if the link matches another link.
pub async fn partial_equal(&self, other: &Self, store: &mut B) -> Result {
match (self, other) {
(Self::Cid(cid), Self::Cid(base_cid)) => {
@@ -102,7 +101,7 @@ mod public_link_tests {
let userland = Cid::default();
- let file = PublicFile::new(time, userland);
+ let file = Rc::new(PublicFile::new(time, userland));
let mut store = MemoryBlockStore::default();
diff --git a/crates/fs/public/node.rs b/crates/fs/public/node.rs
index 45fedd49..7b3e35d8 100644
--- a/crates/fs/public/node.rs
+++ b/crates/fs/public/node.rs
@@ -7,6 +7,7 @@ use std::{
};
use anyhow::{bail, Result};
+use chrono::{DateTime, Utc};
use libipld::{cbor::DagCborCodec, codec::Decode, Cid};
use super::{Id, PublicDirectory, PublicFile};
@@ -20,6 +21,7 @@ pub enum PublicNode {
}
impl PublicNode {
+ /// Checks if the reference of one node is the same as the reference of another node.
pub(crate) fn ptr_eq(&self, other: &PublicNode) -> bool {
match (self, other) {
(Self::File(self_file), Self::File(other_file)) => Rc::ptr_eq(self_file, other_file),
@@ -28,6 +30,22 @@ impl PublicNode {
}
}
+ /// Create node with updated modified time.
+ pub fn update_mtime(&self, time: DateTime) -> Self {
+ match self {
+ Self::File(file) => {
+ let mut file = (**file).clone();
+ file.metadata.unix_fs.modified = time.timestamp();
+ Self::File(Rc::new(file))
+ }
+ Self::Dir(dir) => {
+ let mut dir = (**dir).clone();
+ dir.metadata.unix_fs.modified = time.timestamp();
+ Self::Dir(Rc::new(dir))
+ }
+ }
+ }
+
/// Create node with updated previous pointer value.
pub fn update_previous(&self, cid: Option) -> Self {
match self {
diff --git a/crates/wasm/README.md b/crates/wasm/README.md
index e3111448..555b6e03 100644
--- a/crates/wasm/README.md
+++ b/crates/wasm/README.md
@@ -1,4 +1,72 @@
-## The WebAssembly API
+## Wasm WNFS
+
+This package implements the primitives for creating and manipulating IPLD graphs that encode WNFS.
+
+The core of this project is a WebAssembly binary compiled from the [Rust source code](https://github.com/WebNativeFileSystem/rs-wnfs/tree/main/crates/fs).
+
+## Usage
+
+Creating a new public directory.
+
+```ts
+import { PublicDirectory } from "wnfs";
+
+const time = new Date();
+const dir = new PublicDirectory(time);
+```
+
+The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, an object that implements the BlockStore interface like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/wasm/examples/graph/src/blockstore.ts#L9-L29) can be used.
+
+```ts
+import { MemoryBlockStore } from "./store";
+import { PublicDirectory } from "wnfs";
+
+const time = new Date();
+const dir = new PublicDirectory(time);
+const store = new MemoryBlockStore();
+
+// ...
+```
+
+The WNFS API is immutable, therefore, we need to keep track of the updated root directory after every change.
+
+Each fs operation returns a possibly updated root directory that subsequent changes can be applied on.
+
+```ts
+// ...
+
+// Create a /pictures/cats directory.
+var { rootDir } = await dir.mkdir(["pictures", "cats"], time, store);
+
+// Get a sample CIDv1.
+const cid = Uint8Array.from([
+ 1, 112, 18, 32, 195, 196, 115, 62, 200, 175, 253, 6, 207, 158, 159, 245, 15,
+ 252, 107, 205, 46, 200, 90, 97, 112, 0, 75, 183, 9, 102, 156, 49, 222, 148,
+ 57, 26,
+]);
+
+// Add a file to /pictures/cats.
+var { rootDir } = await rootDir.write(
+ ["pictures", "cats", "tabby.png"],
+ cid,
+ time,
+ store
+);
+
+// Create and add a file to /pictures/dogs directory.
+var { rootDir } = await root.write(
+ ["pictures", "dogs", "billie.jpeg"],
+ cid,
+ time,
+ store
+);
+
+// Delete /pictures/cats directory.
+var { rootDir } = await rootDir.rm(["pictures", "cats"], store);
+
+// List all files in /pictures directory.
+var { result } = await rootDir.ls(["pictures"], store);
+```
## Building the Project
@@ -25,3 +93,21 @@
```bash
yarn playwright test
```
+
+## Publishing Package
+
+- Generate the compilation artifacts in `pkg` directory
+
+ ```bash
+ wasm-pack init
+ ```
+
+- Publish from the `pkg` directory
+
+ ```bash
+ cd pkg
+ ```
+
+ ```bash
+ npm publish --access=public
+ ```
diff --git a/crates/wasm/fs/public/directory.rs b/crates/wasm/fs/public/directory.rs
index 895f8f76..48275343 100644
--- a/crates/wasm/fs/public/directory.rs
+++ b/crates/wasm/fs/public/directory.rs
@@ -35,10 +35,6 @@ impl PublicDirectory {
}
/// Follows a path and fetches the node at the end of the path.
- ///
- /// If path is empty, this returns a cloned directory based on `self`.
- ///
- /// If `diverge` is true, this will clone the spine of the path.
#[wasm_bindgen(js_name = "getNode")]
pub fn get_node(&self, path_segments: &Array, store: BlockStore) -> JsResult {
let directory = Rc::clone(&self.0);
@@ -127,8 +123,6 @@ impl PublicDirectory {
}
/// Removes a file or directory from the directory.
- ///
- /// Rather than mutate the directory directly, we create a new directory and return it.
pub fn rm(&self, path_segments: &Array, store: BlockStore) -> JsResult {
let directory = Rc::clone(&self.0);
let store = ForeignBlockStore(store);
@@ -148,8 +142,6 @@ impl PublicDirectory {
}
/// Writes a file to the directory.
- ///
- /// Rather than mutate the directory directly, we create a new directory and return it.
pub fn write(
&self,
path_segments: &Array,
@@ -180,16 +172,18 @@ impl PublicDirectory {
&self,
path_segments_from: &Array,
path_segments_to: &Array,
+ time: &Date,
store: BlockStore,
) -> JsResult {
let directory = self.0.clone();
let store = ForeignBlockStore(store);
+ let time = DateTime::::from(time);
let path_segments_from = utils::convert_path_segments(path_segments_from)?;
let path_segments_to = utils::convert_path_segments(path_segments_to)?;
Ok(future_to_promise(async move {
let WnfsOpResult { root_dir, .. } = directory
- .basic_mv(&path_segments_from, &path_segments_to, &store)
+ .basic_mv(&path_segments_from, &path_segments_to, time, &store)
.await
.map_err(|e| Error::new(&format!("Cannot create directory: {e}")))?;
@@ -199,8 +193,6 @@ impl PublicDirectory {
/// Creates a new directory at the specified path.
///
- /// If path is empty, this returns a cloned directory based on `self`.
- ///
/// This method acts like `mkdir -p` in Unix because it creates intermediate directories if they do not exist.
pub fn mkdir(
&self,
@@ -223,7 +215,7 @@ impl PublicDirectory {
}))
}
- /// Converts a directory to a node.
+ /// Converts directory to a node.
#[wasm_bindgen(js_name = "asNode")]
pub fn as_node(&self) -> PublicNode {
PublicNode(WnfsPublicNode::Dir(self.0.clone()))
diff --git a/crates/wasm/package.json b/crates/wasm/package.json
index a27c3435..63e6021f 100644
--- a/crates/wasm/package.json
+++ b/crates/wasm/package.json
@@ -1,6 +1,24 @@
{
- "scripts": {
- "build": "webpack"
+ "name": "wnfs",
+ "version": "0.1.1",
+ "homepage": "https://guide.fission.codes",
+ "license": "Apache-2.0",
+ "author": {
+ "name": "Stephen Akinyemi",
+ "email": "appcypher@fission.codes"
+ },
+ "keywords": [
+ "wnfs",
+ "web",
+ "webnative",
+ "filesystem",
+ "cryptography",
+ "web-programming",
+ "wasm"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/WebNativeFileSystem/rs-wnfs/tree/main/crates/wasm"
},
"devDependencies": {
"@playwright/test": "^1.21.1",
From 480cc1243a6cc83c2e11af6f6dadaa611a4e5310 Mon Sep 17 00:00:00 2001
From: Stephen Akinyemi
Date: Fri, 20 May 2022 13:51:29 +0100
Subject: [PATCH 2/8] Add license file to wasm crate
---
crates/wasm/LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 201 insertions(+)
create mode 100644 crates/wasm/LICENSE
diff --git a/crates/wasm/LICENSE b/crates/wasm/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/crates/wasm/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
From 7db77dfe0180945f1fab8e4986424579eed4bbbd Mon Sep 17 00:00:00 2001
From: Stephen Akinyemi
Date: Fri, 20 May 2022 14:08:51 +0100
Subject: [PATCH 3/8] Fix README
---
Cargo.lock | 4 +-
crates/fs/Cargo.toml | 4 +-
crates/fs/README.md | 120 ++++++++++++++++++++++++++++++++++++++++-
crates/wasm/Cargo.toml | 4 +-
crates/wasm/README.md | 9 ++++
5 files changed, 134 insertions(+), 7 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 9fef545d..8fc46311 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1540,7 +1540,7 @@ dependencies = [
[[package]]
name = "wasm-wnfs"
-version = "0.1.1"
+version = "0.1.2"
dependencies = [
"anyhow",
"async-trait",
@@ -1665,7 +1665,7 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
[[package]]
name = "wnfs"
-version = "0.1.1"
+version = "0.1.2"
dependencies = [
"anyhow",
"async-recursion",
diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml
index 9d0bf5d2..a537e50f 100644
--- a/crates/fs/Cargo.toml
+++ b/crates/fs/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "wnfs"
-version = "0.1.1"
+version = "0.1.2"
description = "WebNative filesystem core implementation"
keywords = ["wnfs", "webnative", "ipfs", "decentralisation"]
categories = [
@@ -12,7 +12,7 @@ categories = [
license-file = "../../LICENSE"
readme = "README.md"
edition = "2021"
-repository = "https://github.com/fission-suite/rs-wnfs"
+repository = "https://github.com/WebNativeFileSystem/rs-wnfs/tree/main/crates/fs"
homepage = "https://fission.codes"
authors = ["The Fission Authors"]
diff --git a/crates/fs/README.md b/crates/fs/README.md
index 8b145749..3210a1e8 100644
--- a/crates/fs/README.md
+++ b/crates/fs/README.md
@@ -1,4 +1,122 @@
-## The FileSystem
+
+
+:warning: Work in progress :warning:
+
+##
+
+This crate is a Rust implementation of the primitives for creating and manipulating IPLD graphs that encode WNFS.
+
+A goal of the project is to be easily compiled to WebAssembly to be used in the browsers or other environments.
+
+## Outline
+
+- [Usage](#usage)
+- [Building the Project](#building-the-project)
+- [Testing the Project](#testing-the-project)
+
+## Usage
+
+Creating a new public directory.
+
+```rs
+use wnfs::{PublicDirectory, Id};
+use chrono::Utc;
+
+let dir = PublicDirectory::new(Utc::now());
+println!("id = {}", dir.get_id());
+```
+
+The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, an object that implements the BlockStore trait like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/fs/common/blockstore.rs#L42-L62) can be used.
+
+```rs
+use wnfs::{PublicDirectory, MemoryBlockStore, ipld::Cid};
+use chrono::Utc;
+
+let dir = PublicDirectory::new(Utc::now());
+let store = MemoryBlockStore::default();
+
+// ...
+```
+
+The WNFS API is immutable, therefore, we need to keep track of the updated root directory after every change.
+
+Each fs operation returns a possibly updated root directory that subsequent changes can be applied on.
+
+```rs
+// ...
+
+let dir = Rc::new(dir);
+
+// Create a /pictures/cats directory.
+let OpResult { root_dir, .. } = dir
+ .mkdir(&["pictures".into(), "cats".into()], time, &store)
+ .await
+ .unwrap();
+
+// Get a sample CIDv1.
+let cid = Cid::default();
+
+// Add a file to /pictures/cats.
+let OpResult { root_dir, .. } = root_dir
+ .write(
+ &["pictures".into(), "cats".into(), "tabby.png".into()],
+ cid,
+ time,
+ &store,
+ )
+ .await
+ .unwrap();
+
+// Create and add a file to /pictures/dogs directory.
+let OpResult { root_dir, .. } = root_dir
+ .write(
+ &["pictures".into(), "cats".into(), "billie.jpeg".into()],
+ cid,
+ time,
+ &store,
+ )
+ .await
+ .unwrap();
+
+// Delete /pictures/cats directory.
+let OpResult { root_dir, .. } = root_dir
+ .rm(&["pictures".into(), "cats".into()], &store)
+ .await
+ .unwrap();
+
+// List all files in /pictures directory.
+let OpResult { result, .. } = root_dir
+ .ls(&["pictures".into()], &store)
+ .await
+ .unwrap();
+```
## Building the Project
diff --git a/crates/wasm/Cargo.toml b/crates/wasm/Cargo.toml
index a6d53158..c4c5b05c 100644
--- a/crates/wasm/Cargo.toml
+++ b/crates/wasm/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "wasm-wnfs"
-version = "0.1.1"
+version = "0.1.2"
description = "WebNative filesystem WebAssembly API"
keywords = ["wnfs", "webnative", "ipfs", "decentralisation"]
categories = [
@@ -12,7 +12,7 @@ categories = [
license-file = "LICENSE"
readme = "README.md"
edition = "2021"
-repository = "https://github.com/fission-suite/rs-wnfs"
+repository = "https://github.com/WebNativeFileSystem/rs-wnfs/tree/main/crates/wasm"
homepage = "https://fission.codes"
authors = ["The Fission Authors"]
diff --git a/crates/wasm/README.md b/crates/wasm/README.md
index 555b6e03..ffac9b73 100644
--- a/crates/wasm/README.md
+++ b/crates/wasm/README.md
@@ -4,6 +4,13 @@ This package implements the primitives for creating and manipulating IPLD graphs
The core of this project is a WebAssembly binary compiled from the [Rust source code](https://github.com/WebNativeFileSystem/rs-wnfs/tree/main/crates/fs).
+## Outline
+
+- [Usage](#usage)
+- [Building the Project](#building-the-project)
+- [Testing the Project](#testing-the-project)
+- [Publishing Package](#publishing-package)
+
## Usage
Creating a new public directory.
@@ -88,6 +95,8 @@ var { result } = await rootDir.ls(["pictures"], store);
wasm-pack build --target web
```
+## Testing the Project
+
- Run tests
```bash
From f46636377b0f85f859105817c97c7cfb0fe041e3 Mon Sep 17 00:00:00 2001
From: Stephen Akinyemi
Date: Mon, 23 May 2022 16:47:53 +0100
Subject: [PATCH 4/8] Fix node build, wnfs script and bump version
---
Cargo.lock | 4 +-
README.md | 26 ++++---
crates/fs/Cargo.toml | 4 +-
crates/fs/README.md | 8 +--
crates/wasm/Cargo.toml | 8 +--
crates/wasm/README.md | 24 ++++---
crates/wasm/package.json | 21 ------
scripts/wnfs.sh | 146 +++++++++++++++++++++++++++++++--------
8 files changed, 157 insertions(+), 84 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 8fc46311..c3725cd9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1540,7 +1540,7 @@ dependencies = [
[[package]]
name = "wasm-wnfs"
-version = "0.1.2"
+version = "0.1.5"
dependencies = [
"anyhow",
"async-trait",
@@ -1665,7 +1665,7 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
[[package]]
name = "wnfs"
-version = "0.1.2"
+version = "0.1.5"
dependencies = [
"anyhow",
"async-recursion",
diff --git a/README.md b/README.md
index f76a64f4..81802f04 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ A goal of the project is to be easily compiled to WebAssembly to be used in the
Creating a new public directory.
-```rs
+```rust
use wnfs::{PublicDirectory, Id};
use chrono::Utc;
@@ -55,7 +55,7 @@ println!("id = {}", dir.get_id());
The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, an object that implements the BlockStore trait like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/fs/common/blockstore.rs#L42-L62) can be used.
-```rs
+```rust
use wnfs::{PublicDirectory, MemoryBlockStore, ipld::Cid};
use chrono::Utc;
@@ -69,7 +69,7 @@ The WNFS API is immutable, therefore, we need to keep track of the updated root
Each fs operation returns a possibly updated root directory that subsequent changes can be applied on.
-```rs
+```rust
// ...
let dir = Rc::new(dir);
@@ -169,9 +169,9 @@ let OpResult { result, .. } = root_dir
-- **The _wnfs_ Helper Script**
+- **The _wnfs_ Command**
- If you are on a Unix platform, you can optionally install the `wnfs` script.
+ You can optionally set up the `wnfs` script.
Read more
@@ -182,7 +182,7 @@ let OpResult { result, .. } = root_dir
sh script/wnfs.sh setup
```
- - This lets you run the `wnfs.sh` script with just the `wnfs` command.
+ - This lets you run the `wnfs.sh` script as a command.
```bash
wnfs help
@@ -206,8 +206,16 @@ let OpResult { result, .. } = root_dir
- Build the project
+ Check [REQUIREMENTS](#requirements) on how to set up the `wnfs` command.
+
+ ```bash
+ wnfs build --all
+ ```
+
+- You can also build for specific crates
+
```bash
- sh scripts/wnfs.sh build
+ wnfs build --wasm
```
## Testing the Project
@@ -215,11 +223,11 @@ let OpResult { result, .. } = root_dir
- Run all tests
```bash
- sh scripts/wnfs.sh test
+ wnfs test --all
```
- Show code coverage
```bash
- sh scripts/wnfs.sh coverage
+ wnfs coverage
```
diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml
index a537e50f..bdd5eff7 100644
--- a/crates/fs/Cargo.toml
+++ b/crates/fs/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "wnfs"
-version = "0.1.2"
+version = "0.1.5"
description = "WebNative filesystem core implementation"
keywords = ["wnfs", "webnative", "ipfs", "decentralisation"]
categories = [
@@ -10,7 +10,7 @@ categories = [
"wasm",
]
license-file = "../../LICENSE"
-readme = "README.md"
+readme = "Apache-2.0"
edition = "2021"
repository = "https://github.com/WebNativeFileSystem/rs-wnfs/tree/main/crates/fs"
homepage = "https://fission.codes"
diff --git a/crates/fs/README.md b/crates/fs/README.md
index 3210a1e8..5f1a2cce 100644
--- a/crates/fs/README.md
+++ b/crates/fs/README.md
@@ -27,8 +27,6 @@
-:warning: Work in progress :warning:
-
##
This crate is a Rust implementation of the primitives for creating and manipulating IPLD graphs that encode WNFS.
@@ -45,7 +43,7 @@ A goal of the project is to be easily compiled to WebAssembly to be used in the
Creating a new public directory.
-```rs
+```rust
use wnfs::{PublicDirectory, Id};
use chrono::Utc;
@@ -55,7 +53,7 @@ println!("id = {}", dir.get_id());
The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, an object that implements the BlockStore trait like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/fs/common/blockstore.rs#L42-L62) can be used.
-```rs
+```rust
use wnfs::{PublicDirectory, MemoryBlockStore, ipld::Cid};
use chrono::Utc;
@@ -69,7 +67,7 @@ The WNFS API is immutable, therefore, we need to keep track of the updated root
Each fs operation returns a possibly updated root directory that subsequent changes can be applied on.
-```rs
+```rust
// ...
let dir = Rc::new(dir);
diff --git a/crates/wasm/Cargo.toml b/crates/wasm/Cargo.toml
index c4c5b05c..4b2c5d63 100644
--- a/crates/wasm/Cargo.toml
+++ b/crates/wasm/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "wasm-wnfs"
-version = "0.1.2"
-description = "WebNative filesystem WebAssembly API"
+version = "0.1.5"
+description = "WebNative Filesystem API (WebAssembly)"
keywords = ["wnfs", "webnative", "ipfs", "decentralisation"]
categories = [
"filesystem",
@@ -9,7 +9,7 @@ categories = [
"web-programming",
"wasm",
]
-license-file = "LICENSE"
+license = "Apache-2.0"
readme = "README.md"
edition = "2021"
repository = "https://github.com/WebNativeFileSystem/rs-wnfs/tree/main/crates/wasm"
@@ -18,8 +18,6 @@ authors = ["The Fission Authors"]
[dependencies]
wnfs = { path = "../fs", version = "0.1.0" }
-# serde_json = "1.0"
-# serde = { version = "1.0", features = ["derive"] }
wasm-bindgen = { version = "0.2", optional = true, features = ["serde-serialize"] }
wasm-bindgen-futures = { version = "0.4", optional = true }
js-sys = { version = "0.3", optional = true }
diff --git a/crates/wasm/README.md b/crates/wasm/README.md
index ffac9b73..8f656fd2 100644
--- a/crates/wasm/README.md
+++ b/crates/wasm/README.md
@@ -7,7 +7,7 @@ The core of this project is a WebAssembly binary compiled from the [Rust source
## Outline
- [Usage](#usage)
-- [Building the Project](#building-the-project)
+- [Setting up the project](#setting-up-the-project)
- [Testing the Project](#testing-the-project)
- [Publishing Package](#publishing-package)
@@ -75,7 +75,7 @@ var { rootDir } = await rootDir.rm(["pictures", "cats"], store);
var { result } = await rootDir.ls(["pictures"], store);
```
-## Building the Project
+## Setting up the Project
- Install `wasm-pack`
@@ -83,7 +83,13 @@ var { result } = await rootDir.ls(["pictures"], store);
cargo install wasm-pack
```
-- Install playwrigth binaries
+- Install dependencies
+
+ ```bash
+ yarn
+ ```
+
+- Install playwright binaries
```bash
npx playwright install
@@ -92,7 +98,7 @@ var { result } = await rootDir.ls(["pictures"], store);
- Build project
```bash
- wasm-pack build --target web
+ wasm-pack build
```
## Testing the Project
@@ -105,18 +111,14 @@ var { result } = await rootDir.ls(["pictures"], store);
## Publishing Package
-- Generate the compilation artifacts in `pkg` directory
+- Build the project
```bash
- wasm-pack init
+ wnfs build --wasm
```
- Publish from the `pkg` directory
```bash
- cd pkg
- ```
-
- ```bash
- npm publish --access=public
+ wasm-pack publish --nodejs
```
diff --git a/crates/wasm/package.json b/crates/wasm/package.json
index 63e6021f..fbf6035e 100644
--- a/crates/wasm/package.json
+++ b/crates/wasm/package.json
@@ -1,25 +1,4 @@
{
- "name": "wnfs",
- "version": "0.1.1",
- "homepage": "https://guide.fission.codes",
- "license": "Apache-2.0",
- "author": {
- "name": "Stephen Akinyemi",
- "email": "appcypher@fission.codes"
- },
- "keywords": [
- "wnfs",
- "web",
- "webnative",
- "filesystem",
- "cryptography",
- "web-programming",
- "wasm"
- ],
- "repository": {
- "type": "git",
- "url": "https://github.com/WebNativeFileSystem/rs-wnfs/tree/main/crates/wasm"
- },
"devDependencies": {
"@playwright/test": "^1.21.1",
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
diff --git a/scripts/wnfs.sh b/scripts/wnfs.sh
index 5076be02..172176e4 100755
--- a/scripts/wnfs.sh
+++ b/scripts/wnfs.sh
@@ -1,12 +1,19 @@
#!/bin/bash
+set -e
# PATHS
# Get current working directory
current_dir=`pwd`
-# Get the absolute path of where script is running from
-script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd)"
-script_path="$script_dir/wnfs.sh"
+# Get the absolute path where current script is running from.
+script_path=$(readlink -f $(which $0))
+
+# Get the canonical directory of script.
+if [[ -L $script_path ]]; then
+ script_dir=$(dirname $(readlink -f $script_path))
+else
+ script_dir=$(dirname $script_path)
+fi
# RETURN VARIABLE
ret=""
@@ -17,6 +24,7 @@ args="${@:2}" # All arguments except the first
# COLORS
red='\033[0;31m'
green='\033[0;32m'
+purple='\033[0;35m'
none='\033[0m'
# DESCRIPTION:
@@ -33,6 +41,9 @@ main() {
coverage )
coverage
;;
+ publish )
+ publish
+ ;;
setup )
setup
;;
@@ -58,11 +69,12 @@ help() {
echo " wnfs [COMMAND] [...args]"
echo ""
echo "COMMAND:"
- echo " * build - build project"
- echo " * test - run tests"
- echo " * coverage - show code coverage"
- echo " * setup - install wnfs script"
- echo " * -h, help - print this help message"
+ echo " * build [--wnfs|--wasm|--all] - build projects"
+ echo " * test [--wnfs|--wasm|--all] - run tests"
+ echo " * publish [--wnfs|--wasm|--all] - publish packages"
+ echo " * coverage [--wnfs|--wasm|--all] - show code coverage"
+ echo " * setup - install wnfs script"
+ echo " * help - print this help message"
echo ""
echo ""
}
@@ -75,38 +87,82 @@ help() {
# Builds the project.
#
# USAGE:
-# wnfs build
+# wnfs build [--wnfs|--wasm|--all]
#
build() {
- display_header "💿 BUILDING WNFS PROJECT"
+ if check_flag --wnfs; then
+ build_wnfs
+ elif check_flag --wasm; then
+ build_wasm
+ else
+ build_wnfs
+ build_wasm
+ fi
+}
+
+build_wnfs() {
+ display_header "💿 | BUILDING WNFS PROJECT | 💿"
cargo build --release
+}
- display_header "💿 BUILDING WASM-WNFS PROJECT"
- cd crates/wasm && wasm-pack build --target web --release
+build_wasm() {
+ display_header "💿 | BUILDING WASM-WNFS PROJECT | 💿"
+ echo "script_dir = $script_dir"
+ cd $script_dir/../crates/wasm
+ wasm-pack build --target nodejs
+ sed -i ".bak" -e 's/"name": "wasm-wnfs"/"name": "wnfs"/g' pkg/package.json
+ rm pkg/package.json.bak
}
# DESCRIPTION:
# Runs tests.
#
# USAGE:
-# wnfs test
+# wnfs test [--wnfs|--wasm|--all]
#
test() {
- display_header "🧪 RUNNING WNFS TESTS"
+ if check_flag --wnfs; then
+ test_wnfs
+ elif check_flag --wasm; then
+ test_wasm
+ else
+ test_wnfs
+ test_wasm
+ fi
+}
+
+test_wnfs() {
+ display_header "🧪 | RUNNING WNFS TESTS | 🧪"
cargo test -p wnfs --release -- --nocapture
+}
- display_header "🧪 RUNNING WASM-WNFS TESTS"
- cd crates/wasm && yarn playwright test
+test_wasm() {
+ display_header "🧪 | RUNNING WASM-WNFS TESTS | 🧪"
+ echo "script_dir = $script_dir"
+ cd $script_dir/../crates/wasm
+ yarn playwright test
}
# DESCRIPTION:
# Shows the code coverage of the project
#
# USAGE:
-# wnfs coverage
+# wnfs coverage [--wnfs|--wasm|--all]
#
coverage() {
- displayln "coverage command not implemented yet"
+ errorln "coverage command not implemented yet"
+ exit 1
+}
+
+# DESCRIPTION:
+# Publishes the project.
+#
+# USAGE:
+# wnfs publish [--wnfs|--wasm|--all]
+#
+publish() {
+ errorln "publish command not implemented yet"
+ exit 1
}
#------------------------------------------------------------------------------
@@ -150,36 +206,68 @@ get_flag_value() {
fi
}
+# DESCRIPTION:
+# Checks if the flag is present in the list of arguments
+#
+check_flag() {
+ local found=1
+ local key=$1
+
+ # For every argument in the list of arguments
+ for arg in $args; do
+ # Check if any of the argument matches the key provided
+ if [[ $arg = $key ]]; then
+ found=0
+ break
+ fi
+ done
+
+ return $found
+}
+
# DESCRIPTION:
# Sets up the cript by making it excutable and available system wide
#
setup() {
- display "Make script executable"
+ displayln "Make script executable"
chmod u+x $script_path
- display "Drop a link to it in /usr/local/bin"
- ln -s $script_path /usr/local/bin/wnfs
+ displayln "Drop a link to it in /usr/local/bin"
+ if ln -s $script_path /usr/local/bin/wnfs; then
+ successln "Successfully installed"
+ else
+ local result=$?
+ errorln "Failed to install"
+ exit $result
+ fi
}
# DESCRIPTION:
-# A custom print function
+# Prints a message.
#
-display() {
- printf "::: $1 :::\n"
+displayln() {
+ printf "\n::: $1 :::\n"
}
# DESCRIPTION:
-# A custom print function that starts its output with a newline
+# Prints an error message.
#
-displayln() {
- printf "\n::: $1 :::\n"
+errorln() {
+ printf "\n${red}::: $1 :::${none}\n\n"
+}
+
+# DESCRIPTION:
+# Prints an success message.
+#
+successln() {
+ printf "\n${green}::: $1 :::${none}\n\n"
}
# DESCRIPTION:
-# A custom print function for headers.
+# Prints a header message.
#
display_header() {
- printf "\n${green}$1${none}\n\n"
+ printf "\n${purple}$1${none}\n\n"
}
main $@
From 17382a732089756669272bfa43cd703bef3e584f Mon Sep 17 00:00:00 2001
From: Stephen Akinyemi
Date: Mon, 23 May 2022 17:29:33 +0100
Subject: [PATCH 5/8] Add license file to crates/fs
---
crates/fs/Cargo.toml | 4 +-
crates/fs/LICENSE | 201 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 203 insertions(+), 2 deletions(-)
create mode 100644 crates/fs/LICENSE
diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml
index bdd5eff7..7036cb01 100644
--- a/crates/fs/Cargo.toml
+++ b/crates/fs/Cargo.toml
@@ -9,8 +9,8 @@ categories = [
"web-programming",
"wasm",
]
-license-file = "../../LICENSE"
-readme = "Apache-2.0"
+license = "Apache-2.0"
+readme = "README.md"
edition = "2021"
repository = "https://github.com/WebNativeFileSystem/rs-wnfs/tree/main/crates/fs"
homepage = "https://fission.codes"
diff --git a/crates/fs/LICENSE b/crates/fs/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/crates/fs/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
From e60649dfc0d60ea6f36e5f87180109b52b5c5d44 Mon Sep 17 00:00:00 2001
From: Stephen Akinyemi
Date: Mon, 23 May 2022 18:38:37 +0100
Subject: [PATCH 6/8] Fix packages docs
---
Cargo.lock | 4 +-
README.md | 115 ++++++++++++++++++++++-------------------
crates/fs/Cargo.toml | 2 +-
crates/fs/README.md | 115 ++++++++++++++++++++++-------------------
crates/wasm/Cargo.toml | 2 +-
crates/wasm/README.md | 12 +++--
6 files changed, 137 insertions(+), 113 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index c3725cd9..2573ae96 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1540,7 +1540,7 @@ dependencies = [
[[package]]
name = "wasm-wnfs"
-version = "0.1.5"
+version = "0.1.6"
dependencies = [
"anyhow",
"async-trait",
@@ -1665,7 +1665,7 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
[[package]]
name = "wnfs"
-version = "0.1.5"
+version = "0.1.6"
dependencies = [
"anyhow",
"async-recursion",
diff --git a/README.md b/README.md
index 81802f04..9ed62e9f 100644
--- a/README.md
+++ b/README.md
@@ -47,21 +47,26 @@ Creating a new public directory.
```rust
use wnfs::{PublicDirectory, Id};
+
+use async_std::main;
use chrono::Utc;
-let dir = PublicDirectory::new(Utc::now());
-println!("id = {}", dir.get_id());
+#[async_std::main]
+async fn main() {
+ let dir = PublicDirectory::new(Utc::now());
+ println!("id = {}", dir.get_id());
+}
```
-The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, an object that implements the BlockStore trait like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/fs/common/blockstore.rs#L42-L62) can be used.
+The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, a type that implements the BlockStore trait like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/fs/common/blockstore.rs#L42-L62) can be used.
```rust
-use wnfs::{PublicDirectory, MemoryBlockStore, ipld::Cid};
-use chrono::Utc;
+use wnfs::{MemoryBlockStore, PublicDirectory, OpResult, ipld::Cid};
-let dir = PublicDirectory::new(Utc::now());
-let store = MemoryBlockStore::default();
+use async_std::main;
+use chrono::Utc;
+use std::rc::Rc;
// ...
```
@@ -71,51 +76,57 @@ Each fs operation returns a possibly updated root directory that subsequent chan
```rust
// ...
-
-let dir = Rc::new(dir);
-
-// Create a /pictures/cats directory.
-let OpResult { root_dir, .. } = dir
- .mkdir(&["pictures".into(), "cats".into()], time, &store)
- .await
- .unwrap();
-
-// Get a sample CIDv1.
-let cid = Cid::default();
-
-// Add a file to /pictures/cats.
-let OpResult { root_dir, .. } = root_dir
- .write(
- &["pictures".into(), "cats".into(), "tabby.png".into()],
- cid,
- time,
- &store,
- )
- .await
- .unwrap();
-
-// Create and add a file to /pictures/dogs directory.
-let OpResult { root_dir, .. } = root_dir
- .write(
- &["pictures".into(), "cats".into(), "billie.jpeg".into()],
- cid,
- time,
- &store,
- )
- .await
- .unwrap();
-
-// Delete /pictures/cats directory.
-let OpResult { root_dir, .. } = root_dir
- .rm(&["pictures".into(), "cats".into()], &store)
- .await
- .unwrap();
-
-// List all files in /pictures directory.
-let OpResult { result, .. } = root_dir
- .ls(&["pictures".into()], &store)
- .await
- .unwrap();
+#[async_std::main]
+async fn main() {
+ let time = Utc::now();
+ let dir = Rc::new(PublicDirectory::new(time));
+ let store = MemoryBlockStore::default();
+
+ // Create a /pictures/cats directory.
+ let OpResult { root_dir, .. } = dir
+ .mkdir(&["pictures".into(), "cats".into()], time, &store)
+ .await
+ .unwrap();
+
+ // Get a sample CIDv1.
+ let cid = Cid::default();
+
+ // Add a file to /pictures/cats.
+ let OpResult { root_dir, .. } = root_dir
+ .write(
+ &["pictures".into(), "cats".into(), "tabby.png".into()],
+ cid,
+ time,
+ &store,
+ )
+ .await
+ .unwrap();
+
+ // Create and add a file to /pictures/dogs directory.
+ let OpResult { root_dir, .. } = root_dir
+ .write(
+ &["pictures".into(), "dogs".into(), "billie.jpeg".into()],
+ cid,
+ time,
+ &store,
+ )
+ .await
+ .unwrap();
+
+ // Delete /pictures/cats directory.
+ let OpResult { root_dir, .. } = root_dir
+ .rm(&["pictures".into(), "cats".into()], &store)
+ .await
+ .unwrap();
+
+ // List all files in /pictures directory.
+ let OpResult { result, .. } = root_dir
+ .ls(&["pictures".into()], &store)
+ .await
+ .unwrap();
+
+ println!("Files in /pictures: {:#?}", result);
+}
```
## Building the Project
diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml
index 7036cb01..7a529647 100644
--- a/crates/fs/Cargo.toml
+++ b/crates/fs/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "wnfs"
-version = "0.1.5"
+version = "0.1.6"
description = "WebNative filesystem core implementation"
keywords = ["wnfs", "webnative", "ipfs", "decentralisation"]
categories = [
diff --git a/crates/fs/README.md b/crates/fs/README.md
index 5f1a2cce..a5f0c2e6 100644
--- a/crates/fs/README.md
+++ b/crates/fs/README.md
@@ -45,21 +45,26 @@ Creating a new public directory.
```rust
use wnfs::{PublicDirectory, Id};
+
+use async_std::main;
use chrono::Utc;
-let dir = PublicDirectory::new(Utc::now());
-println!("id = {}", dir.get_id());
+#[async_std::main]
+async fn main() {
+ let dir = PublicDirectory::new(Utc::now());
+ println!("id = {}", dir.get_id());
+}
```
-The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, an object that implements the BlockStore trait like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/fs/common/blockstore.rs#L42-L62) can be used.
+The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, a type that implements the BlockStore trait like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/fs/common/blockstore.rs#L42-L62) can be used.
```rust
-use wnfs::{PublicDirectory, MemoryBlockStore, ipld::Cid};
-use chrono::Utc;
+use wnfs::{MemoryBlockStore, PublicDirectory, OpResult, ipld::Cid};
-let dir = PublicDirectory::new(Utc::now());
-let store = MemoryBlockStore::default();
+use async_std::main;
+use chrono::Utc;
+use std::rc::Rc;
// ...
```
@@ -69,51 +74,57 @@ Each fs operation returns a possibly updated root directory that subsequent chan
```rust
// ...
-
-let dir = Rc::new(dir);
-
-// Create a /pictures/cats directory.
-let OpResult { root_dir, .. } = dir
- .mkdir(&["pictures".into(), "cats".into()], time, &store)
- .await
- .unwrap();
-
-// Get a sample CIDv1.
-let cid = Cid::default();
-
-// Add a file to /pictures/cats.
-let OpResult { root_dir, .. } = root_dir
- .write(
- &["pictures".into(), "cats".into(), "tabby.png".into()],
- cid,
- time,
- &store,
- )
- .await
- .unwrap();
-
-// Create and add a file to /pictures/dogs directory.
-let OpResult { root_dir, .. } = root_dir
- .write(
- &["pictures".into(), "cats".into(), "billie.jpeg".into()],
- cid,
- time,
- &store,
- )
- .await
- .unwrap();
-
-// Delete /pictures/cats directory.
-let OpResult { root_dir, .. } = root_dir
- .rm(&["pictures".into(), "cats".into()], &store)
- .await
- .unwrap();
-
-// List all files in /pictures directory.
-let OpResult { result, .. } = root_dir
- .ls(&["pictures".into()], &store)
- .await
- .unwrap();
+#[async_std::main]
+async fn main() {
+ let time = Utc::now();
+ let dir = Rc::new(PublicDirectory::new(time));
+ let store = MemoryBlockStore::default();
+
+ // Create a /pictures/cats directory.
+ let OpResult { root_dir, .. } = dir
+ .mkdir(&["pictures".into(), "cats".into()], time, &store)
+ .await
+ .unwrap();
+
+ // Get a sample CIDv1.
+ let cid = Cid::default();
+
+ // Add a file to /pictures/cats.
+ let OpResult { root_dir, .. } = root_dir
+ .write(
+ &["pictures".into(), "cats".into(), "tabby.png".into()],
+ cid,
+ time,
+ &store,
+ )
+ .await
+ .unwrap();
+
+ // Create and add a file to /pictures/dogs directory.
+ let OpResult { root_dir, .. } = root_dir
+ .write(
+ &["pictures".into(), "dogs".into(), "billie.jpeg".into()],
+ cid,
+ time,
+ &store,
+ )
+ .await
+ .unwrap();
+
+ // Delete /pictures/cats directory.
+ let OpResult { root_dir, .. } = root_dir
+ .rm(&["pictures".into(), "cats".into()], &store)
+ .await
+ .unwrap();
+
+ // List all files in /pictures directory.
+ let OpResult { result, .. } = root_dir
+ .ls(&["pictures".into()], &store)
+ .await
+ .unwrap();
+
+ println!("Files in /pictures: {:#?}", result);
+}
```
## Building the Project
diff --git a/crates/wasm/Cargo.toml b/crates/wasm/Cargo.toml
index 4b2c5d63..9b13451a 100644
--- a/crates/wasm/Cargo.toml
+++ b/crates/wasm/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "wasm-wnfs"
-version = "0.1.5"
+version = "0.1.6"
description = "WebNative Filesystem API (WebAssembly)"
keywords = ["wnfs", "webnative", "ipfs", "decentralisation"]
categories = [
diff --git a/crates/wasm/README.md b/crates/wasm/README.md
index 8f656fd2..a4dd26d0 100644
--- a/crates/wasm/README.md
+++ b/crates/wasm/README.md
@@ -15,16 +15,16 @@ The core of this project is a WebAssembly binary compiled from the [Rust source
Creating a new public directory.
-```ts
+```js
import { PublicDirectory } from "wnfs";
const time = new Date();
const dir = new PublicDirectory(time);
```
-The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, an object that implements the BlockStore interface like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/wasm/examples/graph/src/blockstore.ts#L9-L29) can be used.
+The in-memory files and directories you create with `wnfs` will need to be sealed and stored somewhere. For that, an type that implements the BlockStore interface like [this one](https://github.com/WebNativeFileSystem/rs-wnfs/blob/8bb0fbb457051295f1ed4a4707dc230c04612658/crates/wasm/examples/graph/src/blockstore.ts#L9-L29) can be used.
-```ts
+```js
import { MemoryBlockStore } from "./store";
import { PublicDirectory } from "wnfs";
@@ -39,7 +39,7 @@ The WNFS API is immutable, therefore, we need to keep track of the updated root
Each fs operation returns a possibly updated root directory that subsequent changes can be applied on.
-```ts
+```js
// ...
// Create a /pictures/cats directory.
@@ -61,7 +61,7 @@ var { rootDir } = await rootDir.write(
);
// Create and add a file to /pictures/dogs directory.
-var { rootDir } = await root.write(
+var { rootDir } = await rootDir.write(
["pictures", "dogs", "billie.jpeg"],
cid,
time,
@@ -73,6 +73,8 @@ var { rootDir } = await rootDir.rm(["pictures", "cats"], store);
// List all files in /pictures directory.
var { result } = await rootDir.ls(["pictures"], store);
+
+console.log("Files in /pictures directory:", result);
```
## Setting up the Project
From 738f122ad7a10b6c42a722c9ccdc33472637fe8a Mon Sep 17 00:00:00 2001
From: Stephen Akinyemi
Date: Mon, 23 May 2022 21:25:07 +0100
Subject: [PATCH 7/8] Fix publish command
---
crates/wasm/README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/crates/wasm/README.md b/crates/wasm/README.md
index a4dd26d0..9ef8d86d 100644
--- a/crates/wasm/README.md
+++ b/crates/wasm/README.md
@@ -122,5 +122,9 @@ console.log("Files in /pictures directory:", result);
- Publish from the `pkg` directory
```bash
- wasm-pack publish --nodejs
+ cd pkg
+ ```
+
+ ```bash
+ npm publish
```
From f6a43d7257bc44c30c7b279b5bac2225a8567429 Mon Sep 17 00:00:00 2001
From: Stephen Akinyemi
Date: Tue, 24 May 2022 11:54:44 +0100
Subject: [PATCH 8/8] Change script name
---
README.md | 20 ++++++++---------
crates/wasm/README.md | 2 +-
scripts/{wnfs.sh => rs-wnfs.sh} | 40 ++++++++++++++++-----------------
3 files changed, 31 insertions(+), 31 deletions(-)
rename scripts/{wnfs.sh => rs-wnfs.sh} (86%)
diff --git a/README.md b/README.md
index 9ed62e9f..f7ecca84 100644
--- a/README.md
+++ b/README.md
@@ -180,9 +180,9 @@ async fn main() {
-- **The _wnfs_ Command**
+- **The _rs-wnfs_ Command**
- You can optionally set up the `wnfs` script.
+ You can optionally set up the `rs-wnfs` script.
Read more
@@ -190,13 +190,13 @@ async fn main() {
- Install it using the following command:
```bash
- sh script/wnfs.sh setup
+ sh script/rs-wnfs.sh setup
```
- - This lets you run the `wnfs.sh` script as a command.
+ - This lets you run the `rs-wnfs.sh` script as a command.
```bash
- wnfs help
+ rs-wnfs help
```
@@ -217,16 +217,16 @@ async fn main() {
- Build the project
- Check [REQUIREMENTS](#requirements) on how to set up the `wnfs` command.
+ Check [REQUIREMENTS](#requirements) on how to set up the `rs-wnfs` command.
```bash
- wnfs build --all
+ rs-wnfs build --all
```
- You can also build for specific crates
```bash
- wnfs build --wasm
+ rs-wnfs build --wasm
```
## Testing the Project
@@ -234,11 +234,11 @@ async fn main() {
- Run all tests
```bash
- wnfs test --all
+ rs-wnfs test --all
```
- Show code coverage
```bash
- wnfs coverage
+ rs-wnfs coverage
```
diff --git a/crates/wasm/README.md b/crates/wasm/README.md
index 9ef8d86d..22239bbd 100644
--- a/crates/wasm/README.md
+++ b/crates/wasm/README.md
@@ -116,7 +116,7 @@ console.log("Files in /pictures directory:", result);
- Build the project
```bash
- wnfs build --wasm
+ rs-wnfs build --wasm
```
- Publish from the `pkg` directory
diff --git a/scripts/wnfs.sh b/scripts/rs-wnfs.sh
similarity index 86%
rename from scripts/wnfs.sh
rename to scripts/rs-wnfs.sh
index 172176e4..403c9744 100755
--- a/scripts/wnfs.sh
+++ b/scripts/rs-wnfs.sh
@@ -59,21 +59,21 @@ main() {
# Prints the help info.
#
# USAGE:
-# wnfs build
+# rs-wnfs build
#
help() {
echo ""
echo "Rust WNFS Utility Script"
echo ""
echo "USAGE:"
- echo " wnfs [COMMAND] [...args]"
+ echo " rs-wnfs [COMMAND] [...args]"
echo ""
echo "COMMAND:"
- echo " * build [--wnfs|--wasm|--all] - build projects"
- echo " * test [--wnfs|--wasm|--all] - run tests"
- echo " * publish [--wnfs|--wasm|--all] - publish packages"
- echo " * coverage [--wnfs|--wasm|--all] - show code coverage"
- echo " * setup - install wnfs script"
+ echo " * build [--fs|--wasm|--all] - build projects"
+ echo " * test [--fs|--wasm|--all] - run tests"
+ echo " * publish [--fs|--wasm|--all] - publish packages"
+ echo " * coverage [--fs|--wasm|--all] - show code coverage"
+ echo " * setup - install rs-wnfs script"
echo " * help - print this help message"
echo ""
echo ""
@@ -87,20 +87,20 @@ help() {
# Builds the project.
#
# USAGE:
-# wnfs build [--wnfs|--wasm|--all]
+# rs-wnfs build [--fs|--wasm|--all]
#
build() {
- if check_flag --wnfs; then
- build_wnfs
+ if check_flag --fs; then
+ build_fs
elif check_flag --wasm; then
build_wasm
else
- build_wnfs
+ build_fs
build_wasm
fi
}
-build_wnfs() {
+build_fs() {
display_header "💿 | BUILDING WNFS PROJECT | 💿"
cargo build --release
}
@@ -118,20 +118,20 @@ build_wasm() {
# Runs tests.
#
# USAGE:
-# wnfs test [--wnfs|--wasm|--all]
+# rs-wnfs test [--fs|--wasm|--all]
#
test() {
- if check_flag --wnfs; then
- test_wnfs
+ if check_flag --fs; then
+ test_fs
elif check_flag --wasm; then
test_wasm
else
- test_wnfs
+ test_fs
test_wasm
fi
}
-test_wnfs() {
+test_fs() {
display_header "🧪 | RUNNING WNFS TESTS | 🧪"
cargo test -p wnfs --release -- --nocapture
}
@@ -147,7 +147,7 @@ test_wasm() {
# Shows the code coverage of the project
#
# USAGE:
-# wnfs coverage [--wnfs|--wasm|--all]
+# rs-wnfs coverage [--fs|--wasm|--all]
#
coverage() {
errorln "coverage command not implemented yet"
@@ -158,7 +158,7 @@ coverage() {
# Publishes the project.
#
# USAGE:
-# wnfs publish [--wnfs|--wasm|--all]
+# rs-wnfs publish [--fs|--wasm|--all]
#
publish() {
errorln "publish command not implemented yet"
@@ -233,7 +233,7 @@ setup() {
chmod u+x $script_path
displayln "Drop a link to it in /usr/local/bin"
- if ln -s $script_path /usr/local/bin/wnfs; then
+ if ln -s $script_path /usr/local/bin/rs-wnfs; then
successln "Successfully installed"
else
local result=$?