initial commit

This commit is contained in:
Mahdi Dibaiee 2015-05-02 18:01:16 +04:30
commit 7b4f2ce45c
11 changed files with 282 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target

4
Cargo.lock generated Normal file
View File

@ -0,0 +1,4 @@
[root]
name = "rust-shell"
version = "0.1.0"

4
Cargo.toml Normal file
View File

@ -0,0 +1,4 @@
[package]
name = "rust-shell"
version = "0.1.0"
authors = ["Mahdi Dibaiee <mdibaiee@aol.com>"]

47
src/commands/cd.rs Normal file
View File

@ -0,0 +1,47 @@
use std::path::Path;
use std::fs;
pub fn exec(directory: &str, args: Vec<String>) -> String {
let dir = args[0].to_string();
let current_path = Path::new(directory);
// Match . and .. and return early if needed
match dir.as_ref() {
"." => return directory.to_string(),
".." => return current_path.parent().unwrap().to_str().unwrap().to_string(),
_ => ()
};
let new_path = Path::new::<String>(&dir);
let joined_path = current_path.join(new_path);
let path = joined_path.as_path();
let path_string = match path.to_str() {
Some(value) => value.to_string(),
None => directory.to_string()
};
if !check_folder(&path) {
println!("Directory not found {}", path_string);
directory.to_string()
} else {
path_string
}
}
fn check_folder(directory: &Path) -> bool {
match fs::read_dir(directory) {
Ok(..) => true,
Err(..) => false
}
}
pub fn help() {
println!(" Change Directory\n
usage: cd <directory>\n
special:
.. => parent
. => current");
}

34
src/commands/echo.rs Normal file
View File

@ -0,0 +1,34 @@
use super::option_parser::parse;
use std::str::FromStr;
pub fn exec(args: Vec<String>) {
let (options, mut params) = parse(args);
for param in params.iter_mut() {
param.push_str("\n");
}
let mut options_iter = options.iter();
let break_option = options_iter.position(|x| *x == "break".to_string());
let add_break: u32 = match break_option {
Some(..) => u32::from_str(options_iter.next().unwrap()).unwrap(),
None => 0u32
};
for i in 0..add_break {
println!("");
};
let message: String = params.iter().map(|x| x.as_ref()).collect();
println!("{}", message);
}
pub fn help() {
println!(" Prints out stuff\n
usage: echo <line1> <line2> ... <lineN>\n\n
Options:\n
--break=n Adds |n| line breaks before message\n");
}

29
src/commands/help.rs Normal file
View File

@ -0,0 +1,29 @@
use super::{cd, ls, echo};
pub fn exec(args: Vec<String>) {
if args.len() > 0 {
match args[0].as_ref() {
"cd" => cd::help(),
"ls" => ls::help(),
"echo" => echo::help(),
_ => println!("Command not found")
}
} else {
println!("
I wrote this program to learn Rust.\n\n
use help <command> for info about an specific command.\n\n
The shell supports only two kinds of arguments:\n
Options:\n
Single-dash options with no value e.g |ls -a|\n
Double-dash options in key=value format, spaces not allowed |echo --break=2|\n
Parameters:\n
Anything that's not an option, is a parameter\n\n
Complex things like pipes are not implemented.\n
Commands:\n
cd, ls, pwd\n");
}
}

67
src/commands/ls.rs Normal file
View File

@ -0,0 +1,67 @@
use std::path::Path;
use std::fs;
use std::fs::File;
use super::option_parser::parse;
pub fn exec(directory: &str, args: Vec<String>) {
let (options, params) = parse(args);
let dir: &str = if params.len() > 0 {
&params[0]
} else {
directory
};
let dir_path = Path::new(dir);
let dir_entry = fs::read_dir(dir_path).unwrap().map(|x| x.unwrap());
let details = options.contains(&"l".to_string());
if !&options.contains(&"a".to_string()) {
let filtered = dir_entry.filter_map(|entry| {
let path = entry.path();
let is_hidden = path.file_name().unwrap().to_str().unwrap().starts_with(".");
if is_hidden {
None
} else {
Some(entry)
}
});
print_files(filtered, details);
} else {
print_files(dir_entry, details);
};
}
fn print_files<T: Iterator<Item=fs::DirEntry>>(entries: T, detailed: bool) {
if detailed {
println!("Name\tSize");
};
for entry in entries {
let path = entry.path();
let file_name = path.file_name().unwrap().to_str().unwrap();
if !detailed {
println!("{}", file_name);
continue;
};
let file = File::open(&path).unwrap();
let meta = file.metadata().unwrap();
let size = meta.len();
println!("{}\t{}", file_name, size);
};
}
pub fn help() {
println!(" List directory contents\n
usage: ls <directory>\n
If no directory is specified, the current directory's files are listed\n\n
Options:\n
-a Show hidden files
-l Show details\n");
}

5
src/commands/mod.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod cd;
pub mod help;
pub mod ls;
pub mod echo;
mod option_parser;

View File

@ -0,0 +1,27 @@
pub fn parse(args: Vec<String>) -> (Vec<String>, Vec<String>) {
let iter = args.into_iter();
let mut options: Vec<String> = vec![];
let mut parameters: Vec<String> = vec![];
for item in iter {
let i: &str = item.as_ref();
if i.starts_with("--") {
let split: Vec<String> = i.split("=").map(|x| x.to_string()).collect();
let key = split[0].chars().skip(2).collect::<String>();
let value = split[1].to_string();
options.push(key);
options.push(value);
} else if i.starts_with("-") {
let chars = i.chars().skip(1);
let string: String = chars.collect::<String>();
options.push(string.to_string());
options.push("".to_string());
} else {
parameters.push(item.to_string());
}
};
(options, parameters)
}

1
src/lib.rs Normal file
View File

@ -0,0 +1 @@
pub mod commands;

63
src/main.rs Normal file
View File

@ -0,0 +1,63 @@
extern crate rust_shell as shell;
use shell::commands;
use std::io;
use std::io::Write;
use std::process::Command;
use std::str;
fn main() {
println!("Welcome to Rust Shell, try |help|");
let mut directory: String = "/".to_string();
loop {
let (cmd, args) = read(&directory);
match cmd.as_ref() {
"help" => commands::help::exec(args),
"cd" => directory = commands::cd::exec(&directory, args),
"ls" => commands::ls::exec(&directory, args),
"echo" => commands::echo::exec(args),
_ => run_command(&directory, cmd, args)
};
}
}
fn read(directory: &str) -> (String, Vec<String>) {
print!("{} ~ $ ", directory);
let mut stdout = io::stdout();
// Flush to ensure stdout is printed immediately
stdout.flush().unwrap();
let mut stdin = io::stdin();
let mut line: String = "".to_string();
stdin.read_line(&mut line).unwrap();
// Last character is a line-break we don't need
line.pop();
let params: Vec<String> = line.split(" ").map(|x| x.to_string()).collect();
let mut iter = params.into_iter();
let cmd = iter.next().unwrap();
let rest: Vec<String> = iter.collect();
(cmd, rest)
}
fn run_command(directory: &str, command: String, args: Vec<String>) -> () {
let out = match Command::new(command)
.args(&args)
.current_dir(directory)
.output() {
Ok(out) => out.stdout,
Err(e) => {
println!("Error: {}", e);
vec![0u8]
}
};
println!("{}", str::from_utf8(&out).unwrap());
}