diff --git a/src/commands/command.rs b/src/commands/command.rs index 22651a444..8a871bea4 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -115,6 +115,7 @@ impl CommandArgs { callback: fn(T, RunnableContext) -> Result, ) -> Result, ShellError> { let shell_manager = self.shell_manager.clone(); + let source_map = self.call_info.source_map.clone(); let host = self.host.clone(); let args = self.evaluate_once(registry)?; let (input, args) = args.split(); @@ -128,6 +129,7 @@ impl CommandArgs { commands: registry.clone(), shell_manager, name: name_span, + source_map, host, }, callback, @@ -146,6 +148,7 @@ impl CommandArgs { }; let shell_manager = self.shell_manager.clone(); + let source_map = self.call_info.source_map.clone(); let host = self.host.clone(); let args = self.evaluate_once(registry)?; let (input, args) = args.split(); @@ -159,6 +162,7 @@ impl CommandArgs { commands: registry.clone(), shell_manager, name: name_span, + source_map, host, }, raw_args, @@ -172,6 +176,7 @@ pub struct RunnableContext { pub shell_manager: ShellManager, pub host: Arc>, pub commands: CommandRegistry, + pub source_map: SourceMap, pub name: Span, } diff --git a/src/commands/ls.rs b/src/commands/ls.rs index 5b1d260b5..c8e9b2084 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -1,57 +1,56 @@ use crate::errors::ShellError; -use crate::object::dir_entry_dict; use crate::prelude::*; -use std::path::{Path, PathBuf}; + +//pub fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result { +// let args = args.evaluate_once(registry)?; +// let path = PathBuf::from(args.shell_manager.path()); +// let mut full_path = PathBuf::from(path); +// match &args.nth(0) { +// Some(Tagged { +// item: Value::Primitive(Primitive::String(s)), +// .. +// }) => full_path.push(Path::new(&s)), +// _ => {} +// } + +// 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.name_span(), +// )); +// } +// } +// Ok(o) => o, +// }; + +// let mut shell_entries = VecDeque::new(); + +// for entry in entries { +// let entry = entry?; +// let filepath = entry.path(); +// let filename = filepath.strip_prefix(&full_path).unwrap(); +// let value = dir_entry_dict( +// filename, +// &entry.metadata()?, +// Tag::unknown_origin(args.call_info.name_span), +// )?; +// shell_entries.push_back(ReturnSuccess::value(value)) +// } +// Ok(shell_entries.to_output_stream()) pub fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result { + let shell_manager = args.shell_manager.clone(); let args = args.evaluate_once(registry)?; - let path = PathBuf::from(args.shell_manager.path()); - let mut full_path = PathBuf::from(path); - match &args.nth(0) { - Some(Tagged { - item: Value::Primitive(Primitive::String(s)), - .. - }) => full_path.push(Path::new(&s)), - _ => {} - } - - 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.name_span(), - )); - } - } - Ok(o) => o, - }; - - let mut shell_entries = VecDeque::new(); - - for entry in entries { - let entry = entry?; - let filepath = entry.path(); - let filename = filepath.strip_prefix(&full_path).unwrap(); - let value = dir_entry_dict( - filename, - &entry.metadata()?, - Tag::unknown_origin(args.call_info.name_span), - )?; - shell_entries.push_back(ReturnSuccess::value(value)) - } - Ok(shell_entries.to_output_stream()) - - // pub fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result { - // let args = args.evaluate_once(registry)?; - // args.shell_manager.ls(args, args.input) + shell_manager.ls(args) } diff --git a/src/commands/save.rs b/src/commands/save.rs index 00dcd6ee7..9fd78c6db 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -6,13 +6,13 @@ use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::object::Value; use crate::prelude::*; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; pub struct Save; #[derive(Deserialize)] pub struct SaveArgs { - path: Tagged, + path: Option>, raw: bool, } @@ -23,7 +23,7 @@ impl StaticCommand for Save { fn signature(&self) -> Signature { Signature::build("save") - .required("path", SyntaxType::Path) + .optional("path", SyntaxType::Path) .switch("raw") } @@ -44,71 +44,172 @@ pub fn save( context: RunnableContext, ) -> Result { let mut full_path = context.cwd(); - full_path.push(path.item()); - let stream = async_stream_block! { - let input: Vec> = context.input.values.collect().await; - - let contents = match full_path.extension() { - Some(x) if x == "csv" && !save_raw => { - if input.len() != 1 { - yield Err(ShellError::string( - "saving to csv requires a single object (or use --raw)", - )); - } - to_csv_to_string(&value_to_csv_value(&input[0])).unwrap() - } - Some(x) if x == "toml" && !save_raw => { - if input.len() != 1 { - yield Err(ShellError::string( - "saving to toml requires a single object (or use --raw)", - )); - } - toml::to_string(&value_to_toml_value(&input[0])).unwrap() - } - Some(x) if x == "json" && !save_raw => { - if input.len() != 1 { - yield Err(ShellError::string( - "saving to json requires a single object (or use --raw)", - )); - } - serde_json::to_string(&value_to_json_value(&input[0])).unwrap() - } - Some(x) if x == "yml" && !save_raw => { - if input.len() != 1 { - yield Err(ShellError::string( - "saving to yml requires a single object (or use --raw)", - )); - } - serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap() - } - Some(x) if x == "yaml" && !save_raw => { - if input.len() != 1 { - yield Err(ShellError::string( - "saving to yaml requires a single object (or use --raw)", - )); - } - serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap() - } - _ => { - let mut save_data = String::new(); - if input.len() > 0 { - let mut first = true; - for i in input.iter() { - if !first { - save_data.push_str("\n"); - } else { - first = false; + if path.is_none() { + let source_map = context.source_map.clone(); + let stream = async_stream_block! { + let input: Vec> = context.input.values.collect().await; + // If there is no filename, check the metadata for the origin filename + if input.len() > 0 { + let origin = input[0].origin(); + match origin.map(|x| source_map.get(&x)).flatten() { + Some(path) => match path { + SpanSource::File(file) => { + full_path.push(Path::new(file)); } - save_data.push_str(&i.as_string().unwrap()); + _ => { + // yield Err(ShellError::labeled_error( + // "Save requires a filepath", + // "needs path", + // context.name, + // )); + } + }, + None => { + // yield Err(ShellError::labeled_error( + // "Save requires a filepath", + // "needs path", + // context.name, + // )); } } - save_data + } else { + // yield Err(ShellError::labeled_error( + // "Save requires a filepath", + // "needs path", + // context.name, + // )); } + + let contents = match full_path.extension() { + Some(x) if x == "csv" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to csv requires a single object (or use --raw)", + )); + } + to_csv_to_string(&value_to_csv_value(&input[0])).unwrap() + } + Some(x) if x == "toml" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to toml requires a single object (or use --raw)", + )); + } + toml::to_string(&value_to_toml_value(&input[0])).unwrap() + } + Some(x) if x == "json" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to json requires a single object (or use --raw)", + )); + } + serde_json::to_string(&value_to_json_value(&input[0])).unwrap() + } + Some(x) if x == "yml" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to yml requires a single object (or use --raw)", + )); + } + serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap() + } + Some(x) if x == "yaml" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to yaml requires a single object (or use --raw)", + )); + } + serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap() + } + _ => { + let mut save_data = String::new(); + if input.len() > 0 { + let mut first = true; + for i in input.iter() { + if !first { + save_data.push_str("\n"); + } else { + first = false; + } + save_data.push_str(&i.as_string().unwrap()); + } + } + save_data + } + }; + + let _ = std::fs::write(full_path, contents); }; - let _ = std::fs::write(full_path, contents); - }; + Ok(OutputStream::new(stream)) + } else { + full_path.push(path.unwrap().item()); - Ok(OutputStream::new(stream)) + let stream = async_stream_block! { + let input: Vec> = context.input.values.collect().await; + + let contents = match full_path.extension() { + Some(x) if x == "csv" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to csv requires a single object (or use --raw)", + )); + } + to_csv_to_string(&value_to_csv_value(&input[0])).unwrap() + } + Some(x) if x == "toml" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to toml requires a single object (or use --raw)", + )); + } + toml::to_string(&value_to_toml_value(&input[0])).unwrap() + } + Some(x) if x == "json" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to json requires a single object (or use --raw)", + )); + } + serde_json::to_string(&value_to_json_value(&input[0])).unwrap() + } + Some(x) if x == "yml" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to yml requires a single object (or use --raw)", + )); + } + serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap() + } + Some(x) if x == "yaml" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to yaml requires a single object (or use --raw)", + )); + } + serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap() + } + _ => { + let mut save_data = String::new(); + if input.len() > 0 { + let mut first = true; + for i in input.iter() { + if !first { + save_data.push_str("\n"); + } else { + first = false; + } + save_data.push_str(&i.as_string().unwrap()); + } + } + save_data + } + }; + + let _ = std::fs::write(full_path, contents); + }; + + Ok(OutputStream::new(stream)) + } } diff --git a/src/commands/table.rs b/src/commands/table.rs index 6a8edda2f..9e8a4bfbc 100644 --- a/src/commands/table.rs +++ b/src/commands/table.rs @@ -31,7 +31,6 @@ pub fn table(_args: TableArgs, context: RunnableContext) -> Result 0 { let mut host = context.host.lock().unwrap(); let view = TableView::from_list(&input); - println!("{:#?}", view); if let Some(view) = view { handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); } diff --git a/src/shell/completer.rs b/src/shell/completer.rs index e073978a8..60df61c3d 100644 --- a/src/shell/completer.rs +++ b/src/shell/completer.rs @@ -1,5 +1,4 @@ use derive_new::new; -use rustyline::completion::Completer; use rustyline::completion::{self, FilenameCompleter}; use rustyline::line_buffer::LineBuffer; diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 097edb6be..00003a6ff 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -1,10 +1,9 @@ -use crate::commands::command::CallInfo; +use crate::commands::command::{CallInfo, EvaluatedStaticCommandArgs}; use crate::object::dir_entry_dict; use crate::prelude::*; -use crate::shell::completer::{CompletionPair, NuCompleter}; +use crate::shell::completer::NuCompleter; use crate::shell::shell::Shell; -use rustyline::completion::{Completer, FilenameCompleter}; -use rustyline::error::ReadlineError; +use rustyline::completion::FilenameCompleter; use rustyline::hint::{Hinter, HistoryHinter}; use std::path::{Path, PathBuf}; pub struct FilesystemShell { @@ -54,10 +53,10 @@ impl Shell for FilesystemShell { "filesystem".to_string() } - fn ls(&self, call_info: CallInfo, _input: InputStream) -> Result { + fn ls(&self, args: EvaluatedStaticCommandArgs) -> Result { let cwd = self.path.clone(); let mut full_path = PathBuf::from(&self.path); - match &call_info.args.nth(0) { + match &args.nth(0) { Some(Tagged { item: value, .. }) => full_path.push(Path::new(&value.as_string()?)), _ => {} } @@ -78,18 +77,15 @@ impl Shell for FilesystemShell { let entries = match entries { Err(e) => { - if let Some(s) = call_info.args.nth(0) { + 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(), - call_info.name_span, - )); + //FIXME + return Err(ShellError::string(e.to_string())); } } Ok(o) => o, @@ -101,7 +97,7 @@ impl Shell for FilesystemShell { let value = dir_entry_dict( filename, &entry.metadata()?, - Tag::unknown_origin(call_info.name_span), + Tag::unknown_origin(args.call_info.name_span), )?; shell_entries.push_back(ReturnSuccess::value(value)) } @@ -118,7 +114,7 @@ impl Shell for FilesystemShell { let value = dir_entry_dict( filename, &metadata, - Tag::unknown_origin(call_info.name_span), + Tag::unknown_origin(args.call_info.name_span), )?; shell_entries.push_back(ReturnSuccess::value(value)) } diff --git a/src/shell/helper.rs b/src/shell/helper.rs index da3f7c2fb..3480df8b2 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -2,7 +2,6 @@ use crate::parser::nom_input; use crate::parser::parse::token_tree::TokenNode; use crate::parser::parse::tokens::RawToken; use crate::parser::{Pipeline, PipelineElement}; -use crate::shell::completer::CompletionPair; use crate::shell::shell_manager::ShellManager; use crate::Tagged; use ansi_term::Color; diff --git a/src/shell/shell.rs b/src/shell/shell.rs index cb796544c..dcbc0f972 100644 --- a/src/shell/shell.rs +++ b/src/shell/shell.rs @@ -1,12 +1,10 @@ -use crate::commands::command::CallInfo; +use crate::commands::command::{CallInfo, EvaluatedStaticCommandArgs}; use crate::errors::ShellError; -use crate::shell::completer::CompletionPair; use crate::stream::{InputStream, OutputStream}; -use rustyline::error::ReadlineError; pub trait Shell { fn name(&self) -> String; - fn ls(&self, call_info: CallInfo, input: InputStream) -> Result; + fn ls(&self, args: EvaluatedStaticCommandArgs) -> Result; fn cd(&self, call_info: CallInfo, input: InputStream) -> Result; fn path(&self) -> String; fn set_path(&mut self, path: String); diff --git a/src/shell/shell_manager.rs b/src/shell/shell_manager.rs index 9f1e588cc..a54631f02 100644 --- a/src/shell/shell_manager.rs +++ b/src/shell/shell_manager.rs @@ -1,11 +1,8 @@ -use crate::commands::command::CallInfo; +use crate::commands::command::{CallInfo, EvaluatedStaticCommandArgs}; use crate::errors::ShellError; -use crate::shell::completer::CompletionPair; use crate::shell::filesystem_shell::FilesystemShell; use crate::shell::shell::Shell; use crate::stream::{InputStream, OutputStream}; -use rustyline::completion::Completer; -use rustyline::error::ReadlineError; use std::error::Error; use std::sync::{Arc, Mutex}; @@ -88,10 +85,10 @@ impl ShellManager { self.set_path(self.path()); } - pub fn ls(&self, call_info: CallInfo, input: InputStream) -> Result { + pub fn ls(&self, args: EvaluatedStaticCommandArgs) -> Result { let env = self.shells.lock().unwrap(); - env.last().unwrap().ls(call_info, input) + env.last().unwrap().ls(args) } pub fn cd(&self, call_info: CallInfo, input: InputStream) -> Result { let env = self.shells.lock().unwrap(); diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index ce2438b2a..4f2634e78 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -1,8 +1,6 @@ -use crate::commands::command::CallInfo; +use crate::commands::command::{CallInfo, EvaluatedStaticCommandArgs}; use crate::prelude::*; -use crate::shell::completer::CompletionPair; use crate::shell::shell::Shell; -use rustyline::error::ReadlineError; use std::ffi::OsStr; use std::path::PathBuf; @@ -59,7 +57,7 @@ impl Shell for ValueShell { "value".to_string() } - fn ls(&self, _call_info: CallInfo, _input: InputStream) -> Result { + fn ls(&self, _args: EvaluatedStaticCommandArgs) -> Result { Ok(self .members() .map(|x| ReturnSuccess::value(x)) diff --git a/tests/filters_test.rs b/tests/filters_test.rs index 30a3c1665..c2dbacb82 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -106,7 +106,7 @@ fn str_converts_to_int() { nu!( output, cwd("tests/fixtures/formats"), - "open caco3_plastics.csv | get 0 | str tariff_item --to-int | where tariff_item == 2509000000 | get tariff_item | echo $it" + "open caco3_plastics.csv | first 1 | str tariff_item --to-int | where tariff_item == 2509000000 | get tariff_item | echo $it" ); assert_eq!(output, "2509000000");