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 +
+ + Fission Logo + + +

WebNative FileSystem (WNFS)

+ +

+ + Concurrency Docs + + + Code Coverage + + + Build Status + + + License + + + Concurrency Docs + + + Discord + +

+
+ +
: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=$?