diff --git a/package.json b/package.json index f99f0b9..85a78cd 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "scripts": { "dev": "next dev", "build": "next build", + "build:wasm": "wasm-pack build ./src/wasm --target web", "start": "next start", "lint": "next lint", "format": "bunx prettier --write .", diff --git a/src/wasm/src/filesystem/directory.rs b/src/wasm/src/filesystem/directory.rs index 3ae7055..7030dd6 100644 --- a/src/wasm/src/filesystem/directory.rs +++ b/src/wasm/src/filesystem/directory.rs @@ -3,42 +3,17 @@ use std::rc::Rc; // use std::sync::{Arc, Mutex}; use crate::filesystem::node::Node; +#[derive(Clone, PartialEq)] pub struct Directory { - name: String, - node: Rc>, + pub name: String, + children: Vec>>, } impl Directory { - // pub fn new(name: String, node: Arc>) -> Directory { - // Directory { - // name, - // node, - // } - // } - - // pub fn get_name(&self) -> String { - // self.name.clone() - // } - - // pub fn parent_name(&self) -> Option { - // match &self.node.borrow().get_parent() { - // Some(parent) => Some(parent.borrow().get_name()), - // None => None, - // } - // } - - // pub fn get_parent(&self) -> Option>> { - // match &self.node.borrow().get_parent() { - // Some(parent) => Some(Rc::clone(parent)), - // None => None, - // } - // } - - // pub fn get_children(&self) -> Vec>> { - // self.node.borrow().get_children().clone() - // } - - // pub fn add_child(&mut self, child: Arc>) { - // self.node.borrow_mut().get_children().push(Rc::clone(&child)); - // } + pub fn new(name: String, children: Vec>>) -> Directory { + Directory { + name, + children + } + } } \ No newline at end of file diff --git a/src/wasm/src/filesystem/file.rs b/src/wasm/src/filesystem/file.rs index 0cae2a0..a917b9f 100644 --- a/src/wasm/src/filesystem/file.rs +++ b/src/wasm/src/filesystem/file.rs @@ -1,32 +1,22 @@ -// use std::{cell::RefCell, rc::Rc}; -use std::sync::{Arc, Mutex}; - -use crate::filesystem::node::Node; - +#[derive(Clone, PartialEq)] pub struct File { - name: String, + pub name: String, data: String, - node: Arc> } impl File { - pub fn new(name: String, data: String, node: Arc>) -> File { + pub fn new(name: String, data: String) -> File { File { name, data, - node, } } - pub fn get_name(&self) -> &String { - &self.name - } - - pub fn read(&self) -> String { - self.data.clone() - } - pub fn write(&mut self, data: String) { - self.data = data; - } + // pub fn read(&self) -> String { + // self.data.clone() + // } + // pub fn write(&mut self, data: String) { + // self.data = data; + // } } diff --git a/src/wasm/src/filesystem/node.rs b/src/wasm/src/filesystem/node.rs index e82877f..96bff5c 100644 --- a/src/wasm/src/filesystem/node.rs +++ b/src/wasm/src/filesystem/node.rs @@ -1,14 +1,17 @@ use std::rc::Rc; use std::cell::RefCell; + +use super::directory::Directory; +use super::file::File; // use std::sync::{Arc, Mutex}; -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum NodeType { - Directory, - File, + Directory(Directory), + File(File), } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub struct Node { name: String, node_type: NodeType, @@ -22,10 +25,26 @@ impl Node { name, node_type, parent, - children: Vec::>>::new(), + children: Vec::new(), + } + } + + pub fn as_directory(&self) -> Option<&Directory> { + if let NodeType::Directory(ref dir) = self.node_type { + Some(dir) + } else { + None } } + // pub fn as_file(&self) -> Option<&File> { + // if let NodeType::File(ref file) = self.node_type { + // Some(file) + // } else { + // None + // } + // } + pub fn get_node_type(&self) -> NodeType { self.node_type.clone() } @@ -41,11 +60,11 @@ impl Node { } } - pub fn add_child(&mut self, child: Rc>) { - self.children.push(child); - } - pub fn get_children(&self) -> Vec>> { self.children.clone() } + + pub fn add_child(&mut self, child: Rc>) { + self.children.push(Rc::clone(&child)); + } } \ No newline at end of file diff --git a/src/wasm/src/lib.rs b/src/wasm/src/lib.rs index b82cd4c..e6eb3c3 100644 --- a/src/wasm/src/lib.rs +++ b/src/wasm/src/lib.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use std::cell::RefCell; use wasm_bindgen::prelude::*; -use web_sys::console; +// use web_sys::console; use serde_json; mod shell; @@ -10,7 +10,8 @@ mod filesystem; use shell::lexer::Lexer; use shell::exec::Exec; -use filesystem::node::Node; +use filesystem::node::{Node, NodeType}; +use filesystem::directory::Directory; // use filesystem::pipe::Pipe; @@ -18,7 +19,7 @@ use filesystem::node::Node; pub struct Shell { lexer: Lexer, exec: Exec, - root: Rc>, + // root: Rc>, current: Rc>, // pipe: Pipe, user: String, @@ -30,15 +31,17 @@ impl Shell { #[wasm_bindgen(constructor)] pub fn new(user: String, hostname: String) -> Shell { let root = Rc::new(RefCell::new( - Node::new("/".to_string(), filesystem::node::NodeType::Directory, None) + Node::new("".to_string(), NodeType::Directory(Directory::new( + "".to_string(), + Vec::new() + )), None) )); Shell { lexer: Lexer::new(), exec: Exec::new(Rc::clone(&root), Rc::clone(&root)), - // current, current: Rc::clone(&root), - root, + // root, // pipe: Pipe::new(""), user, hostname, @@ -50,11 +53,15 @@ impl Shell { let statement = self.lexer.lex(input); let result = self.exec.execute(statement); // console::log_1(&result.clone().into()); + self.current = Rc::clone(&self.exec.get_current()); + self.user = self.exec.get_user(); + self.hostname = self.exec.get_hostname(); + let output = serde_json::json!({ "result": result, - "user": self.user, - "host": self.hostname, - "path": self.current.borrow().get_name(), + "user": self.exec.get_user(), + "host": self.exec.get_hostname(), + "path": self.exec.get_current().borrow().get_name(), }); let output_str = serde_json::to_string(&output).unwrap(); diff --git a/src/wasm/src/shell/exec.rs b/src/wasm/src/shell/exec.rs index 294c290..a01e9be 100644 --- a/src/wasm/src/shell/exec.rs +++ b/src/wasm/src/shell/exec.rs @@ -4,7 +4,9 @@ use std::cell::RefCell; use wasm_bindgen::prelude::*; use web_sys::console; -use crate::filesystem::node::Node; +use crate::filesystem::node::{Node, NodeType}; +use crate::filesystem::directory::Directory; +use crate::filesystem::file::File; // use crate::filesystem::directory::Directory; use crate::shell::lexer::Statement; @@ -21,117 +23,175 @@ impl Exec { } } - fn hello() -> String { - "Hello from WASM!".to_string() + pub fn get_current(&self) -> Rc> { + Rc::clone(&self.current) } - fn ls(&mut self, options: Vec) -> String { - let mut children = Vec::::new(); - // for child in self.current.borrow().get_children() { - // children.push(child.borrow().get_name()); - // } - serde_json::to_string(&children).unwrap() + pub fn get_user(&self) -> String { + "guest".to_owned() } - fn pwd() -> String { - // build path from root to current - // let mut path = Vec::::new(); - // let mut temp_dir = Rc::clone(&self.current); - // loop { - // path.push(temp_dir.borrow().get_name()); - // let temp_parent = temp_dir.borrow().get_parent(); - // match temp_parent { - // Some(parent) => temp_dir = Rc::clone(&parent), - // None => break, - // } - // } - // path.reverse(); - // let mut path_string = String::new(); - // for path_part in path { - // path_string.push_str(path_part.as_str()); - // path_string.push_str("/"); - // } - // path_string.pop(); - // serde_json::to_string(&path_string).unwrap() - "pwd command".to_string() + pub fn get_hostname(&self) -> String { + "soohoonchoi.com".to_owned() } - fn cd(new_path: String) -> String { - console::log_1(&JsValue::from_str(&new_path)); - // find the directory - // if it exists, change the current directory - // else, return an error - // console::log_1(&JsValue::from_str(new_path)); - // let mut temp_dir = if new_path.starts_with("/") { - // Rc::clone(&self.root) - // } else { - // Rc::clone(&self.current) - // }; - // for path in new_path.split("/").filter(|&x| !x.is_empty()) { - // // console::log_1(&JsValue::from_str(path)); - // if path == "." { - // // console::log_1(&JsValue::from_str("Continuing")); - // continue; - // } else if path == ".." { - // // console::log_1(&JsValue::from_str("Going up")); - // let temp_parent = temp_dir.borrow().get_parent(); - // match temp_parent { - // Some(parent) => temp_dir = Rc::clone(&parent), - // None => temp_dir = Rc::clone(&temp_dir), - // } - // } else { - // let mut found = false; - // // console::log_1(&JsValue::from_str("Searching")); - // let children = temp_dir.borrow().get_children(); - // for child in children { - // // console::log_1(&JsValue::from_str(child.borrow().get_name().as_str())); - // let child_node = child.borrow(); - // // console::log_2(&JsValue::from_str(&child_node.get_name()), &JsValue::from_str(path)); - // if child_node.get_name() == path { - // match &*child_node { - // Node::Directory(child_dir) => { - // temp_dir = Rc::clone(child_dir); - // found = true; - // break; - // }, - // Node::File(_) => { - // return Err(JsValue::from_str("Not a directory")) - // } - // } - // } - // } - // if !found { - // return Err(JsValue::from_str("Directory not found")); - // } - // } - // } - // self.current = temp_dir; - // Ok(JsValue::from_str("Success")) - "cd command".to_string() +fn ls(&self) -> String { + let mut children: Vec = Vec::new(); + let current_node = self.current.borrow(); + for child in current_node.get_children() { + children.push(child.borrow().get_name().to_owned()); + } + serde_json::to_string(&children).unwrap() +} + + fn pwd(&self) -> String { + let mut path_string = String::new(); + let mut current_node = Rc::clone(&self.current); + loop { + path_string = format!("{}/{}", current_node.borrow().get_name(), path_string); + let parent = current_node.borrow().get_parent(); + console::log_1(&JsValue::from_str(¤t_node.borrow().get_name())); + match parent { + Some(p) => { + console::log_1(&JsValue::from_str(&p.borrow().get_name())); + current_node = Rc::clone(&p) + }, + None => break, + } + } + serde_json::to_string(&path_string).unwrap() +} + fn cd(&mut self, args: Vec) -> String { + let new_path = { + if args.first().is_some() { + args.first().unwrap().clone() + } else { + "/".to_owned() + } + }; + let mut current_node = Rc::clone(&self.current); + console::log_1(&JsValue::from_str(new_path.as_str())); + for path in new_path.split("/").filter(|&x| !x.is_empty()) { + // console::log_1(&JsValue::from_str(path)); + if path == "." { + continue; + } else if path == ".." { + let parent = self.current.borrow().get_parent(); + match parent { + Some(parent) => current_node = parent, + None => () + } + } else { + let mut found = false; + // console::log_1(&JsValue::from_str("Searching")); + let children = current_node.borrow().get_children(); + for child in children { + // console::log_1(&JsValue::from_str(child.borrow().get_name().as_str())); + let child_node = Rc::clone(&child); + // console::log_2(&JsValue::from_str(&child_node.get_name()), &JsValue::from_str(path)); + let child_node_type = child_node.borrow().get_node_type(); + if child_node.borrow().get_name() == path { + match child_node_type { + NodeType::Directory(_) => { + current_node = child_node; + found = true; + break; + }, + NodeType::File(_) => { + return "Not a directory".to_string(); + } + } + } + } + if !found { + return "Directory not found".to_string(); + } + } + } + self.current = current_node; + return " ".to_string() } - fn mkdir(&mut self, new_dir: String) -> String { + fn mkdir(&mut self, args: Vec) -> String { // let new_node = Rc::new(RefCell::new(Node::new(new_dir, NodeType::Directory, Some(Rc::clone(&self.current)))); // self.current.borrow_mut().add_child(Rc::clone(&new_node)); // Ok(JsValue::from_str("Success")) - "mkdir command".to_string() + if args.len() == 0 { + return "usage: mkdir [-pv] [-m mode] directory_name ...".to_string() + } + let new_path = args.first().unwrap().clone(); + let mut current_node = Rc::clone(&self.current); + if current_node.borrow().as_directory().is_none() { + return "Not a directory".to_string(); + } + + for path in new_path.split("/").filter(|&x| !x.is_empty()) { + let mut found = false; + let children = current_node.borrow().get_children(); + for child in children { + let child_node_type = child.borrow().get_node_type(); + let child_node_name = child.borrow().get_name(); + if child_node_name == path { + match child_node_type { + NodeType::Directory(_) => { + current_node = Rc::clone(&child); // Use the cloned child + found = true; + break; + }, + NodeType::File(_) => { + return "Not a directory".to_string(); + } + } + } + } + if !found { + let new_node = Rc::new(RefCell::new(Node::new(path.to_string(), NodeType::Directory(Directory::new( + path.to_string(), + Vec::new() + )), Some(Rc::clone(¤t_node))))); + current_node.borrow_mut().add_child(Rc::clone(&new_node)); + current_node = Rc::clone(&new_node); + } + } + " ".to_string() + } + + fn echo(&self, args: Vec) -> String { + let mut output = String::new(); + for arg in args { + output.push_str(arg.as_str()); + output.push_str(" "); + } + output + } + + fn touch(&self, args: Vec) -> String { + if args.first().is_none() { + return "usage: touch file_name".to_string() + } + let new_file = args.first().unwrap().clone(); + let cloned_new_file = new_file.clone(); + let new_node = Rc::new(RefCell::new(Node::new(cloned_new_file, NodeType::File( + File::new(new_file, "".to_string()) + ), Some(Rc::clone(&self.current))))); + self.current.borrow_mut().add_child(Rc::clone(&new_node)); + " ".to_string() } pub fn execute(&mut self, input: Statement) -> String { match &input.command[..] { "" => " ".to_owned(), "github" => "github".to_owned(), - "hello" => Self::hello(), - "help" => "

\nhelp command\n

".to_owned(), + "help" => serde_json::to_string("

\nhelp command\n

").unwrap(), "license" => "license command".to_owned(), - "ls" => Self::ls(self, input.options), - "pwd" => Self::pwd(), - "cd" => Self::cd("".into()), + "ls" => Self::ls(self), + "pwd" => Self::pwd(self), + "cd" => Self::cd(self, input.arguments), "clear" => "".to_owned(), - "echo" => "echo command".to_owned(), + "echo" => Self::echo(self, input.arguments), "cat" => "cat command".to_owned(), - "touch" => "touch command".to_owned(), - "mkdir" => "mkdir command".to_owned(), + "touch" => Self::touch(self, input.arguments), + "mkdir" => Self::mkdir(self, input.arguments), "rm" => "rm command".to_owned(), "rmdir" => "rmdir command".to_owned(), "mv" => "mv command".to_owned(),