Merge pull request #105 from jonathandturner/enter

Add enter and exit functions
This commit is contained in:
Jonathan Turner 2019-06-14 10:53:58 +12:00 committed by GitHub
commit a537fc96c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 384 additions and 86 deletions

View file

@ -60,6 +60,8 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
command("from-yaml", from_yaml::from_yaml),
command("get", get::get),
command("open", open::open),
command("enter", enter::enter),
command("exit", exit::exit),
command("pick", pick::pick),
command("split-column", split_column::split_column),
command("split-row", split_row::split_row),
@ -109,14 +111,22 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
continue;
}
let readline = rl.readline(&format!(
"{}{}> ",
context.env.lock().unwrap().cwd().display(),
match current_branch() {
Some(s) => format!("({})", s),
None => "".to_string(),
}
));
let (obj, cwd) = {
let env = context.env.lock().unwrap();
let last = env.last().unwrap();
(last.obj().clone(), last.path().display().to_string())
};
let readline = match obj {
Value::Filesystem => rl.readline(&format!(
"{}{}> ",
cwd,
match current_branch() {
Some(s) => format!("({})", s),
None => "".to_string(),
}
)),
_ => rl.readline(&format!("{}{}> ", obj.type_name(), cwd)),
};
match process_line(readline, &mut context).await {
LineResult::Success(line) => {
@ -207,8 +217,6 @@ impl std::ops::Try for LineResult {
async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context) -> LineResult {
match &readline {
Ok(line) if line.trim() == "exit" => LineResult::Break,
Ok(line) if line.trim() == "" => LineResult::Success(line.clone()),
Ok(line) => {

View file

@ -5,6 +5,8 @@ crate mod classified;
crate mod clip;
crate mod command;
crate mod config;
crate mod enter;
crate mod exit;
crate mod first;
crate mod from_json;
crate mod from_toml;

View file

@ -1,44 +1,77 @@
use crate::errors::ShellError;
use crate::prelude::*;
use std::env;
use std::path::PathBuf;
pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
let cwd = args.env.lock().unwrap().cwd().to_path_buf();
let path = match args.positional.first() {
None => match dirs::home_dir() {
Some(o) => o,
_ => return Err(ShellError::string("Can not change to home directory")),
},
Some(v) => {
let target = v.as_string()?.clone();
match dunce::canonicalize(cwd.join(&target).as_path()) {
Ok(p) => p,
let env = args.env.lock().unwrap();
let latest = env.last().unwrap();
match latest.obj {
Value::Filesystem => {
let cwd = latest.path().to_path_buf();
let path = match args.positional.first() {
None => match dirs::home_dir() {
Some(o) => o,
_ => return Err(ShellError::string("Can not change to home directory")),
},
Some(v) => {
let target = v.as_string()?.clone();
match dunce::canonicalize(cwd.join(&target).as_path()) {
Ok(p) => p,
Err(_) => {
return Err(ShellError::labeled_error(
"Can not change to directory",
"directory not found",
args.positional[0].span.clone(),
));
}
}
}
};
let mut stream = VecDeque::new();
match env::set_current_dir(&path) {
Ok(_) => {}
Err(_) => {
return Err(ShellError::labeled_error(
"Can not change to directory",
"directory not found",
args.positional[0].span.clone(),
));
if args.positional.len() > 0 {
return Err(ShellError::labeled_error(
"Can not change to directory",
"directory not found",
args.positional[0].span.clone(),
));
} else {
return Err(ShellError::string("Can not change to directory"));
}
}
}
stream.push_back(ReturnValue::change_cwd(path));
Ok(stream.boxed())
}
};
let mut stream = VecDeque::new();
match env::set_current_dir(&path) {
Ok(_) => {}
Err(_) => {
if args.positional.len() > 0 {
return Err(ShellError::labeled_error(
"Can not change to directory",
"directory not found",
args.positional[0].span.clone(),
));
} else {
return Err(ShellError::string("Can not change to directory"));
}
_ => {
let mut stream = VecDeque::new();
match args.positional.first() {
None => {
stream.push_back(ReturnValue::change_cwd(PathBuf::from("/")));
}
Some(v) => {
let mut cwd = latest.path().to_path_buf();
let target = v.as_string()?.clone();
match target {
x if x == ".." => {
cwd.pop();
}
_ => match target.chars().nth(0) {
Some(x) if x == '/' => cwd = PathBuf::from(target),
_ => {
cwd.push(target);
}
},
}
stream.push_back(ReturnValue::change_cwd(cwd));
}
};
Ok(stream.boxed())
}
}
stream.push_back(ReturnValue::change_cwd(path));
Ok(stream.boxed())
}

View file

@ -6,9 +6,9 @@ use crate::prelude::*;
use bytes::{BufMut, BytesMut};
use futures_codec::{Decoder, Encoder, Framed};
use std::io::{Error, ErrorKind};
use std::path::PathBuf;
use std::sync::Arc;
use subprocess::Exec;
/// A simple `Codec` implementation that splits up data into lines.
pub struct LinesCodec {}
@ -119,8 +119,27 @@ impl InternalCommand {
let stream = result.filter_map(move |v| match v {
ReturnValue::Action(action) => match action {
CommandAction::ChangeCwd(cwd) => {
env.lock().unwrap().cwd = cwd;
CommandAction::ChangePath(path) => {
env.lock().unwrap().last_mut().map(|x| {
x.path = path;
x
});
futures::future::ready(None)
}
CommandAction::Enter(obj) => {
let new_env = Environment {
obj: obj,
path: PathBuf::from("/"),
};
env.lock().unwrap().push(new_env);
futures::future::ready(None)
}
CommandAction::Exit => {
let mut v = env.lock().unwrap();
if v.len() == 1 {
std::process::exit(0);
}
v.pop();
futures::future::ready(None)
}
},
@ -210,7 +229,7 @@ impl ExternalCommand {
}
process = Exec::shell(new_arg_string);
}
process = process.cwd(context.env.lock().unwrap().cwd());
process = process.cwd(context.env.lock().unwrap().first().unwrap().path());
let mut process = match stream_next {
StreamNext::Last => process,

View file

@ -8,7 +8,7 @@ use std::path::PathBuf;
pub struct CommandArgs {
pub host: Arc<Mutex<dyn Host + Send>>,
pub env: Arc<Mutex<Environment>>,
pub env: Arc<Mutex<Vec<Environment>>>,
pub name_span: Option<Span>,
pub positional: Vec<Spanned<Value>>,
pub named: indexmap::IndexMap<String, Value>,
@ -25,7 +25,9 @@ pub struct SinkCommandArgs {
#[derive(Debug)]
pub enum CommandAction {
ChangeCwd(PathBuf),
ChangePath(PathBuf),
Enter(Value),
Exit,
}
#[derive(Debug)]
@ -36,7 +38,7 @@ pub enum ReturnValue {
impl ReturnValue {
crate fn change_cwd(path: PathBuf) -> ReturnValue {
ReturnValue::Action(CommandAction::ChangeCwd(path))
ReturnValue::Action(CommandAction::ChangePath(path))
}
}

137
src/commands/enter.rs Normal file
View file

@ -0,0 +1,137 @@
use crate::commands::command::CommandAction;
use crate::errors::ShellError;
use crate::object::{Primitive, Value};
use crate::parser::lexer::Spanned;
use crate::prelude::*;
use std::path::{Path, PathBuf};
pub fn enter(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.positional.len() == 0 {
return Err(ShellError::string("open requires a filepath or url"));
}
let cwd = args
.env
.lock()
.unwrap()
.first()
.unwrap()
.path()
.to_path_buf();
let mut full_path = PathBuf::from(cwd);
let (file_extension, contents) = match &args.positional[0].item {
Value::Primitive(Primitive::String(s)) => {
if s.starts_with("http:") || s.starts_with("https:") {
let response = reqwest::get(s);
match response {
Ok(mut r) => match r.text() {
Ok(s) => {
let fname = r
.url()
.path_segments()
.and_then(|segments| segments.last())
.and_then(|name| if name.is_empty() { None } else { Some(name) })
.and_then(|name| {
PathBuf::from(name)
.extension()
.map(|name| name.to_string_lossy().to_string())
});
(fname, s)
}
Err(_) => {
return Err(ShellError::labeled_error(
"Web page contents corrupt",
"received garbled data",
args.positional[0].span,
));
}
},
Err(_) => {
return Err(ShellError::labeled_error(
"URL could not be opened",
"url not found",
args.positional[0].span,
));
}
}
} else {
full_path.push(Path::new(&s));
match std::fs::read_to_string(&full_path) {
Ok(s) => (
full_path
.extension()
.map(|name| name.to_string_lossy().to_string()),
s,
),
Err(_) => {
return Err(ShellError::labeled_error(
"File cound not be opened",
"file not found",
args.positional[0].span,
));
}
}
}
}
_ => {
return Err(ShellError::labeled_error(
"Expected string value for filename",
"expected filename",
args.positional[0].span,
));
}
};
let mut stream = VecDeque::new();
let open_raw = match args.positional.get(1) {
Some(Spanned {
item: Value::Primitive(Primitive::String(s)),
..
}) if s == "--raw" => true,
Some(v) => {
return Err(ShellError::labeled_error(
"Unknown flag for open",
"unknown flag",
v.span,
))
}
_ => false,
};
match file_extension {
Some(x) if x == "toml" && !open_raw => {
stream.push_back(ReturnValue::Action(CommandAction::Enter(
crate::commands::from_toml::from_toml_string_to_value(contents),
)));
}
Some(x) if x == "json" && !open_raw => {
stream.push_back(ReturnValue::Action(CommandAction::Enter(
crate::commands::from_json::from_json_string_to_value(contents),
)));
}
Some(x) if x == "xml" && !open_raw => {
stream.push_back(ReturnValue::Action(CommandAction::Enter(
crate::commands::from_xml::from_xml_string_to_value(contents),
)));
}
Some(x) if x == "yml" && !open_raw => {
stream.push_back(ReturnValue::Action(CommandAction::Enter(
crate::commands::from_yaml::from_yaml_string_to_value(contents),
)));
}
Some(x) if x == "yaml" && !open_raw => {
stream.push_back(ReturnValue::Action(CommandAction::Enter(
crate::commands::from_yaml::from_yaml_string_to_value(contents),
)));
}
_ => {
stream.push_back(ReturnValue::Action(CommandAction::Enter(Value::Primitive(
Primitive::String(contents),
))));
}
}
Ok(stream.boxed())
}

12
src/commands/exit.rs Normal file
View file

@ -0,0 +1,12 @@
use crate::commands::command::CommandAction;
use crate::errors::ShellError;
use crate::object::{Primitive, Value};
use crate::parser::lexer::Spanned;
use crate::prelude::*;
use std::path::{Path, PathBuf};
pub fn exit(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut stream = VecDeque::new();
stream.push_back(ReturnValue::Action(CommandAction::Exit));
Ok(stream.boxed())
}

View file

@ -2,11 +2,14 @@ use crate::errors::ShellError;
use crate::object::{dir_entry_dict, Primitive, Value};
use crate::parser::lexer::Spanned;
use crate::prelude::*;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
let cwd = args.env.lock().unwrap().cwd().to_path_buf();
let mut full_path = PathBuf::from(cwd);
let env = args.env.lock().unwrap();
let path = env.last().unwrap().path.to_path_buf();
let obj = &env.last().unwrap().obj;
let mut full_path = PathBuf::from(path);
match &args.positional.get(0) {
Some(Spanned {
item: Value::Primitive(Primitive::String(s)),
@ -15,29 +18,60 @@ pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
_ => {}
}
let entries = std::fs::read_dir(&full_path);
match obj {
Value::Filesystem => {
let entries = std::fs::read_dir(&full_path);
let entries = match entries {
Err(e) => {
if let Some(s) = args.positional.get(0) {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
s.span,
));
} else {
return Err(ShellError::string(e.to_string()));
let entries = match entries {
Err(e) => {
if let Some(s) = args.positional.get(0) {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
s.span,
));
} else {
return Err(ShellError::string(e.to_string()));
}
}
Ok(o) => o,
};
let mut shell_entries = VecDeque::new();
for entry in entries {
let value = Value::Object(dir_entry_dict(&entry?)?);
shell_entries.push_back(ReturnValue::Value(value))
}
Ok(shell_entries.boxed())
}
_ => {
let mut entries = VecDeque::new();
let mut viewed = obj;
let sep_string = std::path::MAIN_SEPARATOR.to_string();
let sep = OsStr::new(&sep_string);
for p in full_path.iter() {
match p {
x if x == sep => {}
step => match viewed.get_data_by_key(step.to_str().unwrap()) {
Some(v) => {
viewed = v;
}
_ => println!("Obj not Some"),
},
}
}
match viewed {
Value::List(l) => {
for item in l {
entries.push_back(ReturnValue::Value(item.copy()));
}
}
x => {
entries.push_back(ReturnValue::Value(x.clone()));
}
}
Ok(entries.boxed())
}
Ok(o) => o,
};
let mut shell_entries = VecDeque::new();
for entry in entries {
let value = Value::Object(dir_entry_dict(&entry?)?);
shell_entries.push_back(ReturnValue::Value(value))
}
Ok(shell_entries.boxed())
}

View file

@ -9,7 +9,14 @@ pub fn open(args: CommandArgs) -> Result<OutputStream, ShellError> {
return Err(ShellError::string("open requires a filepath or url"));
}
let cwd = args.env.lock().unwrap().cwd().to_path_buf();
let cwd = args
.env
.lock()
.unwrap()
.first()
.unwrap()
.path()
.to_path_buf();
let mut full_path = PathBuf::from(cwd);
let (file_extension, contents) = match &args.positional[0].item {

View file

@ -9,7 +9,15 @@ pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> {
return Err(ShellError::string("save requires a filepath"));
}
let cwd = args.ctx.env.lock().unwrap().cwd().to_path_buf();
let cwd = args
.ctx
.env
.lock()
.unwrap()
.first()
.unwrap()
.path()
.to_path_buf();
let mut full_path = PathBuf::from(cwd);
match &(args.positional[0].item) {
Value::Primitive(Primitive::String(s)) => full_path.push(Path::new(s)),

View file

@ -9,7 +9,14 @@ pub fn size(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.positional.is_empty() {
return Err(ShellError::string("size requires at least one file"));
}
let cwd = args.env.lock().unwrap().cwd().to_path_buf();
let cwd = args
.env
.lock()
.unwrap()
.first()
.unwrap()
.path()
.to_path_buf();
let mut contents = String::new();

View file

@ -30,7 +30,14 @@ pub fn view(args: CommandArgs) -> Result<OutputStream, ShellError> {
}
};
let cwd = args.env.lock().unwrap().cwd().to_path_buf();
let cwd = args
.env
.lock()
.unwrap()
.first()
.unwrap()
.path()
.to_path_buf();
let printer = PrettyPrinter::default()
.line_numbers(false)

View file

@ -13,7 +13,7 @@ pub struct Context {
commands: IndexMap<String, Arc<dyn Command>>,
sinks: IndexMap<String, Arc<dyn Sink>>,
crate host: Arc<Mutex<dyn Host + Send>>,
crate env: Arc<Mutex<Environment>>,
crate env: Arc<Mutex<Vec<Environment>>>,
}
impl Context {
@ -22,7 +22,7 @@ impl Context {
commands: indexmap::IndexMap::new(),
sinks: indexmap::IndexMap::new(),
host: Arc::new(Mutex::new(crate::env::host::BasicHost)),
env: Arc::new(Mutex::new(Environment::basic()?)),
env: Arc::new(Mutex::new(vec![Environment::basic()?])),
})
}

View file

@ -1,18 +1,27 @@
use crate::object::base::Value;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct Environment {
crate cwd: PathBuf,
crate obj: Value,
crate path: PathBuf,
}
impl Environment {
pub fn basic() -> Result<Environment, std::io::Error> {
let cwd = std::env::current_dir()?;
let path = std::env::current_dir()?;
Ok(Environment { cwd })
Ok(Environment {
obj: Value::Filesystem,
path,
})
}
pub fn cwd(&self) -> &Path {
self.cwd.as_path()
pub fn path(&self) -> &Path {
self.path.as_path()
}
pub fn obj(&self) -> &Value {
&self.obj
}
}

View file

@ -40,6 +40,11 @@ impl RenderView for GenericView<'value> {
host.stdout(&format!("{:?}", e));
Ok(())
}
Value::Filesystem => {
host.stdout("<filesystem>");
Ok(())
}
}
}
}

View file

@ -38,6 +38,7 @@ impl TreeView {
}
Value::Block(_) => {}
Value::Error(_) => {}
Value::Filesystem => {}
}
}
crate fn from_value(value: &Value) -> TreeView {

View file

@ -154,6 +154,7 @@ pub enum Value {
Object(crate::object::Dictionary),
List(Vec<Value>),
Block(Block),
Filesystem,
#[allow(unused)]
Error(Box<ShellError>),
@ -167,6 +168,7 @@ impl Value {
Value::List(_) => format!("list"),
Value::Block(_) => format!("block"),
Value::Error(_) => format!("error"),
Value::Filesystem => format!("filesystem"),
}
}
@ -177,6 +179,7 @@ impl Value {
Value::Block(_) => vec![DataDescriptor::value_of()],
Value::List(_) => vec![],
Value::Error(_) => vec![DataDescriptor::value_of()],
Value::Filesystem => vec![],
}
}
@ -189,7 +192,7 @@ impl Value {
Value::Object(o) => match o.get_data_by_key(name) {
Some(v) => return Some(v),
None => {}
}
},
_ => {}
}
}
@ -202,6 +205,7 @@ impl Value {
crate fn get_data(&'a self, desc: &DataDescriptor) -> MaybeOwned<'a, Value> {
match self {
p @ Value::Primitive(_) => MaybeOwned::Borrowed(p),
p @ Value::Filesystem => MaybeOwned::Borrowed(p),
Value::Object(o) => o.get_data(desc),
Value::Block(_) => MaybeOwned::Owned(Value::nothing()),
Value::List(_) => MaybeOwned::Owned(Value::nothing()),
@ -219,6 +223,7 @@ impl Value {
Value::List(list)
}
Value::Error(e) => Value::Error(Box::new(e.copy_error())),
Value::Filesystem => Value::Filesystem,
}
}
@ -229,6 +234,7 @@ impl Value {
Value::Object(_) => format!("[object Object]"),
Value::List(_) => format!("[list List]"),
Value::Error(e) => format!("{}", e),
Value::Filesystem => format!("<filesystem>"),
}
}

View file

@ -44,6 +44,7 @@ impl Serialize for Value {
Value::List(l) => l.serialize(serializer),
Value::Block(b) => b.serialize(serializer),
Value::Error(e) => e.serialize(serializer),
Value::Filesystem => "".serialize(serializer),
}
}
}

View file

@ -1 +1 @@
"S"
markup

View file

@ -1,3 +1,3 @@
cd tests
open test.json | to-json | from-json | get glossary.GlossDiv.title | echo $it
open test.json | get glossary.GlossDiv.GlossList.GlossEntry.GlossSee | echo $it
exit