mirror of
https://github.com/nushell/nushell.git
synced 2025-08-17 22:59:52 +02:00
@@ -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)
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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)))
|
||||
}
|
||||
|
||||
|
@@ -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
21
src/commands/enter.rs
Normal 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())
|
||||
}
|
@@ -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())
|
||||
}
|
||||
}
|
||||
|
@@ -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
7
src/commands/next.rs
Normal 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())
|
||||
}
|
@@ -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
7
src/commands/prev.rs
Normal 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())
|
||||
}
|
@@ -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)
|
||||
|
@@ -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
18
src/commands/shells.rs
Normal 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())
|
||||
}
|
Reference in New Issue
Block a user