First few commands
This commit is contained in:
commit
a5e3039f80
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
123
Cargo.lock
generated
Normal file
123
Cargo.lock
generated
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.71"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-terminal"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasm-terminal"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Daniel Kluge <daniel-git@c0ntroller.de>"]
|
||||||
|
description = "Console application with commands in WASM for my website"
|
||||||
|
license = "GPT-3"
|
||||||
|
repository = "https://git.c0ntroller.de/c0ntroller/wasm-terminal"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wasm-bindgen = "0.2.89"
|
239
src/console/commands.rs
Normal file
239
src/console/commands.rs
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::{Command, types::Content};
|
||||||
|
|
||||||
|
pub fn commands() -> Vec<Rc<Command>> {
|
||||||
|
let mut available_commands: Vec<Rc<Command>> = Vec::new();
|
||||||
|
|
||||||
|
available_commands.push(
|
||||||
|
Rc::new(Command::new(
|
||||||
|
"debug".to_string(),
|
||||||
|
"Prints the args and flags".to_string(),
|
||||||
|
vec![
|
||||||
|
Rc::new(super::Flag::new("long".to_string(), "l".to_string(), "A long flag".to_string())),
|
||||||
|
Rc::new(super::Flag::new("short".to_string(), "s".to_string(), "A short flag".to_string())),
|
||||||
|
],
|
||||||
|
|_console, args, flags| {
|
||||||
|
let mut output = String::from("Flags");
|
||||||
|
for flag in flags {
|
||||||
|
output.push_str(&format!("\n\tLong: {}\n\tShort: {}\n\tDescription: {}", flag.long, flag.short, flag.description));
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push_str("\nArgs:");
|
||||||
|
for arg in args {
|
||||||
|
output.push_str(&format!("\n\t{}", arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(output))
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
|
available_commands.push(
|
||||||
|
Rc::new(Command::new(
|
||||||
|
"echo".to_string(),
|
||||||
|
"Prints the args".to_string(),
|
||||||
|
Vec::new(),
|
||||||
|
|_console, args, _flags| {
|
||||||
|
let mut output = String::new();
|
||||||
|
for arg in args {
|
||||||
|
output.push_str(&format!("{} ", arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(output))
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
|
available_commands.push(
|
||||||
|
Rc::new(Command::new(
|
||||||
|
"pwd".to_string(),
|
||||||
|
"Prints the current working directory".to_string(),
|
||||||
|
Vec::new(),
|
||||||
|
|console, args, _flags| {
|
||||||
|
if args.len() > 0 {
|
||||||
|
return Err("pwd does not take any arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(console.get_current_dir().borrow().get_path()))
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
|
available_commands.push(
|
||||||
|
Rc::new(Command::new(
|
||||||
|
"cd".to_string(),
|
||||||
|
"Changes the current working directory".to_string(),
|
||||||
|
Vec::new(),
|
||||||
|
|console, args, _flags| {
|
||||||
|
if args.len() != 1 {
|
||||||
|
return Err("Only the path must be specified!");
|
||||||
|
}
|
||||||
|
|
||||||
|
match console.cd(args[0].clone()) {
|
||||||
|
Ok(_) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
|
available_commands.push(
|
||||||
|
Rc::new(Command::new(
|
||||||
|
"ls".to_string(),
|
||||||
|
"Lists the contents of the current directory".to_string(),
|
||||||
|
vec![
|
||||||
|
Rc::new(super::Flag::new("list".to_string(), "l".to_string(), "Print contents in a list".to_string())),
|
||||||
|
],
|
||||||
|
|console, args, flags| {
|
||||||
|
if args.len() > 0 {
|
||||||
|
return Err("ls does not take any arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// Virtual cd to read the contents there
|
||||||
|
|
||||||
|
let seperator = match flags.iter().find(|f| f.long == "list" ) {
|
||||||
|
Some(_) => "\n",
|
||||||
|
None => "\t",
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut output = String::new();
|
||||||
|
let current_dir = console.get_current_dir();
|
||||||
|
for dir in current_dir.borrow().get_subdirs() {
|
||||||
|
output.push_str(&format!("{}{}", dir.borrow().get_name(), seperator));
|
||||||
|
}
|
||||||
|
for file in current_dir.borrow().get_files() {
|
||||||
|
output.push_str(&format!("{}{}", file.borrow().get_name(), seperator));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(output))
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
|
available_commands.push(
|
||||||
|
Rc::new(Command::new(
|
||||||
|
"mkdir".to_string(),
|
||||||
|
"Creates a new directory".to_string(),
|
||||||
|
Vec::new(),
|
||||||
|
|console, args, _flags| {
|
||||||
|
if args.len() != 1 {
|
||||||
|
return Err("Only the name must be specified!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// virtual_cd
|
||||||
|
// Get the directory name from the path
|
||||||
|
|
||||||
|
let current_dir = console.get_current_dir();
|
||||||
|
let result = match current_dir.borrow_mut().mkdir(args[0].clone()) {
|
||||||
|
Ok(_) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
result
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
|
available_commands.push(
|
||||||
|
Rc::new(Command::new(
|
||||||
|
"touch".to_string(),
|
||||||
|
"Creates a new file".to_string(),
|
||||||
|
Vec::new(),
|
||||||
|
|console, args, _flags| {
|
||||||
|
if args.len() != 1 {
|
||||||
|
return Err("Only the name must be specified!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// virtual_cd
|
||||||
|
// Get the directory name from the path
|
||||||
|
|
||||||
|
let current_dir = console.get_current_dir();
|
||||||
|
let result = match current_dir.borrow_mut().touch(args[0].clone()) {
|
||||||
|
Ok(_) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
result
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
|
available_commands.push(
|
||||||
|
Rc::new(Command::new(
|
||||||
|
"cat".to_string(),
|
||||||
|
"Prints the contents of a file".to_string(),
|
||||||
|
Vec::new(),
|
||||||
|
|console, args, _flags| {
|
||||||
|
if args.len() != 1 {
|
||||||
|
return Err("Only the name must be specified!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// virtual_cd
|
||||||
|
// Get the directory name from the path
|
||||||
|
|
||||||
|
let current_dir = console.get_current_dir();
|
||||||
|
let file = match current_dir.borrow().get_file(args[0].clone()) {
|
||||||
|
Some(file) => file,
|
||||||
|
None => return Err("File not found"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let content = file.borrow().read();
|
||||||
|
|
||||||
|
Ok(Some(content))
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
|
available_commands.push(
|
||||||
|
Rc::new(Command::new(
|
||||||
|
"rm".to_string(),
|
||||||
|
"Removes a file or directory".to_string(),
|
||||||
|
Vec::new(),
|
||||||
|
|console, args, _flags| {
|
||||||
|
if args.len() != 1 {
|
||||||
|
return Err("Only the name must be specified!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// virtual_cd
|
||||||
|
// Get the directory name from the path
|
||||||
|
|
||||||
|
let current_dir = console.get_current_dir();
|
||||||
|
let result = match current_dir.borrow_mut().remove_file(args[0].clone()) {
|
||||||
|
Ok(_) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
result
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
|
// write into file
|
||||||
|
available_commands.push(
|
||||||
|
Rc::new(Command::new(
|
||||||
|
"write".to_string(),
|
||||||
|
"Writes into a file".to_string(),
|
||||||
|
vec![
|
||||||
|
Rc::new(super::Flag::new("append".to_string(), "a".to_string(), "Append to the file".to_string())),
|
||||||
|
],
|
||||||
|
|console, args, flags| {
|
||||||
|
// TODO
|
||||||
|
// virtual_cd
|
||||||
|
// Get the directory name from the path
|
||||||
|
|
||||||
|
let current_dir = console.get_current_dir();
|
||||||
|
let file = match current_dir.borrow().get_file(args[0].clone()) {
|
||||||
|
Some(file) => file,
|
||||||
|
None => return Err("File not found"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if flags.iter().find(|f| f.long == "append" ).is_some() {
|
||||||
|
file.borrow_mut().append(args[1].clone());
|
||||||
|
return Ok(None);
|
||||||
|
} else {
|
||||||
|
file.borrow_mut().write(args[1].clone());
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
|
available_commands
|
||||||
|
}
|
189
src/console/mod.rs
Normal file
189
src/console/mod.rs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
mod types;
|
||||||
|
mod commands;
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use types::*;
|
||||||
|
use commands::commands;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
pub struct Flag {
|
||||||
|
pub long: String,
|
||||||
|
pub short: String,
|
||||||
|
pub description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl Flag {
|
||||||
|
pub fn new(long: String, short: String, description: String) -> Flag {
|
||||||
|
Flag {
|
||||||
|
long,
|
||||||
|
short,
|
||||||
|
description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Command {
|
||||||
|
pub name: String,
|
||||||
|
pub description: String,
|
||||||
|
pub flags: Vec<Rc<Flag>>,
|
||||||
|
function: fn(&mut Console, Vec<String>, Vec<Rc<Flag>>) -> Result<Option<String>, &'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
pub fn new(name: String, description: String, flags: Vec<Rc<Flag>>, function: fn(&mut Console, Vec<String>, Vec<Rc<Flag>>) -> Result<Option<String>, &'static str>) -> Command {
|
||||||
|
Command {
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
flags,
|
||||||
|
function,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_flags(&self, args: Vec<String>) -> (Vec<Rc<Flag>>, Vec<String>) {
|
||||||
|
let mut flags: Vec<Rc<Flag>> = Vec::new();
|
||||||
|
let mut other = Vec::new();
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
if arg.starts_with("--") {
|
||||||
|
let f = match self.flags.iter().find(|f| arg[2..] == f.long) {
|
||||||
|
Some(f) => Rc::clone(f),
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
flags.push(f);
|
||||||
|
} else if arg.starts_with("-") {
|
||||||
|
let f = match self.flags.iter().find(|f| arg[1..] == f.short) {
|
||||||
|
Some(f) => Rc::clone(f),
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
flags.push(f);
|
||||||
|
} else {
|
||||||
|
other.push(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(flags, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute(&self, console: &mut Console, args: Vec<String>) -> Result<Option<String>, &'static str> {
|
||||||
|
let (flags, args) = self.parse_flags(args.clone());
|
||||||
|
(self.function)(console, args, flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub struct Console {
|
||||||
|
root: Rc<RefCell<Directory>>,
|
||||||
|
pwd: RefCell<Vec<Rc<RefCell<Directory>>>>,
|
||||||
|
last_commands: Vec<String>,
|
||||||
|
available_commands: Vec<Rc<Command>>,
|
||||||
|
output: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Console {
|
||||||
|
fn get_current_dir(&self) -> Rc<RefCell<Directory>> {
|
||||||
|
match self.pwd.borrow().last() {
|
||||||
|
Some(dir) => Rc::clone(dir),
|
||||||
|
None => Rc::clone(&self.root),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cd(&mut self, path: String) -> Result<(), &'static str> {
|
||||||
|
let segments = PathSegment::parse_path_segment(path);
|
||||||
|
let mut current = self.get_current_dir();
|
||||||
|
|
||||||
|
for segment in segments {
|
||||||
|
match segment {
|
||||||
|
PathSegment::Root => {
|
||||||
|
self.pwd.borrow_mut().clear();
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
PathSegment::This => continue,
|
||||||
|
PathSegment::Parent => {
|
||||||
|
if self.pwd.borrow().len() == 0 {
|
||||||
|
return Err("Cannot go above root directory");
|
||||||
|
}
|
||||||
|
self.pwd.borrow_mut().pop();
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
PathSegment::SubDirectory(name) => {
|
||||||
|
let dir = match current.borrow().get_dir(name) {
|
||||||
|
Some(dir) => dir,
|
||||||
|
None => return Err("Directory does not exist"),
|
||||||
|
};
|
||||||
|
self.pwd.borrow_mut().push(dir);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
current = self.get_current_dir();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get a directory from a path
|
||||||
|
// the PWD should be unchanged after this function
|
||||||
|
/*fn virtual_cd(&self, path: String) -> Result<Box<&mut Directory>, &'static str> {
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
|
fn execute_inner(&mut self, command: String) -> Option<String> {
|
||||||
|
let mut args: Vec<String> = command.split(" ").map(|s| s.to_string()).collect();
|
||||||
|
let command = match args.first() {
|
||||||
|
Some(command) => command,
|
||||||
|
None => return Some("No command entered".to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let command = match self.available_commands.iter().find(|c| c.name == *command) {
|
||||||
|
Some(command) => command.clone(),
|
||||||
|
None => return Some(format!("Command {} not found", command)),
|
||||||
|
};
|
||||||
|
|
||||||
|
args.remove(0);
|
||||||
|
|
||||||
|
let result = command.execute(self, args);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(result) => {
|
||||||
|
self.last_commands.push(command.name.clone());
|
||||||
|
result
|
||||||
|
},
|
||||||
|
Err(e) => Some(e.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
impl Console {
|
||||||
|
pub fn new() -> Console {
|
||||||
|
let root = Rc::new(RefCell::new(Directory::new("".to_string(), "/".to_string())));
|
||||||
|
|
||||||
|
let console = Console {
|
||||||
|
root,
|
||||||
|
pwd: RefCell::new(Vec::new()),
|
||||||
|
output: Vec::new(),
|
||||||
|
last_commands: Vec::new(),
|
||||||
|
available_commands: commands(),
|
||||||
|
};
|
||||||
|
|
||||||
|
console
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_output(&self) -> Vec<String> {
|
||||||
|
self.output.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_last_commands(&self) -> Vec<String> {
|
||||||
|
self.last_commands.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute(&mut self, command: String) -> Option<String> {
|
||||||
|
self.output.push(format!("$ {}", command));
|
||||||
|
self.last_commands.push(command.clone());
|
||||||
|
let result = self.execute_inner(command);
|
||||||
|
if result.is_some() {
|
||||||
|
self.output.push(result.clone().unwrap());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
279
src/console/types.rs
Normal file
279
src/console/types.rs
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
|
||||||
|
pub trait Content {
|
||||||
|
fn get_name(&self) -> String;
|
||||||
|
fn get_path(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct File {
|
||||||
|
path: String,
|
||||||
|
name: String,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Content for File {
|
||||||
|
fn get_name(&self) -> String {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_path(&self) -> String {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl File {
|
||||||
|
pub fn new(name: String, content: String, path: String) -> File {
|
||||||
|
File {
|
||||||
|
name,
|
||||||
|
content,
|
||||||
|
path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty_new(name: String, path: String) -> File {
|
||||||
|
File {
|
||||||
|
name,
|
||||||
|
content: String::new(),
|
||||||
|
path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self) -> String {
|
||||||
|
self.content.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, content: String) {
|
||||||
|
self.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(&mut self, content: String) {
|
||||||
|
self.content.push_str(&content);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rename(&mut self, name: String) {
|
||||||
|
self.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Directory {
|
||||||
|
name: String,
|
||||||
|
files: RefCell<Vec<Rc<RefCell<File>>>>,
|
||||||
|
subdirs: RefCell<Vec<Rc<RefCell<Directory>>>>,
|
||||||
|
path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Content for Directory {
|
||||||
|
fn get_name(&self) -> String {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_path(&self) -> String {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl Directory {
|
||||||
|
pub fn new(name: String, path: String) -> Directory {
|
||||||
|
Directory {
|
||||||
|
name,
|
||||||
|
files: RefCell::new(Vec::new()),
|
||||||
|
subdirs: RefCell::new(Vec::new()),
|
||||||
|
path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> String {
|
||||||
|
self.path.clone() + "/" + &self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_file(&mut self, file: File) -> Result<(), &'static str>{
|
||||||
|
if self.get_dir(file.name.clone()).is_some() {
|
||||||
|
return Err("File already exists");
|
||||||
|
}
|
||||||
|
if file.name.contains('/') || file.name.contains('\\')|| file.name.contains(' ') {
|
||||||
|
return Err("File name cannot contain spraces, '/' or '\\'");
|
||||||
|
}
|
||||||
|
self.files.borrow_mut().push(Rc::new(RefCell::new(file)));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_dir(&mut self, dir: Directory) -> Result<(), &'static str> {
|
||||||
|
if self.get_dir(dir.name.clone()).is_some() {
|
||||||
|
return Err("Directory already exists");
|
||||||
|
}
|
||||||
|
if dir.name.contains('/') || dir.name.contains('\\')|| dir.name.contains(' ') {
|
||||||
|
return Err("Directory name cannot contain spraces, '/' or '\\'");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.subdirs.borrow_mut().push(Rc::new(RefCell::new(dir)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mkdir(&self, name: String) -> Result<Rc<RefCell<Directory>>, &'static str> {
|
||||||
|
if self.get_dir(name.clone()).is_some() {
|
||||||
|
return Err("Directory already exists");
|
||||||
|
}
|
||||||
|
if name.contains('/') || name.contains('\\')|| name.contains(' ') {
|
||||||
|
return Err("Directory name cannot contain spraces, '/' or '\\'");
|
||||||
|
}
|
||||||
|
|
||||||
|
let dir = Rc::new(RefCell::new(Directory::new(name, self.path())));
|
||||||
|
self.subdirs.borrow_mut().push(dir.clone());
|
||||||
|
Ok(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_file(&self, name: String) -> Option<Rc<RefCell<File>>> {
|
||||||
|
for file in self.files.borrow().iter() {
|
||||||
|
if file.borrow().get_name() == name {
|
||||||
|
return Some(Rc::clone(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_files(&self) -> Vec<Rc<RefCell<File>>> {
|
||||||
|
self.files.borrow().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_subdirs(&self) -> Vec<Rc<RefCell<Directory>>> {
|
||||||
|
self.subdirs.borrow().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_dir(&self, name: String) -> Option<Rc<RefCell<Directory>>> {
|
||||||
|
for dir in self.subdirs.borrow().iter() {
|
||||||
|
if dir.borrow().get_name() == name {
|
||||||
|
return Some(Rc::clone(dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/*pub fn get_file_mut(&self, name: String) -> Option<Rc<File>> {
|
||||||
|
for file in self.files.borrow_mut().iter() {
|
||||||
|
if file.get_name() == name {
|
||||||
|
return Some(Rc::clone(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_dir_mut(&self, name: String) -> Option<Rc<Directory>> {
|
||||||
|
for dir in self.subdirs.borrow().iter() {
|
||||||
|
if dir.get_name() == name {
|
||||||
|
return Some(Rc::clone(dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}*/
|
||||||
|
|
||||||
|
pub fn remove_file(&self, name: String) -> Result<(), &'static str> {
|
||||||
|
let mut index = 0;
|
||||||
|
for file in self.files.borrow().iter() {
|
||||||
|
if file.borrow().get_name() == name {
|
||||||
|
self.files.borrow_mut().remove(index);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
Err("File not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_dir(&self, name: String) -> Result<(), &'static str> {
|
||||||
|
let mut index = 0;
|
||||||
|
for dir in self.subdirs.borrow().iter() {
|
||||||
|
if dir.borrow().get_name() == name {
|
||||||
|
self.subdirs.borrow_mut().remove(index);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
Err("Directory not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rename(&mut self, name: String) {
|
||||||
|
self.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn touch(&self, name: String) -> Result<Rc<RefCell<File>>, &'static str> {
|
||||||
|
if self.get_file(name.clone()).is_some() {
|
||||||
|
return Err("File already exists");
|
||||||
|
}
|
||||||
|
if name.contains('/') || name.contains('\\')|| name.contains(' ') {
|
||||||
|
return Err("File name cannot contain spraces, '/' or '\\'");
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = Rc::new(RefCell::new(File::empty_new(name, self.path.clone())));
|
||||||
|
self.files.borrow_mut().push(file.clone());
|
||||||
|
Ok(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_file(&self, name: String, content: String) -> Result<Rc<RefCell<File>>, &'static str> {
|
||||||
|
if self.get_file(name.clone()).is_some() {
|
||||||
|
return Err("File already exists");
|
||||||
|
}
|
||||||
|
if name.contains('/') || name.contains('\\')|| name.contains(' ') {
|
||||||
|
return Err("File name cannot contain spraces, '/' or '\\'");
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = Rc::new(RefCell::new(File::new(name, content, self.path())));
|
||||||
|
self.files.borrow_mut().push(file.clone());
|
||||||
|
Ok(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Directory {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.files.borrow_mut().clear();
|
||||||
|
self.subdirs.borrow_mut().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PathSegment {
|
||||||
|
This,
|
||||||
|
Parent,
|
||||||
|
SubDirectory(String),
|
||||||
|
Root,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PathSegment {
|
||||||
|
pub fn parse_path_segment(path: String) -> Vec<PathSegment> {
|
||||||
|
let mut segments: Vec<PathSegment> = Vec::new();
|
||||||
|
let mut current: String = String::new();
|
||||||
|
|
||||||
|
let mut first = true;
|
||||||
|
|
||||||
|
for c in path.chars() {
|
||||||
|
if c == '/' {
|
||||||
|
if first {
|
||||||
|
segments.push(PathSegment::Root);
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
if current == "." {
|
||||||
|
segments.push(PathSegment::This);
|
||||||
|
} else if current == ".." {
|
||||||
|
segments.push(PathSegment::Parent);
|
||||||
|
} else {
|
||||||
|
segments.push(PathSegment::SubDirectory(current));
|
||||||
|
}
|
||||||
|
current = String::new();
|
||||||
|
} else {
|
||||||
|
current.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if current == "." {
|
||||||
|
segments.push(PathSegment::This);
|
||||||
|
} else if current == ".." {
|
||||||
|
segments.push(PathSegment::Parent);
|
||||||
|
} else {
|
||||||
|
segments.push(PathSegment::SubDirectory(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
segments
|
||||||
|
}
|
||||||
|
}
|
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod console;
|
Loading…
Reference in New Issue
Block a user