diff --git a/src/cli.rs b/src/cli.rs index d54abc8c9e..4b5d5c6813 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -231,7 +231,7 @@ pub async fn cli() -> Result<(), Box> { let last = env.back().unwrap(); (last.obj().clone(), last.path().display().to_string()) }; - let readline = match obj { + let readline = match obj.item { Value::Filesystem => rl.readline(&format!( "{}{}> ", cwd, @@ -396,7 +396,7 @@ async fn process_line(readline: Result, ctx: &mut Context } (Some(ClassifiedCommand::Sink(left)), None) => { - let input_vec: Vec = input.objects.into_vec().await; + let input_vec: Vec> = input.objects.into_vec().await; if let Err(err) = left.run(ctx, input_vec) { return LineResult::Error(line.clone(), err); } diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index be49c25e07..55b4b42923 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -22,7 +22,7 @@ pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { Ok(()) } -fn equal_shapes(input: &Vec) -> bool { +fn equal_shapes(input: &Vec>) -> bool { let mut items = input.iter(); let item = match items.next() { diff --git a/src/commands/cd.rs b/src/commands/cd.rs index 7df0f9b3be..8d76b5cbd0 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -6,8 +6,9 @@ use std::path::PathBuf; pub fn cd(args: CommandArgs) -> Result { let env = args.env.lock().unwrap(); let latest = env.back().unwrap(); + let obj = latest.obj; - match latest.obj { + match obj.item { Value::Filesystem => { let cwd = latest.path().to_path_buf(); diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 8ac0f5fe10..d29715c779 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -59,24 +59,6 @@ impl ClassifiedInputStream { } } - pub fn into_vec(self) -> impl std::future::Future> { - self.objects.into_vec() - } - - crate fn from_vec(stream: VecDeque) -> ClassifiedInputStream { - ClassifiedInputStream { - objects: stream.into(), - stdin: None, - } - } - - crate fn from_list(stream: Vec) -> ClassifiedInputStream { - ClassifiedInputStream { - objects: stream.into(), - stdin: None, - } - } - crate fn from_input_stream(stream: impl Into) -> ClassifiedInputStream { ClassifiedInputStream { objects: stream.into(), @@ -111,7 +93,11 @@ crate struct SinkCommand { } impl SinkCommand { - crate fn run(self, context: &mut Context, input: Vec) -> Result<(), ShellError> { + crate fn run( + self, + context: &mut Context, + input: Vec>, + ) -> Result<(), ShellError> { context.run_sink(self.command, self.name_span.clone(), self.args, input) } } @@ -160,7 +146,11 @@ impl InternalCommand { } CommandAction::Exit => match context.env.lock().unwrap().pop_back() { Some(Environment { - obj: Value::Filesystem, + obj: + Spanned { + item: Value::Filesystem, + .. + }, .. }) => std::process::exit(0), None => std::process::exit(-1), @@ -199,7 +189,7 @@ impl ExternalCommand { stream_next: StreamNext, ) -> Result { let stdin = input.stdin; - let inputs: Vec = input.objects.into_vec().await; + let inputs: Vec> = input.objects.into_vec().await; trace!("-> {}", self.name); trace!("inputs = {:?}", inputs); diff --git a/src/commands/command.rs b/src/commands/command.rs index 7d51cd38f2..92b0dc3b82 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -7,7 +7,7 @@ use crate::parser::{ use crate::prelude::*; use getset::Getters; use serde::{Deserialize, Serialize}; -use std::{ops::Try, path::PathBuf}; +use std::path::PathBuf; #[derive(Getters)] #[get = "crate"] @@ -49,26 +49,26 @@ pub struct SinkCommandArgs { pub ctx: Context, pub name_span: Option, pub args: Args, - pub input: Vec, + pub input: Vec>, } #[derive(Debug, Serialize, Deserialize)] pub enum CommandAction { ChangePath(PathBuf), - Enter(Value), + Enter(Spanned), Exit, } #[derive(Debug, Serialize, Deserialize)] pub enum ReturnSuccess { - Value(Value), + Value(Spanned), Action(CommandAction), } pub type ReturnValue = Result; -impl From for ReturnValue { - fn from(input: Value) -> ReturnValue { +impl From> for ReturnValue { + fn from(input: Spanned) -> ReturnValue { Ok(ReturnSuccess::Value(input)) } } @@ -78,8 +78,12 @@ impl ReturnSuccess { Ok(ReturnSuccess::Action(CommandAction::ChangePath(path))) } - pub fn value(input: Value) -> ReturnValue { - Ok(ReturnSuccess::Value(input)) + pub fn value(input: impl Into>) -> ReturnValue { + Ok(ReturnSuccess::Value(input.into())) + } + + pub fn spanned_value(input: Value, span: Span) -> ReturnValue { + Ok(ReturnSuccess::Value(Spanned::from_item(input, span))) } } diff --git a/src/commands/config.rs b/src/commands/config.rs index 313d2f93be..839fe9c275 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,4 +1,3 @@ -#[macro_use] use crate::prelude::*; use crate::errors::ShellError; @@ -44,7 +43,7 @@ impl Command for Config { } pub fn config(args: CommandArgs) -> Result { - let mut result = crate::object::config::config()?; + let mut result = crate::object::config::config(args.name_span)?; trace!("{:#?}", args.args.positional); trace!("{:#?}", args.args.named); @@ -56,7 +55,7 @@ pub fn config(args: CommandArgs) -> Result { .ok_or_else(|| ShellError::string(&format!("Missing key {} in config", key)))?; return Ok( - vec![value.clone()].into(), // futures::stream::once(futures::future::ready(ReturnSuccess::Value(value.clone()))).into(), + stream![value.clone()], // futures::stream::once(futures::future::ready(ReturnSuccess::Value(value.clone()))).into(), ); } @@ -66,16 +65,21 @@ pub fn config(args: CommandArgs) -> Result { config::write_config(&result)?; - return Ok(stream![Value::Object(result.into())].from_input_stream()); + return Ok( + stream![Spanned::from_item(Value::Object(result.into()), v.span())] + .from_input_stream(), + ); } } - if let Some(_) = args.get("clear") { + if let Some(c) = args.get("clear") { result.clear(); config::write_config(&result)?; - return Ok(stream![Value::Object(result.into())].from_input_stream()); + return Ok( + stream![Spanned::from_item(Value::Object(result.into()), c.span())].from_input_stream(), + ); } if let Some(v) = args.get("remove") { @@ -90,7 +94,7 @@ pub fn config(args: CommandArgs) -> Result { ))); } - let obj = VecDeque::from_iter(vec![Value::Object(result.into())]); + let obj = VecDeque::from_iter(vec![Value::Object(result.into()).spanned(v)]); return Ok(obj.from_input_stream()); } diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 8b6f547e6b..7fd557f6dc 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -49,7 +49,7 @@ pub fn enter(args: CommandArgs) -> Result { let full_path = PathBuf::from(cwd); - let (file_extension, contents) = match &args.expect_nth(0)?.item { + let (file_extension, contents, contents_span) = match &args.expect_nth(0)?.item { Value::Primitive(Primitive::String(s)) => fetch(&full_path, s, args.expect_nth(0)?.span)?, _ => { return Err(ShellError::labeled_error( @@ -90,7 +90,7 @@ pub fn enter(args: CommandArgs) -> Result { }; stream.push_back(Ok(ReturnSuccess::Action(CommandAction::Enter( - parse_as_value(file_extension, contents, span)?, + parse_as_value(file_extension, contents, contents_span, span)?, )))); Ok(stream.into()) diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs index 7fa5dc3a59..d4697d0acc 100644 --- a/src/commands/from_ini.rs +++ b/src/commands/from_ini.rs @@ -31,9 +31,9 @@ pub fn from_ini(args: CommandArgs) -> Result { let span = args.name_span; Ok(out .values - .map(move |a| match a { + .map(move |a| match a.item { Value::Primitive(Primitive::String(s)) => match from_ini_string_to_value(s) { - Ok(x) => Ok(ReturnSuccess::Value(x)), + Ok(x) => ReturnSuccess::value(x.spanned(a.span)), Err(e) => Err(ShellError::maybe_labeled_error( "Could not parse as INI", format!("{:#?}", e), diff --git a/src/commands/from_json.rs b/src/commands/from_json.rs index cb1610b769..edf8c1f29b 100644 --- a/src/commands/from_json.rs +++ b/src/commands/from_json.rs @@ -12,7 +12,7 @@ fn convert_json_value_to_nu_value(v: &serde_hjson::Value) -> Value { serde_hjson::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))), serde_hjson::Value::Array(a) => Value::List( a.iter() - .map(|x| convert_json_value_to_nu_value(x)) + .map(|x| convert_json_value_to_nu_value(x).spanned_unknown()) .collect(), ), serde_hjson::Value::Object(o) => { @@ -35,9 +35,9 @@ pub fn from_json(args: CommandArgs) -> Result { let span = args.name_span; Ok(out .values - .map(move |a| match a { + .map(move |a| match a.item { Value::Primitive(Primitive::String(s)) => match from_json_string_to_value(s) { - Ok(x) => ReturnSuccess::value(x), + Ok(x) => ReturnSuccess::value(x.spanned(a.span)), Err(_) => Err(ShellError::maybe_labeled_error( "Could not parse as JSON", "piped data failed JSON parse", diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index b50b09da39..a91c591726 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -10,7 +10,7 @@ fn convert_toml_value_to_nu_value(v: &toml::Value) -> Value { toml::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))), toml::Value::Array(a) => Value::List( a.iter() - .map(|x| convert_toml_value_to_nu_value(x)) + .map(|x| convert_toml_value_to_nu_value(x).spanned_unknown()) .collect(), ), toml::Value::Datetime(dt) => Value::Primitive(Primitive::String(dt.to_string())), @@ -34,9 +34,9 @@ pub fn from_toml(args: CommandArgs) -> Result { let span = args.name_span; Ok(out .values - .map(move |a| match a { + .map(move |a| match a.item { Value::Primitive(Primitive::String(s)) => match from_toml_string_to_value(s) { - Ok(x) => ReturnSuccess::value(x), + Ok(x) => ReturnSuccess::value(x.spanned(a.span)), Err(_) => Err(ShellError::maybe_labeled_error( "Could not parse as TOML", "piped data failed TOML parse", diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index a1b1a14d76..8a722ea3fc 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -10,7 +10,7 @@ fn from_node_to_value<'a, 'd>(n: &roxmltree::Node<'a, 'd>) -> Value { children_values.push(from_node_to_value(&c)); } - let children_values: Vec = children_values + let children_values: Vec> = children_values .into_iter() .filter(|x| match x { Value::Primitive(Primitive::String(f)) => { @@ -22,6 +22,7 @@ fn from_node_to_value<'a, 'd>(n: &roxmltree::Node<'a, 'd>) -> Value { } _ => true, }) + .map(|v| v.spanned_unknown()) .collect(); let mut collected = Dictionary::default(); @@ -53,9 +54,9 @@ pub fn from_xml(args: CommandArgs) -> Result { let span = args.name_span; Ok(out .values - .map(move |a| match a { + .map(move |a| match a.item { Value::Primitive(Primitive::String(s)) => match from_xml_string_to_value(s) { - Ok(x) => ReturnSuccess::value(x), + Ok(x) => ReturnSuccess::value(x.spanned(a.span)), Err(_) => Err(ShellError::maybe_labeled_error( "Could not parse as XML", "piped data failed XML parse", diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index 8647f79904..b4679fdb78 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -14,7 +14,7 @@ fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value) -> Value { serde_yaml::Value::String(s) => Value::string(s), serde_yaml::Value::Sequence(a) => Value::List( a.iter() - .map(|x| convert_yaml_value_to_nu_value(x)) + .map(|x| convert_yaml_value_to_nu_value(x).spanned_unknown()) .collect(), ), serde_yaml::Value::Mapping(t) => { @@ -44,9 +44,9 @@ pub fn from_yaml(args: CommandArgs) -> Result { let span = args.name_span; Ok(out .values - .map(move |a| match a { + .map(move |a| match a.item { Value::Primitive(Primitive::String(s)) => match from_yaml_string_to_value(s) { - Ok(x) => ReturnSuccess::value(x), + Ok(x) => ReturnSuccess::value(x.spanned(a.span)), Err(_) => Err(ShellError::maybe_labeled_error( "Could not parse as YAML", "piped data failed YAML parse", diff --git a/src/commands/get.rs b/src/commands/get.rs index f022f8e930..fea58ed0a9 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -3,7 +3,7 @@ use crate::object::Value; use crate::parser::Span; use crate::prelude::*; -fn get_member(path: &str, span: Span, obj: &Value) -> Result { +fn get_member(path: &str, span: Span, obj: &Spanned) -> Result, ShellError> { let mut current = obj; for p in path.split(".") { match current.get_data_by_key(p) { @@ -18,7 +18,7 @@ fn get_member(path: &str, span: Span, obj: &Value) -> Result } } - Ok(current.copy()) + Ok(current.clone()) } pub fn get(args: CommandArgs) -> Result { @@ -56,12 +56,15 @@ pub fn get(args: CommandArgs) -> Result { let mut result = VecDeque::new(); for field in &fields { match get_member(&field.0, field.1, &item) { - Ok(Value::List(l)) => { + Ok(Spanned { + item: Value::List(l), + .. + }) => { for item in l { - result.push_back(ReturnSuccess::value(item.copy())); + result.push_back(ReturnSuccess::value(item.clone())); } } - Ok(x) => result.push_back(ReturnSuccess::value(x.copy())), + Ok(x) => result.push_back(ReturnSuccess::value(x.clone())), Err(_) => {} } } diff --git a/src/commands/lines.rs b/src/commands/lines.rs index 7f1ae850f2..12cd0c020e 100644 --- a/src/commands/lines.rs +++ b/src/commands/lines.rs @@ -11,7 +11,7 @@ pub fn lines(args: CommandArgs) -> Result { let stream = input .values - .map(move |v| match v { + .map(move |v| match v.item { Value::Primitive(Primitive::String(s)) => { let split_result: Vec<_> = s.lines().filter(|s| s.trim() != "").collect(); @@ -19,9 +19,9 @@ pub fn lines(args: CommandArgs) -> Result { let mut result = VecDeque::new(); for s in split_result { - result.push_back(ReturnSuccess::value(Value::Primitive(Primitive::String( - s.into(), - )))); + result.push_back(ReturnSuccess::value( + Value::Primitive(Primitive::String(s.into())).spanned_unknown(), + )); } result } diff --git a/src/commands/ls.rs b/src/commands/ls.rs index e23080ae8d..8181e9cea3 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -18,7 +18,7 @@ pub fn ls(args: CommandArgs) -> Result { _ => {} } - match obj { + match obj.item { Value::Filesystem => { let entries = std::fs::read_dir(&full_path); @@ -45,7 +45,7 @@ pub fn ls(args: CommandArgs) -> Result { for entry in entries { let value = Value::Object(dir_entry_dict(&entry?)?); - shell_entries.push_back(ReturnSuccess::value(value)) + shell_entries.push_back(ReturnSuccess::value(value.spanned_unknown())) } Ok(shell_entries.to_output_stream()) } @@ -125,9 +125,12 @@ pub fn ls(args: CommandArgs) -> Result { } } match viewed { - Value::List(l) => { + Spanned { + item: Value::List(l), + .. + } => { for item in l { - entries.push_back(ReturnSuccess::value(item.copy())); + entries.push_back(ReturnSuccess::value(item.clone())); } } x => { diff --git a/src/commands/macros.rs b/src/commands/macros.rs index 5a47ffe0cc..5a4e9e2d14 100644 --- a/src/commands/macros.rs +++ b/src/commands/macros.rs @@ -49,7 +49,7 @@ macro_rules! command { positional: vec![$($mandatory_positional)*], rest_positional: false, named: { - use $crate::parser::registry::{NamedType, NamedValue}; + use $crate::parser::registry::NamedType; #[allow(unused_mut)] let mut named: indexmap::IndexMap = indexmap::IndexMap::new(); @@ -261,10 +261,10 @@ macro_rules! command { Extract { $($extract:tt)* { - use std::convert::TryInto; + use $crate::object::types::ExtractType; let value = $args.expect_nth($($positional_count)*)?; - let value = $param_kind.check(value)?; - value.extract() + // let value = $param_kind.check(value)?; + $param_kind::extract(value)? } } ); diff --git a/src/commands/open.rs b/src/commands/open.rs index 34a6490df0..72440bea79 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -21,7 +21,7 @@ command! { let full_path = PathBuf::from(cwd); - let (file_extension, contents) = match &args.expect_nth(0)?.item { + let (file_extension, contents, contents_span) = match &args.expect_nth(0)?.item { Value::Primitive(Primitive::String(s)) => fetch(&full_path, s, args.expect_nth(0)?.span)?, _ => { return Err(ShellError::labeled_error( @@ -64,6 +64,7 @@ command! { stream.push_back(ReturnSuccess::value(parse_as_value( file_extension, contents, + contents_span, span, )?)); @@ -75,7 +76,7 @@ pub fn fetch( cwd: &PathBuf, location: &str, span: Span, -) -> Result<(Option, String), ShellError> { +) -> Result<(Option, String, Span), ShellError> { let mut cwd = cwd.clone(); if location.starts_with("http:") || location.starts_with("https:") { let response = reqwest::get(location); @@ -106,7 +107,7 @@ pub fn fetch( None => path_extension, }; - Ok((extension, Value::string(s))) + Ok((extension, s, span)) } Err(_) => { return Err(ShellError::labeled_error( @@ -131,7 +132,8 @@ pub fn fetch( Ok(s) => Ok(( cwd.extension() .map(|name| name.to_string_lossy().to_string()), - Value::string(s), + s, + span, )), Err(_) => Ok((None, Value::Binary(bytes))), }, @@ -149,10 +151,12 @@ pub fn fetch( pub fn parse_as_value( extension: Option, contents: String, + contents_span: Span, name_span: Option, -) -> Result { +) -> Result, ShellError> { match extension { Some(x) if x == "toml" => crate::commands::from_toml::from_toml_string_to_value(contents) + .map(|c| c.spanned(contents_span)) .map_err(move |_| { ShellError::maybe_labeled_error( "Could not open as TOML", @@ -161,6 +165,7 @@ pub fn parse_as_value( ) }), Some(x) if x == "json" => crate::commands::from_json::from_json_string_to_value(contents) + .map(|c| c.spanned(contents_span)) .map_err(move |_| { ShellError::maybe_labeled_error( "Could not open as JSON", @@ -169,6 +174,7 @@ pub fn parse_as_value( ) }), Some(x) if x == "ini" => crate::commands::from_ini::from_ini_string_to_value(contents) + .map(|c| c.spanned(contents_span)) .map_err(move |_| { ShellError::maybe_labeled_error( "Could not open as INI", @@ -177,6 +183,7 @@ pub fn parse_as_value( ) }), Some(x) if x == "xml" => crate::commands::from_xml::from_xml_string_to_value(contents) + .map(|c| c.spanned(contents_span)) .map_err(move |_| { ShellError::maybe_labeled_error( "Could not open as XML", @@ -185,6 +192,7 @@ pub fn parse_as_value( ) }), Some(x) if x == "yml" => crate::commands::from_yaml::from_yaml_string_to_value(contents) + .map(|c| c.spanned(contents_span)) .map_err(move |_| { ShellError::maybe_labeled_error( "Could not open as YAML", @@ -193,6 +201,7 @@ pub fn parse_as_value( ) }), Some(x) if x == "yaml" => crate::commands::from_yaml::from_yaml_string_to_value(contents) + .map(|c| c.spanned(contents_span)) .map_err(move |_| { ShellError::maybe_labeled_error( "Could not open as YAML", @@ -200,6 +209,6 @@ pub fn parse_as_value( name_span, ) }), - _ => Ok(Value::string(contents)), + _ => Ok(Value::string(contents).spanned(contents_span)), } } diff --git a/src/commands/pick.rs b/src/commands/pick.rs index 0a1e209f20..e4b2c35b72 100644 --- a/src/commands/pick.rs +++ b/src/commands/pick.rs @@ -14,11 +14,11 @@ pub fn pick(args: CommandArgs) -> Result { let fields: Result, _> = args.positional_iter().map(|a| a.as_string()).collect(); let fields = fields?; + let input = args.input; - let objects = args - .input + let objects = input .values - .map(move |item| Value::Object(select_fields(&item, &fields))); + .map(move |value| Value::Object(select_fields(&value.item, &fields)).spanned(value.span)); Ok(objects.from_input_stream()) } diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 1ae9e989e3..06cd27a453 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -50,14 +50,17 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result> = VecDeque::new(); + eos.push_back(Value::Primitive(Primitive::EndOfStream).spanned_unknown()); let stream = args .input .chain(eos) .map(move |v| match v { - Value::Primitive(Primitive::EndOfStream) => { + Spanned { + item: Value::Primitive(Primitive::EndOfStream), + .. + } => { let stdin = child.stdin.as_mut().expect("Failed to open stdin"); let stdout = child.stdout.as_mut().expect("Failed to open stdout"); diff --git a/src/commands/ps.rs b/src/commands/ps.rs index 3a69618fe8..d2be9c0ddc 100644 --- a/src/commands/ps.rs +++ b/src/commands/ps.rs @@ -4,14 +4,14 @@ use crate::object::Value; use crate::prelude::*; use sysinfo::{RefreshKind, SystemExt}; -pub fn ps(_args: CommandArgs) -> Result { +pub fn ps(args: CommandArgs) -> Result { let mut system = sysinfo::System::new_with_specifics(RefreshKind::new().with_processes()); system.refresh_processes(); let list = system.get_process_list(); let list = list .into_iter() - .map(|(_, process)| Value::Object(process_dict(process))) + .map(|(item, process)| Value::Object(process_dict(process)).spanned(args.name_span)) .collect::>(); Ok(list.from_input_stream()) diff --git a/src/commands/reject.rs b/src/commands/reject.rs index ad8cd51f89..7fb0161c64 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -18,7 +18,7 @@ pub fn reject(args: CommandArgs) -> Result { let stream = args .input .values - .map(move |item| Value::Object(reject_fields(&item, &fields))); + .map(move |item| Value::Object(reject_fields(&item, &fields)).spanned(args.name_span)); Ok(stream.from_input_stream()) } diff --git a/src/commands/size.rs b/src/commands/size.rs index 094e48005d..041e8181a8 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -24,20 +24,20 @@ pub fn size(args: CommandArgs) -> Result { let mut contents = String::new(); - let mut list = VecDeque::new(); - for name in args.positional_iter() { - let name = name.as_string()?; + let mut list: VecDeque = VecDeque::new(); + for spanned_name in args.positional_iter() { + let name = spanned_name.as_string()?; let path = cwd.join(&name); let mut file = File::open(path)?; file.read_to_string(&mut contents)?; - list.push_back(count(&name, &contents)); + list.push_back(count(&name, &contents).spanned(spanned_name).into()); contents.clear(); } Ok(list.to_output_stream()) } -fn count(name: &str, contents: &str) -> ReturnValue { +fn count(name: &str, contents: &str) -> Value { let mut lines: i64 = 0; let mut words: i64 = 0; let mut chars: i64 = 0; @@ -69,5 +69,5 @@ fn count(name: &str, contents: &str) -> ReturnValue { dict.add("chars", Value::int(chars)); dict.add("max length", Value::int(bytes)); - ReturnSuccess::value(Value::Object(dict)) + Value::Object(dict) } diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index 26d22372bc..e7e69d6da9 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -11,8 +11,8 @@ pub fn sort_by(args: CommandArgs) -> Result { vec.sort_by_key(|item| { fields .iter() - .map(|f| item.get_data_by_key(f).map(|i| i.copy())) - .collect::>>() + .map(|f| item.get_data_by_key(f).map(|i| i.clone())) + .collect::>>>() }); vec.into_iter().collect::>() diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index 0a1cac8142..037e34b7cb 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -21,7 +21,7 @@ pub fn split_column(args: CommandArgs) -> Result { Ok(input .values - .map(move |v| match v { + .map(move |v| match v.item { Value::Primitive(Primitive::String(s)) => { let splitter = positional[0].as_string().unwrap().replace("\\n", "\n"); trace!("splitting with {:?}", splitter); @@ -40,7 +40,7 @@ pub fn split_column(args: CommandArgs) -> Result { for (&k, v) in split_result.iter().zip(gen_columns.iter()) { dict.add(v.clone(), Value::Primitive(Primitive::String(k.into()))); } - ReturnSuccess::value(Value::Object(dict)) + ReturnSuccess::value(Value::Object(dict).spanned(v.span)) } else if split_result.len() == (positional.len() - 1) { let mut dict = crate::object::Dictionary::default(); for (&k, v) in split_result.iter().zip(positional.iter().skip(1)) { @@ -49,7 +49,7 @@ pub fn split_column(args: CommandArgs) -> Result { Value::Primitive(Primitive::String(k.into())), ); } - ReturnSuccess::value(Value::Object(dict)) + ReturnSuccess::value(Value::Object(dict).spanned(v.span)) } else { let mut dict = crate::object::Dictionary::default(); for k in positional.iter().skip(1) { @@ -58,7 +58,7 @@ pub fn split_column(args: CommandArgs) -> Result { Value::Primitive(Primitive::String("".into())), ); } - ReturnSuccess::value(Value::Object(dict)) + ReturnSuccess::value(Value::Object(dict).spanned(v.span)) } } _ => Err(ShellError::maybe_labeled_error( diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index 380c9ca193..b65f13ba71 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -22,7 +22,7 @@ pub fn split_row(args: CommandArgs) -> Result { let stream = input .values - .map(move |v| match v { + .map(move |v| match v.item { Value::Primitive(Primitive::String(s)) => { let splitter = positional[0].as_string().unwrap().replace("\\n", "\n"); trace!("splitting with {:?}", splitter); @@ -32,9 +32,9 @@ pub fn split_row(args: CommandArgs) -> Result { let mut result = VecDeque::new(); for s in split_result { - result.push_back(ReturnSuccess::value(Value::Primitive(Primitive::String( - s.into(), - )))); + result.push_back(ReturnSuccess::value( + Value::Primitive(Primitive::String(s.into())).spanned(v.span), + )); } result } diff --git a/src/commands/sysinfo.rs b/src/commands/sysinfo.rs index e6b60840ff..3a7cd5646c 100644 --- a/src/commands/sysinfo.rs +++ b/src/commands/sysinfo.rs @@ -1,95 +1,53 @@ use crate::errors::ShellError; use crate::object::base::OF64; -use crate::object::Dictionary; +use crate::object::SpannedDictBuilder; use crate::object::{Primitive, Value}; use crate::prelude::*; +use log::trace; use sys_info::*; use sysinfo::{ComponentExt, DiskExt, NetworkExt, RefreshKind, SystemExt}; -pub fn sysinfo(_args: CommandArgs) -> Result { - let mut idx = indexmap::IndexMap::new(); +pub fn sysinfo(args: CommandArgs) -> Result { + let mut idx = SpannedDictBuilder::new(args.name_span); if let (Ok(name), Ok(version)) = (os_type(), os_release()) { - let mut os_idx = indexmap::IndexMap::new(); - os_idx.insert( - "name".to_string(), - Value::Primitive(Primitive::String(name)), - ); - os_idx.insert( - "version".to_string(), - Value::Primitive(Primitive::String(version)), - ); + let mut os_idx = SpannedDictBuilder::new(args.name_span); + os_idx.insert("name", Primitive::String(name)); + os_idx.insert("version", Primitive::String(version)); - idx.insert("os".to_string(), Value::Object(Dictionary::from(os_idx))); + idx.insert_spanned("os", os_idx.into_spanned_value()); } if let (Ok(num_cpu), Ok(cpu_speed)) = (cpu_num(), cpu_speed()) { - let mut cpu_idx = indexmap::IndexMap::new(); - cpu_idx.insert( - "num".to_string(), - Value::Primitive(Primitive::Int(num_cpu as i64)), - ); - cpu_idx.insert( - "speed".to_string(), - Value::Primitive(Primitive::Int(cpu_speed as i64)), - ); + let mut cpu_idx = SpannedDictBuilder::new(args.name_span); + cpu_idx.insert("num", Primitive::Int(num_cpu as i64)); + cpu_idx.insert("speed", Primitive::Int(cpu_speed as i64)); - idx.insert("cpu".to_string(), Value::Object(Dictionary::from(cpu_idx))); + idx.insert_spanned("cpu", cpu_idx); } if let Ok(x) = loadavg() { - let mut load_idx = indexmap::IndexMap::new(); - load_idx.insert( - "1min".to_string(), - Value::Primitive(Primitive::Float(OF64::from(x.one))), - ); - load_idx.insert( - "5min".to_string(), - Value::Primitive(Primitive::Float(OF64::from(x.five))), - ); - load_idx.insert( - "15min".to_string(), - Value::Primitive(Primitive::Float(OF64::from(x.fifteen))), - ); + let mut load_idx = SpannedDictBuilder::new(args.name_span); - idx.insert( - "load avg".to_string(), - Value::Object(Dictionary::from(load_idx)), - ); + load_idx.insert("1min", Primitive::Float(OF64::from(x.one))); + load_idx.insert("5min", Primitive::Float(OF64::from(x.five))); + load_idx.insert("15min", Primitive::Float(OF64::from(x.fifteen))); + + idx.insert_spanned("load avg", load_idx); } if let Ok(x) = mem_info() { - let mut mem_idx = indexmap::IndexMap::new(); - mem_idx.insert( - "total".to_string(), - Value::Primitive(Primitive::Bytes(x.total as u64 * 1024)), - ); - mem_idx.insert( - "free".to_string(), - Value::Primitive(Primitive::Bytes(x.free as u64 * 1024)), - ); - mem_idx.insert( - "avail".to_string(), - Value::Primitive(Primitive::Bytes(x.avail as u64 * 1024)), - ); - mem_idx.insert( - "buffers".to_string(), - Value::Primitive(Primitive::Bytes(x.buffers as u64 * 1024)), - ); - mem_idx.insert( - "cached".to_string(), - Value::Primitive(Primitive::Bytes(x.cached as u64 * 1024)), - ); - mem_idx.insert( - "swap total".to_string(), - Value::Primitive(Primitive::Bytes(x.swap_total as u64 * 1024)), - ); - mem_idx.insert( - "swap free".to_string(), - Value::Primitive(Primitive::Bytes(x.swap_free as u64 * 1024)), - ); + let mut mem_idx = SpannedDictBuilder::new(args.name_span); - idx.insert("mem".to_string(), Value::Object(Dictionary::from(mem_idx))); + mem_idx.insert("total", Primitive::Bytes(x.total as u64 * 1024)); + mem_idx.insert("free", Primitive::Bytes(x.free as u64 * 1024)); + mem_idx.insert("avail", Primitive::Bytes(x.avail as u64 * 1024)); + mem_idx.insert("buffers", Primitive::Bytes(x.buffers as u64 * 1024)); + mem_idx.insert("cached", Primitive::Bytes(x.cached as u64 * 1024)); + mem_idx.insert("swap total", Primitive::Bytes(x.swap_total as u64 * 1024)); + mem_idx.insert("swap free", Primitive::Bytes(x.swap_free as u64 * 1024)); + + idx.insert_spanned("mem", mem_idx); } /* @@ -107,57 +65,42 @@ pub fn sysinfo(_args: CommandArgs) -> Result { */ if let Ok(x) = hostname() { - idx.insert( - "hostname".to_string(), - Value::Primitive(Primitive::String(x)), - ); + idx.insert("hostname", Primitive::String(x)); } #[cfg(not(windows))] { if let Ok(x) = boottime() { - let mut boottime_idx = indexmap::IndexMap::new(); - boottime_idx.insert( - "days".to_string(), - Value::Primitive(Primitive::Int(x.tv_sec / (24 * 3600))), - ); - boottime_idx.insert( - "hours".to_string(), - Value::Primitive(Primitive::Int((x.tv_sec / 3600) % 24)), - ); - boottime_idx.insert( - "mins".to_string(), - Value::Primitive(Primitive::Int((x.tv_sec / 60) % 60)), - ); + let mut boottime_idx = SpannedDictBuilder::new(args.name_span); + boottime_idx.insert("days", Primitive::Int(x.tv_sec / (24 * 3600))); + boottime_idx.insert("hours", Primitive::Int((x.tv_sec / 3600) % 24)); + boottime_idx.insert("mins", Primitive::Int((x.tv_sec / 60) % 60)); - idx.insert( - "uptime".to_string(), - Value::Object(Dictionary::from(boottime_idx)), - ); + idx.insert("uptime", boottime_idx); } } let system = sysinfo::System::new_with_specifics(RefreshKind::everything().without_processes()); let components_list = system.get_components_list(); if components_list.len() > 0 { - let mut v = vec![]; + let mut v: Vec> = vec![]; for component in components_list { - let mut component_idx = indexmap::IndexMap::new(); + let mut component_idx = SpannedDictBuilder::new(args.name_span); + component_idx.insert("name", Primitive::String(component.get_label().to_string())); component_idx.insert( - "name".to_string(), - Value::string(component.get_label().to_string()), + "temp", + Primitive::Float(OF64::from(component.get_temperature() as f64)), ); component_idx.insert( - "temp".to_string(), - Value::float(component.get_temperature() as f64), + "max", + Primitive::Float(OF64::from(component.get_max() as f64)), ); - component_idx.insert("max".to_string(), Value::float(component.get_max() as f64)); if let Some(critical) = component.get_critical() { - component_idx.insert("critical".to_string(), Value::float(critical as f64)); + component_idx.insert("critical", Primitive::Float(OF64::from(critical as f64))); } - v.push(Value::Object(Dictionary::from(component_idx))); + v.push(component_idx.into()); } - idx.insert("temps".to_string(), Value::List(v)); + idx.insert("temps", Value::List(v)); } let disks = system.get_disks(); @@ -165,38 +108,26 @@ pub fn sysinfo(_args: CommandArgs) -> Result { let mut v = vec![]; for disk in disks { - let mut disk_idx = indexmap::IndexMap::new(); - disk_idx.insert( - "name".to_string(), - Value::string(disk.get_name().to_string_lossy()), - ); - disk_idx.insert( - "available".to_string(), - Value::bytes(disk.get_available_space()), - ); - disk_idx.insert("total".to_string(), Value::bytes(disk.get_total_space())); - v.push(Value::Object(Dictionary::from(disk_idx))); + let mut disk_idx = SpannedDictBuilder::new(args.name_span); + disk_idx.insert("name", Value::string(disk.get_name().to_string_lossy())); + disk_idx.insert("available", Value::bytes(disk.get_available_space())); + disk_idx.insert("total", Value::bytes(disk.get_total_space())); + v.push(disk_idx.into()); } - idx.insert("disks".to_string(), Value::List(v)); + idx.insert("disks", Value::List(v)); } let network = system.get_network(); let incoming = network.get_income(); let outgoing = network.get_outcome(); - let mut network_idx = indexmap::IndexMap::new(); - network_idx.insert("incoming".to_string(), Value::bytes(incoming)); - network_idx.insert("outgoing".to_string(), Value::bytes(outgoing)); - idx.insert( - "network".to_string(), - Value::Object(Dictionary::from(network_idx)), - ); + let mut network_idx = SpannedDictBuilder::new(args.name_span); + network_idx.insert("incoming", Value::bytes(incoming)); + network_idx.insert("outgoing", Value::bytes(outgoing)); + idx.insert_spanned("network", network_idx); - // println!("{:#?}", system.get_network()); - - let mut stream = VecDeque::new(); - stream.push_back(Value::Object(Dictionary::from(idx))); + let mut stream = stream![idx.into_spanned_value()]; Ok(stream.from_input_stream()) } diff --git a/src/commands/to_array.rs b/src/commands/to_array.rs index 9e2f4c33d9..7cea62dbe8 100644 --- a/src/commands/to_array.rs +++ b/src/commands/to_array.rs @@ -5,7 +5,7 @@ pub fn to_array(args: CommandArgs) -> Result { let out = args.input.values.collect(); Ok(out - .map(|vec: Vec<_>| stream![Value::List(vec)]) + .map(|vec: Vec<_>| stream![Value::List(vec).spanned_unknown()]) // TODO: args.input should have a span .flatten_stream() .from_input_stream()) } diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 2244604929..8563761b2f 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -45,15 +45,16 @@ pub fn to_json(args: CommandArgs) -> Result { let out = args.input; let span = args.name_span; Ok(out - .map( - move |a| match serde_json::to_string(&value_to_json_value(&a)) { - Ok(x) => Ok(ReturnValue::Value(Value::Primitive(Primitive::String(x)))), - Err(_) => Err(ShellError::maybe_labeled_error( - "Can not convert to JSON string", - "can not convert piped data to JSON string", - span, - )), - }, - ) - .boxed()) + .values + .map(move |a| match serde_json::to_string(&a) { + Ok(x) => { + ReturnSuccess::value(Value::Primitive(Primitive::String(x)).spanned(args.name_span)) + } + Err(_) => Err(ShellError::maybe_labeled_error( + "Can not convert to JSON string", + "can not convert piped data to JSON string", + span, + )), + }) + .to_output_stream()) } diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index ca3edcf936..1c6543bc70 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -37,7 +37,9 @@ pub fn to_toml(args: CommandArgs) -> Result { Ok(out .values .map(move |a| match toml::to_string(&a) { - Ok(x) => ReturnSuccess::value(Value::Primitive(Primitive::String(x))), + Ok(x) => { + ReturnSuccess::value(Value::Primitive(Primitive::String(x)).spanned(args.name_span)) + } Err(_) => Err(ShellError::maybe_labeled_error( "Can not convert to TOML string", "can not convert piped data to TOML string", diff --git a/src/commands/trim.rs b/src/commands/trim.rs index 92466d9f34..79b4acbedd 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -10,15 +10,14 @@ pub fn trim(args: CommandArgs) -> Result { Ok(input .values - .map(move |v| match v { - Value::Primitive(Primitive::String(s)) => { - ReturnSuccess::value(Value::Primitive(Primitive::String(s.trim().into()))) - } - _ => Err(ShellError::maybe_labeled_error( - "Expected string values from pipeline", - "expects strings from pipeline", - span, - )), - }) + .map(move |v| ReturnSuccess::value(String::check(&v)?.clone())) + // Value::Primitive(Primitive::String(s)) => { + // ReturnSuccess::value(Value::Primitive(Primitive::String(s.trim().into()))) + // } + // _ => Err(ShellError::maybe_labeled_error( + // "Expected string values from pipeline", + // "expects strings from pipeline", + // span, + // )), .to_output_stream()) } diff --git a/src/commands/where_.rs b/src/commands/where_.rs index 9ea08b4f11..d5d9f6bb61 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,5 +1,5 @@ use crate::errors::ShellError; -use crate::object::types::*; +use crate::object::Block; use crate::prelude::*; use futures::future::ready; use log::trace; @@ -13,7 +13,7 @@ command! { let return_value = match result { Err(err) => Some(Err(err)), - Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.copy()))), + Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), _ => None, }; diff --git a/src/context.rs b/src/context.rs index aee27d17e9..f0b8dca629 100644 --- a/src/context.rs +++ b/src/context.rs @@ -54,7 +54,7 @@ impl Context { command: Arc, name_span: Option, args: Args, - input: Vec, + input: Vec>, ) -> Result<(), ShellError> { let command_args = SinkCommandArgs { ctx: self.clone(), diff --git a/src/env/environment.rs b/src/env/environment.rs index 9b0875e651..0590cda68f 100644 --- a/src/env/environment.rs +++ b/src/env/environment.rs @@ -1,9 +1,10 @@ use crate::object::base::Value; +use crate::prelude::*; use std::path::{Path, PathBuf}; #[derive(Debug, Clone)] pub struct Environment { - crate obj: Value, + crate obj: Spanned, crate path: PathBuf, } @@ -12,7 +13,7 @@ impl Environment { let path = std::env::current_dir()?; Ok(Environment { - obj: Value::Filesystem, + obj: Value::Filesystem.spanned_unknown(), path, }) } @@ -21,7 +22,7 @@ impl Environment { self.path.as_path() } - pub fn obj(&self) -> &Value { + pub fn obj(&self) -> &Spanned { &self.obj } } diff --git a/src/errors.rs b/src/errors.rs index a87edd5ce8..d78719f50a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -48,7 +48,7 @@ pub fn labelled( ) -> impl FnOnce(ShellError) -> ShellError + 'a { let span = span.into(); - move |error| ShellError::maybe_labeled_error(heading, span_message, span) + move |_| ShellError::maybe_labeled_error(heading, span_message, span) } #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] @@ -247,10 +247,6 @@ impl ShellError { crate fn unexpected(title: impl Into) -> ShellError { ShellError::string(&format!("Unexpected: {}", title.into())) } - - crate fn copy_error(&self) -> ShellError { - self.clone() - } } #[derive(Debug, Clone)] diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 38f9012769..0fcccee768 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -10,15 +10,15 @@ use indexmap::IndexMap; #[derive(new)] crate struct Scope { - it: Value, + it: Spanned, #[new(default)] - vars: IndexMap, + vars: IndexMap>, } impl Scope { crate fn empty() -> Scope { Scope { - it: Value::nothing(), + it: Value::nothing().spanned_unknown(), vars: IndexMap::new(), } } @@ -64,8 +64,10 @@ crate fn evaluate_baseline_expr( }) } Some(next) => { - item = - Spanned::from_item(next.clone(), (expr.span().start, name.span().end)) + item = Spanned::from_item( + next.clone().item, + (expr.span().start, name.span().end), + ) } }; } @@ -93,14 +95,11 @@ fn evaluate_reference( source: &Text, ) -> Result, ShellError> { match name { - hir::Variable::It(span) => Ok(Spanned::from_item(scope.it.copy(), span)), - hir::Variable::Other(span) => Ok(Spanned::from_item( - scope - .vars - .get(span.slice(source)) - .map(|v| v.copy()) - .unwrap_or_else(|| Value::nothing()), - span, - )), + hir::Variable::It(span) => Ok(Spanned::from_item(scope.it.item, span)), + hir::Variable::Other(span) => Ok(scope + .vars + .get(span.slice(source)) + .map(|v| v.clone()) + .unwrap_or_else(|| Value::nothing().spanned(span))), } } diff --git a/src/format/table.rs b/src/format/table.rs index 6b2205c3f3..0140ab70bd 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -13,7 +13,7 @@ pub struct TableView { } impl TableView { - fn merge_descriptors(values: &[Value]) -> Vec { + fn merge_descriptors(values: &[Spanned]) -> Vec { let mut ret = vec![]; for value in values { for desc in value.data_descriptors() { @@ -25,7 +25,7 @@ impl TableView { ret } - pub fn from_list(values: &[Value]) -> Option { + pub fn from_list(values: &[Spanned]) -> Option { if values.len() == 0 { return None; } diff --git a/src/format/vtable.rs b/src/format/vtable.rs index 9e658f5c84..ac27f3bc83 100644 --- a/src/format/vtable.rs +++ b/src/format/vtable.rs @@ -12,7 +12,7 @@ pub struct VTableView { } impl VTableView { - pub fn from_list(values: &[Value]) -> Option { + pub fn from_list(values: &[Spanned]) -> Option { if values.len() == 0 { return None; } diff --git a/src/object.rs b/src/object.rs index 7e8be20e09..f094c63a17 100644 --- a/src/object.rs +++ b/src/object.rs @@ -2,9 +2,10 @@ crate mod base; crate mod config; crate mod dict; crate mod files; +crate mod into; crate mod process; crate mod types; -crate use base::{Primitive, Value}; -crate use dict::Dictionary; +crate use base::{Block, Primitive, Switch, Value}; +crate use dict::{Dictionary, SpannedDictBuilder}; crate use files::dir_entry_dict; diff --git a/src/object/base.rs b/src/object/base.rs index 54693314a5..73f144ea08 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -153,8 +153,8 @@ impl Deserialize<'de> for Block { } impl Block { - pub fn invoke(&self, value: &Value) -> Result, ShellError> { - let scope = Scope::new(value.copy()); + pub fn invoke(&self, value: &Spanned) -> Result, ShellError> { + let scope = Scope::new(value.clone()); if self.expressions.len() == 0 { return Ok(Spanned::from_item(Value::nothing(), self.span)); @@ -174,20 +174,19 @@ impl Block { pub enum Value { Primitive(Primitive), Object(crate::object::Dictionary), - List(Vec), Binary(Vec), - + List(Vec>), #[allow(unused)] Block(Block), Filesystem, } -pub fn debug_list(values: &'a Vec) -> ValuesDebug<'a> { +pub fn debug_list(values: &'a Vec>) -> ValuesDebug<'a> { ValuesDebug { values } } pub struct ValuesDebug<'a> { - values: &'a Vec, + values: &'a Vec>, } impl fmt::Debug for ValuesDebug<'a> { @@ -199,12 +198,12 @@ impl fmt::Debug for ValuesDebug<'a> { } pub struct ValueDebug<'a> { - value: &'a Value, + value: &'a Spanned, } impl fmt::Debug for ValueDebug<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.value { + match self.value.item() { Value::Primitive(p) => p.debug(f), Value::Object(o) => o.debug(f), Value::List(l) => debug_list(l).fmt(f), @@ -281,6 +280,12 @@ impl std::convert::TryFrom>> for Switch { } } +impl Spanned { + crate fn debug(&'a self) -> ValueDebug<'a> { + ValueDebug { value: self } + } +} + impl Value { crate fn type_name(&self) -> String { match self { @@ -314,13 +319,16 @@ impl Value { } } - crate fn get_data_by_key(&'a self, name: &str) -> Option<&Value> { + crate fn get_data_by_key(&'a self, name: &str) -> Option<&Spanned> { match self { Value::Object(o) => o.get_data_by_key(name), Value::List(l) => { for item in l { match item { - Value::Object(o) => match o.get_data_by_key(name) { + Spanned { + item: Value::Object(o), + .. + } => match o.get_data_by_key(name) { Some(v) => return Some(v), None => {} }, @@ -333,7 +341,7 @@ impl Value { } } - crate fn get_data_by_index(&'a self, idx: usize) -> Option<&Value> { + crate fn get_data_by_index(&'a self, idx: usize) -> Option<&Spanned> { match self { Value::List(l) => l.iter().nth(idx), _ => None, @@ -352,20 +360,6 @@ impl Value { } } - crate fn copy(&self) -> Value { - match self { - Value::Primitive(p) => Value::Primitive(p.clone()), - Value::Object(o) => Value::Object(o.copy_dict()), - Value::Block(b) => Value::Block(b.clone()), - Value::List(l) => { - let list = l.iter().map(|i| i.copy()).collect(); - Value::List(list) - } - Value::Filesystem => Value::Filesystem, - Value::Binary(b) => Value::Binary(b.clone()), - } - } - crate fn format_leaf(&self, desc: Option<&String>) -> String { match self { Value::Primitive(p) => p.format(desc), @@ -417,7 +411,7 @@ impl Value { } } - crate fn as_pair(&self) -> Result<(Value, Value), ShellError> { + crate fn as_pair(&self) -> Result<(Spanned, Spanned), ShellError> { match self { Value::List(list) if list.len() == 2 => Ok((list[0].clone(), list[1].clone())), other => Err(ShellError::string(format!( @@ -509,11 +503,6 @@ impl Value { pub fn nothing() -> Value { Value::Primitive(Primitive::Nothing) } - - #[allow(unused)] - pub fn list(values: impl Into>) -> Value { - Value::List(values.into()) - } } crate fn select_fields(obj: &Value, fields: &[String]) -> crate::object::Dictionary { @@ -522,9 +511,9 @@ crate fn select_fields(obj: &Value, fields: &[String]) -> crate::object::Diction let descs = obj.data_descriptors(); for field in fields { - match descs.iter().find(|d| *d == field) { + match descs.iter().find(|d| d.name.is_string(field)) { None => out.add(field, Value::nothing()), - Some(desc) => out.add(desc.clone(), obj.get_data(desc).borrow().copy()), + Some(desc) => out.add(desc.clone(), obj.get_data(desc).borrow().clone()), } } @@ -539,7 +528,7 @@ crate fn reject_fields(obj: &Value, fields: &[String]) -> crate::object::Diction for desc in descs { match desc { x if fields.iter().any(|field| *field == x) => continue, - _ => out.add(desc.clone(), obj.get_data(&desc).borrow().copy()), + _ => out.add(desc.clone(), obj.get_data(&desc).borrow().clone()), } } @@ -552,7 +541,7 @@ crate fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool { match descs.iter().find(|d| *d == field) { None => false, Some(desc) => { - let v = obj.get_data(desc).borrow().copy(); + let v = obj.get_data(desc).borrow().clone(); match v { Value::Primitive(Primitive::Boolean(b)) => match (op, rhs) { diff --git a/src/object/config.rs b/src/object/config.rs index 288ed21328..f11fef027d 100644 --- a/src/object/config.rs +++ b/src/object/config.rs @@ -16,24 +16,31 @@ const APP_INFO: AppInfo = AppInfo { #[derive(Deserialize, Serialize)] struct Config { #[serde(flatten)] - extra: IndexMap, + extra: IndexMap>, } -crate fn write_config(map: &IndexMap) -> Result<(), ShellError> { +crate fn write_config(config: &IndexMap>) -> Result<(), ShellError> { let location = app_root(AppDataType::UserConfig, &APP_INFO) .map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?; let filename = location.join("config.toml"); touch(&filename)?; - let contents = toml::to_string(&Config { extra: map.clone() })?; + let contents = toml::to_string(&Config { + extra: config + .iter() + .map(|(k, v)| (k.clone(), v.item.clone())) + .collect(), + })?; fs::write(&filename, &contents)?; Ok(()) } -crate fn config() -> Result, ShellError> { +crate fn config(span: impl Into) -> Result>, ShellError> { + let span = span.into(); + let location = app_root(AppDataType::UserConfig, &APP_INFO) .map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?; @@ -43,9 +50,11 @@ crate fn config() -> Result, ShellError> { trace!("config file = {}", filename.display()); let contents = fs::read_to_string(filename) + .map(|v| v.spanned(span)) .map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?; let parsed: Config = toml::from_str(&contents) + .map(|v| v.spanned(span)) .map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?; Ok(parsed.extra) diff --git a/src/object/dict.rs b/src/object/dict.rs index 9c0e4ed34f..c8f9965dac 100644 --- a/src/object/dict.rs +++ b/src/object/dict.rs @@ -9,7 +9,7 @@ use std::fmt; #[derive(Debug, Default, Eq, PartialEq, Serialize, Deserialize, Clone, new)] pub struct Dictionary { - pub entries: IndexMap, + pub entries: IndexMap>, } impl PartialOrd for Dictionary { @@ -28,8 +28,8 @@ impl PartialOrd for Dictionary { } } -impl From> for Dictionary { - fn from(input: IndexMap) -> Dictionary { +impl From>> for Dictionary { + fn from(input: IndexMap>) -> Dictionary { let mut out = IndexMap::default(); for (key, value) in input { @@ -93,7 +93,7 @@ impl Dictionary { } } - crate fn get_data_by_key(&self, name: &str) -> Option<&Value> { + crate fn get_data_by_key(&self, name: &str) -> Option<&Spanned> { match self .entries .iter() @@ -114,3 +114,41 @@ impl Dictionary { debug.finish() } } + +#[derive(Debug)] +pub struct SpannedDictBuilder { + span: Span, + dict: IndexMap>, +} + +impl SpannedDictBuilder { + pub fn new(span: impl Into) -> SpannedDictBuilder { + SpannedDictBuilder { + span: span.into(), + dict: IndexMap::default(), + } + } + + pub fn insert(&mut self, key: impl Into, value: impl Into) { + self.dict + .insert(key.into(), value.into().spanned(self.span)); + } + + pub fn insert_spanned( + &mut self, + key: impl Into, + value: impl Into>, + ) { + self.dict.insert(key.into(), value.into()); + } + + pub fn into_spanned_value(self) -> Spanned { + Value::Object(Dictionary { entries: self.dict }).spanned(self.span) + } +} + +impl From for Spanned { + fn from(input: SpannedDictBuilder) -> Spanned { + input.into_spanned_value() + } +} diff --git a/src/object/into.rs b/src/object/into.rs new file mode 100644 index 0000000000..dceb5192d1 --- /dev/null +++ b/src/object/into.rs @@ -0,0 +1,23 @@ +use crate::object::{Primitive, Value}; +use crate::prelude::*; + +impl From for Value { + fn from(input: Primitive) -> Value { + Value::Primitive(input) + } +} + +impl From for Value { + fn from(input: String) -> Value { + Value::Primitive(Primitive::String(input)) + } +} + +impl> Spanned { + pub fn into_spanned_value(self) -> Spanned { + let Spanned { item, span } = self; + + let value = item.into(); + value.spanned(span) + } +} diff --git a/src/object/types.rs b/src/object/types.rs index 3d026772e3..46e52548e1 100644 --- a/src/object/types.rs +++ b/src/object/types.rs @@ -2,29 +2,44 @@ use crate::object::base as value; use crate::parser::hir; use crate::prelude::*; use derive_new::new; -use serde_derive::{Deserialize, Serialize}; +use serde_derive::Deserialize; pub trait Type: std::fmt::Debug + Send { + type Extractor: ExtractType; + fn name(&self) -> &'static str; - fn check(&self, value: Spanned) -> Result, ShellError>; + fn check(&self, value: &'value Spanned) -> Result<&'value Spanned, ShellError>; fn coerce(&self) -> Option { None } } -pub trait ExtractType: Type { - fn extract(value: Value) -> T; +pub trait ExtractType: Sized { + fn extract(value: &Spanned) -> Result; + fn check(value: &'value Spanned) -> Result<&'value Spanned, ShellError>; } #[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)] pub struct Any; impl Type for Any { + type Extractor = Spanned; + fn name(&self) -> &'static str { "Any" } - fn check(&self, value: Spanned) -> Result, ShellError> { + fn check(&self, value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + Ok(value) + } +} + +impl ExtractType for Spanned { + fn extract(value: &Spanned) -> Result { + Ok(value.clone()) + } + + fn check(&self, value: &'value Spanned) -> Result<&'value Spanned, ShellError> { Ok(value) } } @@ -33,11 +48,13 @@ impl Type for Any { pub struct Integer; impl Type for Integer { + type Extractor = i64; + fn name(&self) -> &'static str { "Integer" } - fn check(&self, value: Spanned) -> Result, ShellError> { + fn check(&self, value: &'value Spanned) -> Result<&'value Spanned, ShellError> { match value { v @ Spanned { item: Value::Primitive(Primitive::Int(_)), @@ -48,11 +65,67 @@ impl Type for Integer { } } -impl ExtractType for Integer { - fn extract(value: Value) -> i64 { +impl ExtractType for i64 { + fn extract(value: &Spanned) -> Result { match value { - Value::Primitive(Primitive::Int(int)) => int, - _ => unreachable!("invariant: must check before extract"), + &Spanned { + item: Value::Primitive(Primitive::Int(int)), + .. + } => Ok(int), + other => Err(ShellError::type_error("Integer", other.spanned_type_name())), + } + } + + fn check(&self, value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + match value { + v @ Spanned { + item: Value::Primitive(Primitive::Int(_)), + .. + } => Ok(v), + other => Err(ShellError::type_error("Integer", other.spanned_type_name())), + } + } +} + +#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)] +pub struct NuString; + +impl Type for NuString { + type Extractor = String; + + fn name(&self) -> &'static str { + "Integer" + } + + fn check(&self, value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + match value { + v @ Spanned { + item: Value::Primitive(Primitive::Int(_)), + .. + } => Ok(v), + other => Err(ShellError::type_error("Integer", other.spanned_type_name())), + } + } +} + +impl ExtractType for String { + fn extract(value: &Spanned) -> Result { + match value { + Spanned { + item: Value::Primitive(Primitive::String(string)), + .. + } => Ok(string.clone()), + other => Err(ShellError::type_error("String", other.spanned_type_name())), + } + } + + fn check(&self, value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + match value { + v @ Spanned { + item: Value::Primitive(Primitive::Int(_)), + .. + } => Ok(v), + other => Err(ShellError::type_error("Integer", other.spanned_type_name())), } } } @@ -61,11 +134,13 @@ impl ExtractType for Integer { pub struct Block; impl Type for Block { + type Extractor = value::Block; + fn name(&self) -> &'static str { "Block" } - fn check(&self, value: Spanned) -> Result, ShellError> { + fn check(&self, value: &'value Spanned) -> Result<&'value Spanned, ShellError> { match value { v @ Spanned { item: Value::Block(_), @@ -76,11 +151,24 @@ impl Type for Block { } } -impl ExtractType for Block { - fn extract(value: Value) -> value::Block { +impl ExtractType for value::Block { + fn extract(value: &Spanned) -> Result { match value { - Value::Block(block) => block, - _ => unreachable!("invariant: must check before extract"), + Spanned { + item: Value::Block(block), + .. + } => Ok(block.clone()), + other => Err(ShellError::type_error("Block", other.spanned_type_name())), + } + } + + fn check(&self, value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + match value { + v @ Spanned { + item: Value::Block(_), + .. + } => Ok(v), + other => Err(ShellError::type_error("Block", other.spanned_type_name())), } } } diff --git a/src/parser.rs b/src/parser.rs index 5b7d7fcbfc..e9a6ce1ac4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,7 +12,7 @@ crate use parse::flag::Flag; crate use parse::operator::Operator; crate use parse::parser::{nom_input, pipeline}; crate use parse::pipeline::{Pipeline, PipelineElement}; -pub use parse::span::{Span, Spanned}; +pub use parse::span::{Span, Spanned, SpannedItem}; crate use parse::text::Text; crate use parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode}; crate use parse::tokens::{RawToken, Token}; diff --git a/src/parser/parse/span.rs b/src/parser/parse/span.rs index 4d3f38659a..7ed86e6a12 100644 --- a/src/parser/parse/span.rs +++ b/src/parser/parse/span.rs @@ -16,6 +16,13 @@ pub trait SpannedItem: Sized { fn spanned(self, span: impl Into) -> Spanned { Spanned::from_item(self, span.into()) } + + // For now, this is a temporary facility. In many cases, there are other useful spans that we + // could be using, such as the original source spans of JSON or Toml files, but we don't yet + // have the infrastructure to make that work. + fn spanned_unknown(self) -> Spanned { + Spanned::from_item(self, (0, 0)) + } } impl SpannedItem for T {} @@ -64,6 +71,15 @@ pub struct Span { // source: &'source str, } +impl From> for Span { + fn from(input: Option) -> Span { + match input { + None => Span { start: 0, end: 0 }, + Some(span) => span, + } + } +} + impl From<&Spanned> for Span { fn from(input: &Spanned) -> Span { input.span @@ -113,6 +129,14 @@ impl From<&std::ops::Range> for Span { } impl Span { + pub fn unknown() -> Span { + Span { start: 0, end: 0 } + } + + pub fn is_unknown(&self) -> bool { + self.start == 0 && self.end == 0 + } + pub fn slice(&self, source: &'a str) -> &'a str { &source[self.start..self.end] } diff --git a/src/prelude.rs b/src/prelude.rs index 9c79787cf9..40ef6eb098 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -42,7 +42,7 @@ crate use crate::env::{Environment, Host}; crate use crate::errors::ShellError; crate use crate::object::types::{ExtractType, Type}; crate use crate::object::{Primitive, Value}; -crate use crate::parser::{Span, Spanned}; +crate use crate::parser::{Span, Spanned, SpannedItem}; crate use crate::stream::{InputStream, OutputStream}; crate use crate::Text; crate use futures::stream::BoxStream; @@ -58,7 +58,7 @@ pub trait FromInputStream { impl FromInputStream for T where - T: Stream + Send + 'static, + T: Stream> + Send + 'static, { fn from_input_stream(self) -> OutputStream { OutputStream { diff --git a/src/stream.rs b/src/stream.rs index a7ca6e0486..9281831b89 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,15 +1,15 @@ use crate::prelude::*; pub struct InputStream { - crate values: BoxStream<'static, Value>, + crate values: BoxStream<'static, Spanned>, } impl InputStream { - pub fn into_vec(self) -> impl Future> { + pub fn into_vec(self) -> impl Future>> { self.values.collect() } - pub fn from_stream(input: impl Stream + Send + 'static) -> InputStream { + pub fn from_stream(input: impl Stream> + Send + 'static) -> InputStream { InputStream { values: input.boxed(), } @@ -22,8 +22,8 @@ impl From> for InputStream { } } -impl From> for InputStream { - fn from(input: VecDeque) -> InputStream { +impl From>> for InputStream { + fn from(input: VecDeque>) -> InputStream { InputStream { values: input.boxed(), } @@ -46,13 +46,7 @@ pub struct OutputStream { } impl OutputStream { - pub fn from_stream(input: impl Stream + Send + 'static) -> OutputStream { - OutputStream { - values: input.boxed(), - } - } - - pub fn from_input(input: impl Stream + Send + 'static) -> OutputStream { + pub fn from_input(input: impl Stream> + Send + 'static) -> OutputStream { OutputStream { values: input.map(ReturnSuccess::value).boxed(), }