Multi shells (#254)

Add multi-shells
This commit is contained in:
Jonathan Turner
2019-08-08 05:49:11 +12:00
committed by GitHub
parent bb50f1eb14
commit c231dd32cd
29 changed files with 759 additions and 224 deletions

View File

@@ -1,52 +1,6 @@
use crate::errors::ShellError;
use crate::prelude::*;
use std::env;
pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
let env = args.env.lock().unwrap();
let cwd = env.path().to_path_buf();
let path = match args.nth(0) {
None => match dirs::home_dir() {
Some(o) => o,
_ => {
return Err(ShellError::labeled_error(
"Can not change to home directory",
"can not go to home",
args.call_info.name_span,
))
}
},
Some(v) => {
let target = v.as_string()?;
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",
v.span().clone(),
));
}
}
}
};
let mut stream = VecDeque::new();
match env::set_current_dir(&path) {
Ok(_) => {}
Err(_) => {
if args.len() > 0 {
return Err(ShellError::labeled_error(
"Can not change to directory",
"directory not found",
args.nth(0).unwrap().span().clone(),
));
} else {
return Err(ShellError::string("Can not change to directory"));
}
}
}
stream.push_back(ReturnSuccess::change_cwd(path));
Ok(stream.into())
args.shell_manager.cd(args.call_info, args.input)
}

View File

@@ -145,12 +145,62 @@ impl InternalCommand {
match item? {
ReturnSuccess::Action(action) => match action {
CommandAction::ChangePath(path) => {
context.env.lock().unwrap().path = path;
context.shell_manager.set_path(path);
}
CommandAction::AddSpanSource(uuid, span_source) => {
context.add_span_source(uuid, span_source);
}
CommandAction::Exit => std::process::exit(0),
CommandAction::EnterShell(location) => {
let path = std::path::Path::new(&location);
if path.is_dir() {
// If it's a directory, add a new filesystem shell
context
.shell_manager
.push(Box::new(FilesystemShell::with_location(location)?));
} else {
// If it's a file, attempt to open the file as a value and enter it
let cwd = context.shell_manager.path();
let full_path = std::path::PathBuf::from(cwd);
let (file_extension, contents, contents_tag, _) =
crate::commands::open::fetch(
&full_path,
&location,
Span::unknown(),
)?;
match contents {
Value::Primitive(Primitive::String(string)) => {
let value = crate::commands::open::parse_as_value(
file_extension,
string,
contents_tag,
Span::unknown(),
)?;
context.shell_manager.push(Box::new(ValueShell::new(value)));
}
value => context
.shell_manager
.push(Box::new(ValueShell::new(value.tagged(Tag::unknown())))),
}
}
}
CommandAction::PreviousShell => {
context.shell_manager.prev();
}
CommandAction::NextShell => {
context.shell_manager.next();
}
CommandAction::LeaveShell => {
context.shell_manager.pop();
if context.shell_manager.is_empty() {
std::process::exit(0);
}
}
},
ReturnSuccess::Value(v) => {
@@ -298,7 +348,7 @@ impl ExternalCommand {
process = Exec::shell(new_arg_string);
}
process = process.cwd(context.env.lock().unwrap().path());
process = process.cwd(context.shell_manager.path());
let mut process = match stream_next {
StreamNext::Last => process,

View File

@@ -6,7 +6,6 @@ use crate::parser::registry::{self, Args};
use crate::prelude::*;
use getset::Getters;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use uuid::Uuid;
#[derive(Deserialize, Serialize, Debug, Clone)]
@@ -20,7 +19,7 @@ pub struct CallInfo {
#[get = "crate"]
pub struct CommandArgs {
pub host: Arc<Mutex<dyn Host + Send>>,
pub env: Arc<Mutex<Environment>>,
pub shell_manager: ShellManager,
pub call_info: CallInfo,
pub input: InputStream,
}
@@ -60,9 +59,13 @@ pub struct SinkCommandArgs {
#[derive(Debug, Serialize, Deserialize)]
pub enum CommandAction {
ChangePath(PathBuf),
ChangePath(String),
AddSpanSource(Uuid, SpanSource),
Exit,
EnterShell(String),
PreviousShell,
NextShell,
LeaveShell,
}
#[derive(Debug, Serialize, Deserialize)]
@@ -80,7 +83,7 @@ impl From<Tagged<Value>> for ReturnValue {
}
impl ReturnSuccess {
pub fn change_cwd(path: PathBuf) -> ReturnValue {
pub fn change_cwd(path: String) -> ReturnValue {
Ok(ReturnSuccess::Action(CommandAction::ChangePath(path)))
}

View File

@@ -3,7 +3,7 @@ use crate::parser::hir::SyntaxType;
use crate::parser::registry::{CommandConfig, NamedType, PositionalType};
use crate::prelude::*;
use indexmap::IndexMap;
use std::path::Path;
use std::path::{Path, PathBuf};
pub struct Copycp;
@@ -32,8 +32,8 @@ impl Command for Copycp {
}
pub fn cp(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut source = args.env.lock().unwrap().path().to_path_buf();
let mut destination = args.env.lock().unwrap().path().to_path_buf();
let mut source = PathBuf::from(args.shell_manager.path());
let mut destination = PathBuf::from(args.shell_manager.path());
let mut dst = String::new();

21
src/commands/enter.rs Normal file
View File

@@ -0,0 +1,21 @@
use crate::commands::command::CommandAction;
use crate::errors::ShellError;
use crate::prelude::*;
pub fn enter(args: CommandArgs) -> Result<OutputStream, ShellError> {
//TODO: We could also enter a value in the stream
if args.len() == 0 {
return Err(ShellError::labeled_error(
"Enter requires a path",
"needs parameter",
args.call_info.name_span,
));
}
let location = args.expect_nth(0)?.as_string()?;
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
location,
)))]
.into())
}

View File

@@ -1,7 +1,39 @@
use crate::commands::command::CommandAction;
use crate::errors::ShellError;
use crate::parser::registry::{CommandConfig, NamedType};
use crate::prelude::*;
use indexmap::IndexMap;
pub fn exit(_args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into())
pub struct Exit;
impl Command for Exit {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
exit(args)
}
fn name(&self) -> &str {
"exit"
}
fn config(&self) -> CommandConfig {
let mut named: IndexMap<String, NamedType> = IndexMap::new();
named.insert("now".to_string(), NamedType::Switch);
CommandConfig {
name: self.name().to_string(),
positional: vec![],
rest_positional: false,
named,
is_sink: false,
is_filter: false,
}
}
}
pub fn exit(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.call_info.args.has("now") {
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into())
} else {
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::LeaveShell))].into())
}
}

View File

@@ -1,80 +1,6 @@
use crate::errors::ShellError;
use crate::object::dir_entry_dict;
use crate::prelude::*;
use std::path::{Path, PathBuf};
pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
let env = args.env.lock().unwrap();
let path = env.path.to_path_buf();
let cwd = path.clone();
let mut full_path = PathBuf::from(path);
match &args.nth(0) {
Some(Tagged { item: value, .. }) => full_path.push(Path::new(&value.as_string()?)),
_ => {}
}
let entries = glob::glob(&full_path.to_string_lossy());
if entries.is_err() {
return Err(ShellError::string("Invalid pattern."));
}
let mut shell_entries = VecDeque::new();
let entries: Vec<_> = entries.unwrap().collect();
// If this is a single entry, try to display the contents of the entry if it's a directory
if entries.len() == 1 {
if let Ok(entry) = &entries[0] {
if entry.is_dir() {
let entries = std::fs::read_dir(&full_path);
let entries = match entries {
Err(e) => {
if let Some(s) = args.nth(0) {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
s.span(),
));
} else {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
args.call_info.name_span,
));
}
}
Ok(o) => o,
};
for entry in entries {
let entry = entry?;
let filepath = entry.path();
let filename = filepath.strip_prefix(&cwd).unwrap();
let value = dir_entry_dict(
filename,
&entry.metadata()?,
Tag::unknown_origin(args.call_info.name_span),
)?;
shell_entries.push_back(ReturnSuccess::value(value))
}
return Ok(shell_entries.to_output_stream());
}
}
}
// Enumerate the entries from the glob and add each
for entry in entries {
if let Ok(entry) = entry {
let filename = entry.strip_prefix(&cwd).unwrap();
let metadata = std::fs::metadata(&entry)?;
let value = dir_entry_dict(
filename,
&metadata,
Tag::unknown_origin(args.call_info.name_span),
)?;
shell_entries.push_back(ReturnSuccess::value(value))
}
}
Ok(shell_entries.to_output_stream())
args.shell_manager.ls(args.call_info, args.input)
}

7
src/commands/next.rs Normal file
View File

@@ -0,0 +1,7 @@
use crate::commands::command::CommandAction;
use crate::errors::ShellError;
use crate::prelude::*;
pub fn next(_args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::NextShell))].into())
}

View File

@@ -12,11 +12,7 @@ command! {
let span = args.call_info.name_span;
let cwd = args
.env
.lock()
.unwrap()
.path()
.to_path_buf();
.shell_manager.path();
let full_path = PathBuf::from(cwd);

7
src/commands/prev.rs Normal file
View File

@@ -0,0 +1,7 @@
use crate::commands::command::CommandAction;
use crate::errors::ShellError;
use crate::prelude::*;
pub fn prev(_args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::PreviousShell))].into())
}

View File

@@ -5,6 +5,7 @@ use crate::prelude::*;
use glob::glob;
use indexmap::IndexMap;
use std::path::PathBuf;
pub struct Remove;
@@ -33,7 +34,7 @@ impl Command for Remove {
}
pub fn rm(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut full_path = args.env.lock().unwrap().path().to_path_buf();
let mut full_path = PathBuf::from(args.shell_manager.path());
match args
.nth(0)

View File

@@ -5,11 +5,40 @@ use crate::commands::to_toml::value_to_toml_value;
use crate::commands::to_yaml::value_to_yaml_value;
use crate::errors::ShellError;
use crate::object::{Primitive, Value};
use crate::parser::registry::{CommandConfig, NamedType};
use crate::prelude::*;
use crate::SpanSource;
use indexmap::IndexMap;
use std::path::{Path, PathBuf};
pub struct Save;
impl Sink for Save {
fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> {
save(args)
}
fn name(&self) -> &str {
"save"
}
fn config(&self) -> CommandConfig {
let mut named: IndexMap<String, NamedType> = IndexMap::new();
named.insert("raw".to_string(), NamedType::Switch);
CommandConfig {
name: self.name().to_string(),
positional: vec![],
rest_positional: false,
named,
is_sink: false,
is_filter: false,
}
}
}
pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> {
let cwd = args.ctx.env.lock().unwrap().path().to_path_buf();
let cwd = args.ctx.shell_manager.path();
let mut full_path = PathBuf::from(cwd);
let save_raw = if args.call_info.args.has("raw") {

18
src/commands/shells.rs Normal file
View File

@@ -0,0 +1,18 @@
use crate::errors::ShellError;
use crate::object::TaggedDictBuilder;
use crate::prelude::*;
pub fn shells(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut shells_out = VecDeque::new();
let span = args.call_info.name_span;
for shell in args.shell_manager.shells.lock().unwrap().iter() {
let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(span));
dict.insert("name", shell.name());
dict.insert("path", shell.path());
shells_out.push_back(dict.into_tagged_value());
}
Ok(shells_out.to_output_stream())
}