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