Merge pull request #170 from nushell/argument-error

Add deeper support for spans in streams of values (by @wycats)
This commit is contained in:
Jonathan Turner 2019-07-14 05:51:10 +12:00 committed by GitHub
commit 5ec5167cb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 2032 additions and 1218 deletions

View File

@ -1,5 +1,5 @@
variables: variables:
lkg-rust-nightly: "2019-06-28" lkg-rust-nightly: "2019-07-04"
trigger: trigger:
- master - master

14
Cargo.lock generated
View File

@ -1278,12 +1278,14 @@ dependencies = [
[[package]] [[package]]
name = "language-reporting" name = "language-reporting"
version = "0.3.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/jonathandturner/language-reporting#0a6c284a19a00b5b6b680480c0ad5f241fc5edac"
dependencies = [ dependencies = [
"derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"render-tree 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "render-tree 0.1.1 (git+https://github.com/jonathandturner/language-reporting)",
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1649,7 +1651,7 @@ dependencies = [
"image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"language-reporting 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "language-reporting 0.3.0 (git+https://github.com/jonathandturner/language-reporting)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"logos 0.10.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)", "logos 0.10.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2354,7 +2356,7 @@ dependencies = [
[[package]] [[package]]
name = "render-tree" name = "render-tree"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/jonathandturner/language-reporting#0a6c284a19a00b5b6b680480c0ad5f241fc5edac"
dependencies = [ dependencies = [
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3497,7 +3499,7 @@ dependencies = [
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b7d43206b34b3f94ea9445174bda196e772049b9bddbc620c9d29b2d20110d" "checksum jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b7d43206b34b3f94ea9445174bda196e772049b9bddbc620c9d29b2d20110d"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-reporting 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61e5d4e5c7a76724d544bb5652a8a3ded29475a1b260a263b5d6743f5871ac83" "checksum language-reporting 0.3.0 (git+https://github.com/jonathandturner/language-reporting)" = "<none>"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
@ -3606,7 +3608,7 @@ dependencies = [
"checksum regex 1.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1325e8a57b7da4cbcb38b3957112f729990bad0a18420e7e250ef6b1d9a15763" "checksum regex 1.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1325e8a57b7da4cbcb38b3957112f729990bad0a18420e7e250ef6b1d9a15763"
"checksum regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d76410686f9e3a17f06128962e0ecc5755870bb890c34820c7af7f1db2e1d48" "checksum regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d76410686f9e3a17f06128962e0ecc5755870bb890c34820c7af7f1db2e1d48"
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
"checksum render-tree 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "68ed587df09cfb7ce1bc6fe8f77e24db219f222c049326ccbfb948ec67e31664" "checksum render-tree 0.1.1 (git+https://github.com/jonathandturner/language-reporting)" = "<none>"
"checksum reqwest 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)" = "00eb63f212df0e358b427f0f40aa13aaea010b470be642ad422bcbca2feff2e4" "checksum reqwest 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)" = "00eb63f212df0e358b427f0f40aa13aaea010b470be642ad422bcbca2feff2e4"
"checksum result 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560" "checksum result 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560"
"checksum roxmltree 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "330d8f80a274bc3cb608908ee345970e7e24b96907f1ad69615a498bec57871c" "checksum roxmltree 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "330d8f80a274bc3cb608908ee345970e7e24b96907f1ad69615a498bec57871c"

View File

@ -40,7 +40,7 @@ serde_derive = "1.0.94"
getset = "0.0.7" getset = "0.0.7"
logos = "0.10.0-rc2" logos = "0.10.0-rc2"
logos-derive = "0.10.0-rc2" logos-derive = "0.10.0-rc2"
language-reporting = "0.3.0" language-reporting = {git = "https://github.com/jonathandturner/language-reporting"}
app_dirs = "1.2.1" app_dirs = "1.2.1"
toml = "0.5.1" toml = "0.5.1"
toml-query = "0.9.2" toml-query = "0.9.2"

View File

@ -159,6 +159,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
command("sysinfo", Box::new(sysinfo::sysinfo)), command("sysinfo", Box::new(sysinfo::sysinfo)),
command("cd", Box::new(cd::cd)), command("cd", Box::new(cd::cd)),
command("view", Box::new(view::view)), command("view", Box::new(view::view)),
// command("skip", skip::Skip),
command("first", Box::new(first::first)), command("first", Box::new(first::first)),
command("size", Box::new(size::size)), command("size", Box::new(size::size)),
command("from-ini", Box::new(from_ini::from_ini)), command("from-ini", Box::new(from_ini::from_ini)),
@ -167,7 +168,6 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
command("from-xml", Box::new(from_xml::from_xml)), command("from-xml", Box::new(from_xml::from_xml)),
command("from-yaml", Box::new(from_yaml::from_yaml)), command("from-yaml", Box::new(from_yaml::from_yaml)),
command("get", Box::new(get::get)), command("get", Box::new(get::get)),
command("enter", Box::new(enter::enter)),
command("exit", Box::new(exit::exit)), command("exit", Box::new(exit::exit)),
command("lines", Box::new(lines::lines)), command("lines", Box::new(lines::lines)),
command("pick", Box::new(pick::pick)), command("pick", Box::new(pick::pick)),
@ -180,11 +180,12 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
command("to-json", Box::new(to_json::to_json)), command("to-json", Box::new(to_json::to_json)),
command("to-toml", Box::new(to_toml::to_toml)), command("to-toml", Box::new(to_toml::to_toml)),
command("sort-by", Box::new(sort_by::sort_by)), command("sort-by", Box::new(sort_by::sort_by)),
command("sort-by", Box::new(sort_by::sort_by)),
Arc::new(Open), Arc::new(Open),
Arc::new(Where), Arc::new(Where),
Arc::new(Config), Arc::new(Config),
Arc::new(SkipWhile), Arc::new(SkipWhile),
command("sort-by", Box::new(sort_by::sort_by)), Arc::new(Enter),
]); ]);
context.add_sinks(vec![ context.add_sinks(vec![
@ -227,7 +228,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
let last = env.back().unwrap(); let last = env.back().unwrap();
(last.obj().clone(), last.path().display().to_string()) (last.obj().clone(), last.path().display().to_string())
}; };
let readline = match obj { let readline = match obj.item {
Value::Filesystem => rl.readline(&format!( Value::Filesystem => rl.readline(&format!(
"{}{}> ", "{}{}> ",
cwd, cwd,
@ -392,7 +393,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
} }
(Some(ClassifiedCommand::Sink(left)), None) => { (Some(ClassifiedCommand::Sink(left)), None) => {
let input_vec: Vec<Value> = input.objects.collect().await; let input_vec: Vec<Spanned<Value>> = input.objects.into_vec().await;
if let Err(err) = left.run(ctx, input_vec) { if let Err(err) = left.run(ctx, input_vec) {
return LineResult::Error(line.clone(), err); return LineResult::Error(line.clone(), err);
} }
@ -496,7 +497,7 @@ fn classify_command(
let config = command.config(); let config = command.config();
let scope = Scope::empty(); let scope = Scope::empty();
trace!("classifying {:?}", config); trace!(target: "nu::build_pipeline", "classifying {:?}", config);
let args = config.evaluate_args(call, context, &scope, source)?; let args = config.evaluate_args(call, context, &scope, source)?;

View File

@ -1,3 +1,6 @@
#[macro_use]
crate mod macros;
crate mod args; crate mod args;
crate mod autoview; crate mod autoview;
crate mod cd; crate mod cd;
@ -39,6 +42,7 @@ crate mod where_;
crate use command::command; crate use command::command;
crate use config::Config; crate use config::Config;
crate use enter::Enter;
crate use open::Open; crate use open::Open;
crate use skip_while::SkipWhile; crate use skip_while::SkipWhile;
crate use where_::Where; crate use where_::Where;

View File

@ -5,7 +5,11 @@ use crate::prelude::*;
pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> {
if args.input.len() > 0 { if args.input.len() > 0 {
if let Value::Binary(_) = args.input[0] { if let Spanned {
item: Value::Binary(_),
..
} = args.input[0]
{
args.ctx.get_sink("binaryview").run(args)?; args.ctx.get_sink("binaryview").run(args)?;
} else if equal_shapes(&args.input) { } else if equal_shapes(&args.input) {
args.ctx.get_sink("table").run(args)?; args.ctx.get_sink("table").run(args)?;
@ -22,7 +26,7 @@ pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> {
Ok(()) Ok(())
} }
fn equal_shapes(input: &Vec<Value>) -> bool { fn equal_shapes(input: &Vec<Spanned<Value>>) -> bool {
let mut items = input.iter(); let mut items = input.iter();
let item = match items.next() { let item = match items.next() {

View File

@ -6,8 +6,9 @@ use std::path::PathBuf;
pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
let env = args.env.lock().unwrap(); let env = args.env.lock().unwrap();
let latest = env.back().unwrap(); let latest = env.back().unwrap();
let obj = &latest.obj;
match latest.obj { match obj.item() {
Value::Filesystem => { Value::Filesystem => {
let cwd = latest.path().to_path_buf(); let cwd = latest.path().to_path_buf();
@ -52,14 +53,14 @@ pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
} }
} }
} }
stream.push_back(ReturnValue::change_cwd(path)); stream.push_back(ReturnSuccess::change_cwd(path));
Ok(stream.boxed()) Ok(stream.into())
} }
_ => { _ => {
let mut stream = VecDeque::new(); let mut stream = VecDeque::new();
match args.nth(0) { match args.nth(0) {
None => { None => {
stream.push_back(ReturnValue::change_cwd(PathBuf::from("/"))); stream.push_back(ReturnSuccess::change_cwd(PathBuf::from("/")));
} }
Some(v) => { Some(v) => {
let mut cwd = latest.path().to_path_buf(); let mut cwd = latest.path().to_path_buf();
@ -75,10 +76,10 @@ pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
} }
}, },
} }
stream.push_back(ReturnValue::change_cwd(cwd)); stream.push_back(ReturnSuccess::change_cwd(cwd));
} }
}; };
Ok(stream.boxed()) Ok(stream.into())
} }
} }
} }

View File

@ -54,21 +54,21 @@ crate struct ClassifiedInputStream {
impl ClassifiedInputStream { impl ClassifiedInputStream {
crate fn new() -> ClassifiedInputStream { crate fn new() -> ClassifiedInputStream {
ClassifiedInputStream { ClassifiedInputStream {
objects: VecDeque::new().boxed(), objects: VecDeque::new().into(),
stdin: None, stdin: None,
} }
} }
crate fn from_input_stream(stream: InputStream) -> ClassifiedInputStream { crate fn from_input_stream(stream: impl Into<InputStream>) -> ClassifiedInputStream {
ClassifiedInputStream { ClassifiedInputStream {
objects: stream, objects: stream.into(),
stdin: None, stdin: None,
} }
} }
crate fn from_stdout(stdout: std::fs::File) -> ClassifiedInputStream { crate fn from_stdout(stdout: std::fs::File) -> ClassifiedInputStream {
ClassifiedInputStream { ClassifiedInputStream {
objects: VecDeque::new().boxed(), objects: VecDeque::new().into(),
stdin: Some(stdout), stdin: Some(stdout),
} }
} }
@ -86,6 +86,18 @@ crate enum ClassifiedCommand {
External(ExternalCommand), External(ExternalCommand),
} }
impl ClassifiedCommand {
#[allow(unused)]
pub fn span(&self) -> Span {
match self {
ClassifiedCommand::Expr(token) => token.span(),
ClassifiedCommand::Internal(internal) => internal.name_span.into(),
ClassifiedCommand::Sink(sink) => sink.name_span.into(),
ClassifiedCommand::External(external) => external.name_span.into(),
}
}
}
crate struct SinkCommand { crate struct SinkCommand {
crate command: Arc<dyn Sink>, crate command: Arc<dyn Sink>,
crate name_span: Option<Span>, crate name_span: Option<Span>,
@ -93,7 +105,11 @@ crate struct SinkCommand {
} }
impl SinkCommand { impl SinkCommand {
crate fn run(self, context: &mut Context, input: Vec<Value>) -> Result<(), ShellError> { crate fn run(
self,
context: &mut Context,
input: Vec<Spanned<Value>>,
) -> Result<(), ShellError> {
context.run_sink(self.command, self.name_span.clone(), self.args, input) context.run_sink(self.command, self.name_span.clone(), self.args, input)
} }
} }
@ -110,31 +126,24 @@ impl InternalCommand {
context: &mut Context, context: &mut Context,
input: ClassifiedInputStream, input: ClassifiedInputStream,
) -> Result<InputStream, ShellError> { ) -> Result<InputStream, ShellError> {
let objects = if log_enabled!(log::Level::Trace) { if log_enabled!(log::Level::Trace) {
trace!("->"); trace!(target: "nu::run::internal", "->");
trace!("{}", self.command.name()); trace!(target: "nu::run::internal", "{}", self.command.name());
trace!("{:?}", self.args.debug()); trace!(target: "nu::run::internal", "{:?}", self.args.debug());
let objects: Vec<_> = input.objects.collect().await; }
trace!(
"input = {:#?}",
objects.iter().map(|o| o.debug()).collect::<Vec<_>>(),
);
VecDeque::from(objects).boxed()
} else {
input.objects
};
let mut result = let objects: InputStream =
trace_stream!(target: "nu::trace_stream::internal", "input" = input.objects);
let result =
context.run_command(self.command, self.name_span.clone(), self.args, objects)?; context.run_command(self.command, self.name_span.clone(), self.args, objects)?;
let mut result = result.values;
let mut stream = VecDeque::new(); let mut stream = VecDeque::new();
while let Some(item) = result.next().await { while let Some(item) = result.next().await {
match item { match item? {
ReturnValue::Value(Value::Error(err)) => { ReturnSuccess::Action(action) => match action {
return Err(*err);
}
ReturnValue::Action(action) => match action {
CommandAction::ChangePath(path) => { CommandAction::ChangePath(path) => {
context.env.lock().unwrap().back_mut().map(|x| { context.env.lock().unwrap().back_mut().map(|x| {
x.path = path; x.path = path;
@ -150,7 +159,11 @@ impl InternalCommand {
} }
CommandAction::Exit => match context.env.lock().unwrap().pop_back() { CommandAction::Exit => match context.env.lock().unwrap().pop_back() {
Some(Environment { Some(Environment {
obj: Value::Filesystem, obj:
Spanned {
item: Value::Filesystem,
..
},
.. ..
}) => std::process::exit(0), }) => std::process::exit(0),
None => std::process::exit(-1), None => std::process::exit(-1),
@ -158,12 +171,13 @@ impl InternalCommand {
}, },
}, },
ReturnValue::Value(v) => { ReturnSuccess::Value(v) => {
stream.push_back(v); stream.push_back(v);
} }
} }
} }
Ok(stream.boxed() as InputStream)
Ok(stream.into())
} }
} }
@ -187,9 +201,12 @@ impl ExternalCommand {
input: ClassifiedInputStream, input: ClassifiedInputStream,
stream_next: StreamNext, stream_next: StreamNext,
) -> Result<ClassifiedInputStream, ShellError> { ) -> Result<ClassifiedInputStream, ShellError> {
let inputs: Vec<Value> = input.objects.collect().await; let stdin = input.stdin;
let inputs: Vec<Spanned<Value>> = input.objects.into_vec().await;
let name_span = self.name_span.clone();
trace!("{:?} -> {}", inputs, self.name); trace!(target: "nu::run::external", "-> {}", self.name);
trace!(target: "nu::run::external", "inputs = {:?}", inputs);
let mut arg_string = format!("{}", self.name); let mut arg_string = format!("{}", self.name);
for arg in &self.args { for arg in &self.args {
@ -298,7 +315,7 @@ impl ExternalCommand {
} }
}; };
if let Some(stdin) = input.stdin { if let Some(stdin) = stdin {
process = process.stdin(stdin); process = process.stdin(stdin);
} }
@ -317,8 +334,11 @@ impl ExternalCommand {
let stdout = popen.stdout.take().unwrap(); let stdout = popen.stdout.take().unwrap();
let file = futures::io::AllowStdIo::new(stdout); let file = futures::io::AllowStdIo::new(stdout);
let stream = Framed::new(file, LinesCodec {}); let stream = Framed::new(file, LinesCodec {});
let stream = stream.map(|line| Value::string(line.unwrap())); let stream =
Ok(ClassifiedInputStream::from_input_stream(stream.boxed())) stream.map(move |line| Value::string(line.unwrap()).spanned(name_span));
Ok(ClassifiedInputStream::from_input_stream(
stream.boxed() as BoxStream<'static, Spanned<Value>>
))
} }
} }
} }

View File

@ -1,10 +1,11 @@
use crate::commands::command::SinkCommandArgs; use crate::commands::command::SinkCommandArgs;
use crate::errors::ShellError; use crate::errors::{labelled, ShellError};
use clipboard::{ClipboardContext, ClipboardProvider}; use clipboard::{ClipboardContext, ClipboardProvider};
pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> { pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> {
let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap(); let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap();
let mut new_copy_data = String::new(); let mut new_copy_data = String::new();
if args.input.len() > 0 { if args.input.len() > 0 {
let mut first = true; let mut first = true;
for i in args.input.iter() { for i in args.input.iter() {
@ -13,18 +14,17 @@ pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> {
} else { } else {
first = false; first = false;
} }
match i.as_string() {
Ok(s) => new_copy_data.push_str(&s), let string = i.as_string().map_err(labelled(
Err(_) => { args.name_span,
return Err(ShellError::maybe_labeled_error(
"Given non-string data", "Given non-string data",
"expected strings from pipeline", "expected strings from pipeline",
args.name_span, ))?;
))
} new_copy_data.push_str(&string);
}
} }
} }
clip_context.set_contents(new_copy_data).unwrap(); clip_context.set_contents(new_copy_data).unwrap();
Ok(()) Ok(())

View File

@ -49,25 +49,41 @@ pub struct SinkCommandArgs {
pub ctx: Context, pub ctx: Context,
pub name_span: Option<Span>, pub name_span: Option<Span>,
pub args: Args, pub args: Args,
pub input: Vec<Value>, pub input: Vec<Spanned<Value>>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum CommandAction { pub enum CommandAction {
ChangePath(PathBuf), ChangePath(PathBuf),
Enter(Value), Enter(Spanned<Value>),
Exit, Exit,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum ReturnValue { pub enum ReturnSuccess {
Value(Value), Value(Spanned<Value>),
Action(CommandAction), Action(CommandAction),
} }
impl ReturnValue { pub type ReturnValue = Result<ReturnSuccess, ShellError>;
crate fn change_cwd(path: PathBuf) -> ReturnValue {
ReturnValue::Action(CommandAction::ChangePath(path)) impl From<Spanned<Value>> for ReturnValue {
fn from(input: Spanned<Value>) -> ReturnValue {
Ok(ReturnSuccess::Value(input))
}
}
impl ReturnSuccess {
pub fn change_cwd(path: PathBuf) -> ReturnValue {
Ok(ReturnSuccess::Action(CommandAction::ChangePath(path)))
}
pub fn value(input: impl Into<Spanned<Value>>) -> ReturnValue {
Ok(ReturnSuccess::Value(input.into()))
}
pub fn spanned_value(input: Value, span: Span) -> ReturnValue {
Ok(ReturnSuccess::Value(Spanned::from_item(input, span)))
} }
} }
@ -78,14 +94,11 @@ pub trait Command {
fn config(&self) -> registry::CommandConfig { fn config(&self) -> registry::CommandConfig {
registry::CommandConfig { registry::CommandConfig {
name: self.name().to_string(), name: self.name().to_string(),
mandatory_positional: vec![], positional: vec![],
optional_positional: vec![],
rest_positional: true, rest_positional: true,
named: indexmap::IndexMap::new(), named: indexmap::IndexMap::new(),
is_filter: true, is_filter: true,
is_sink: false, is_sink: false,
can_load: vec![],
can_save: vec![],
} }
} }
} }
@ -97,14 +110,11 @@ pub trait Sink {
fn config(&self) -> registry::CommandConfig { fn config(&self) -> registry::CommandConfig {
registry::CommandConfig { registry::CommandConfig {
name: self.name().to_string(), name: self.name().to_string(),
mandatory_positional: vec![], positional: vec![],
optional_positional: vec![],
rest_positional: true, rest_positional: true,
named: indexmap::IndexMap::new(), named: indexmap::IndexMap::new(),
is_filter: false, is_filter: false,
is_sink: true, is_sink: true,
can_load: vec![],
can_save: vec![],
} }
} }
} }

View File

@ -1,10 +1,12 @@
use crate::prelude::*;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::config; use crate::object::config;
use crate::object::Value; use crate::object::Value;
use crate::parser::registry::{CommandConfig, NamedType, NamedValue}; use crate::parser::registry::{CommandConfig, NamedType, NamedValue};
use crate::prelude::*;
use indexmap::IndexMap; use indexmap::IndexMap;
use log::trace; use log::trace;
use std::iter::FromIterator;
pub struct Config; pub struct Config;
@ -29,20 +31,17 @@ impl Command for Config {
CommandConfig { CommandConfig {
name: self.name().to_string(), name: self.name().to_string(),
mandatory_positional: vec![], positional: vec![],
optional_positional: vec![],
rest_positional: false, rest_positional: false,
named, named,
is_sink: true, is_sink: true,
is_filter: false, is_filter: false,
can_load: vec![],
can_save: vec![],
} }
} }
} }
pub fn config(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn config(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut result = crate::object::config::config()?; let mut result = crate::object::config::config(args.name_span)?;
trace!("{:#?}", args.args.positional); trace!("{:#?}", args.args.positional);
trace!("{:#?}", args.args.named); trace!("{:#?}", args.args.named);
@ -54,8 +53,7 @@ pub fn config(args: CommandArgs) -> Result<OutputStream, ShellError> {
.ok_or_else(|| ShellError::string(&format!("Missing key {} in config", key)))?; .ok_or_else(|| ShellError::string(&format!("Missing key {} in config", key)))?;
return Ok( return Ok(
futures::stream::once(futures::future::ready(ReturnValue::Value(value.clone()))) stream![value.clone()].into(), // futures::stream::once(futures::future::ready(ReturnSuccess::Value(value.clone()))).into(),
.boxed(),
); );
} }
@ -66,24 +64,19 @@ pub fn config(args: CommandArgs) -> Result<OutputStream, ShellError> {
config::write_config(&result)?; config::write_config(&result)?;
return Ok( return Ok(
futures::stream::once(futures::future::ready(ReturnValue::Value(Value::Object( stream![Spanned::from_item(Value::Object(result.into()), v.span())]
result.into(), .from_input_stream(),
))))
.boxed(),
); );
} }
} }
if let Some(_) = args.get("clear") { if let Some(c) = args.get("clear") {
result.clear(); result.clear();
config::write_config(&result)?; config::write_config(&result)?;
return Ok( return Ok(
futures::stream::once(futures::future::ready(ReturnValue::Value(Value::Object( stream![Spanned::from_item(Value::Object(result.into()), c.span())].from_input_stream(),
result.into(),
))))
.boxed(),
); );
} }
@ -99,21 +92,12 @@ pub fn config(args: CommandArgs) -> Result<OutputStream, ShellError> {
))); )));
} }
return Ok( let obj = VecDeque::from_iter(vec![Value::Object(result.into()).spanned(v)]);
futures::stream::once(futures::future::ready(ReturnValue::Value(Value::Object( return Ok(obj.from_input_stream());
result.into(),
))))
.boxed(),
);
} }
if args.len() == 0 { if args.len() == 0 {
return Ok( return Ok(vec![Value::Object(result.into()).spanned(args.name_span)].into());
futures::stream::once(futures::future::ready(ReturnValue::Value(Value::Object(
result.into(),
))))
.boxed(),
);
} }
Err(ShellError::string(format!("Unimplemented"))) Err(ShellError::string(format!("Unimplemented")))

View File

@ -2,9 +2,33 @@ use crate::commands::command::CommandAction;
use crate::commands::open::{fetch, parse_as_value}; use crate::commands::open::{fetch, parse_as_value};
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::{Primitive, Value}; use crate::object::{Primitive, Value};
use crate::parser::registry::{CommandConfig, PositionalType};
use crate::prelude::*; use crate::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
pub struct Enter;
impl Command for Enter {
fn config(&self) -> CommandConfig {
CommandConfig {
name: self.name().to_string(),
positional: vec![PositionalType::mandatory_block("path")],
rest_positional: false,
is_filter: false,
is_sink: false,
named: indexmap::IndexMap::new(),
}
}
fn name(&self) -> &str {
"enter"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
enter(args)
}
}
pub fn enter(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn enter(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.len() == 0 { if args.len() == 0 {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
@ -27,7 +51,7 @@ pub fn enter(args: CommandArgs) -> Result<OutputStream, ShellError> {
let full_path = PathBuf::from(cwd); 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)?, Value::Primitive(Primitive::String(s)) => fetch(&full_path, s, args.expect_nth(0)?.span)?,
_ => { _ => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
@ -68,15 +92,14 @@ pub fn enter(args: CommandArgs) -> Result<OutputStream, ShellError> {
}; };
match contents { match contents {
Value::Primitive(Primitive::String(x)) => { Value::Primitive(Primitive::String(string)) => {
stream.push_back(ReturnValue::Action(CommandAction::Enter(parse_as_value( stream.push_back(Ok(ReturnSuccess::Action(CommandAction::Enter(
file_extension, parse_as_value(file_extension, string, contents_span, span)?,
x, ))));
span,
)?)));
}
x => stream.push_back(ReturnValue::Action(CommandAction::Enter(x))),
} }
Ok(stream.boxed()) other => stream.push_back(ReturnSuccess::value(other.spanned(contents_span))),
};
Ok(stream.into())
} }

View File

@ -3,7 +3,5 @@ use crate::errors::ShellError;
use crate::prelude::*; use crate::prelude::*;
pub fn exit(_args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn exit(_args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut stream = VecDeque::new(); Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into())
stream.push_back(ReturnValue::Action(CommandAction::Exit));
Ok(stream.boxed())
} }

View File

@ -27,8 +27,5 @@ pub fn first(args: CommandArgs) -> Result<OutputStream, ShellError> {
let input = args.input; let input = args.input;
Ok(input Ok(OutputStream::from_input(input.values.take(amount as u64)))
.take(amount as u64)
.map(|v| ReturnValue::Value(v))
.boxed())
} }

View File

@ -1,51 +1,61 @@
use crate::object::{Dictionary, Primitive, Value}; use crate::object::{Primitive, SpannedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
use indexmap::IndexMap;
use std::collections::HashMap; use std::collections::HashMap;
fn convert_ini_second_to_nu_value(v: &HashMap<String, String>) -> Value { fn convert_ini_second_to_nu_value(
let mut second = Dictionary::new(IndexMap::new()); v: &HashMap<String, String>,
span: impl Into<Span>,
) -> Spanned<Value> {
let mut second = SpannedDictBuilder::new(span);
for (key, value) in v.into_iter() { for (key, value) in v.into_iter() {
second.add( second.insert(key.clone(), Primitive::String(value.clone()));
key.clone(),
Value::Primitive(Primitive::String(value.clone())),
);
} }
Value::Object(second)
} second.into_spanned_value()
fn convert_ini_top_to_nu_value(v: &HashMap<String, HashMap<String, String>>) -> Value {
let mut top_level = Dictionary::new(IndexMap::new());
for (key, value) in v.iter() {
top_level.add(key.clone(), convert_ini_second_to_nu_value(value));
}
Value::Object(top_level)
} }
pub fn from_ini_string_to_value(s: String) -> Result<Value, Box<dyn std::error::Error>> { fn convert_ini_top_to_nu_value(
v: &HashMap<String, HashMap<String, String>>,
span: impl Into<Span>,
) -> Spanned<Value> {
let span = span.into();
let mut top_level = SpannedDictBuilder::new(span);
for (key, value) in v.iter() {
top_level.insert_spanned(key.clone(), convert_ini_second_to_nu_value(value, span));
}
top_level.into_spanned_value()
}
pub fn from_ini_string_to_value(
s: String,
span: impl Into<Span>,
) -> Result<Spanned<Value>, Box<dyn std::error::Error>> {
let v: HashMap<String, HashMap<String, String>> = serde_ini::from_str(&s)?; let v: HashMap<String, HashMap<String, String>> = serde_ini::from_str(&s)?;
Ok(convert_ini_top_to_nu_value(&v)) Ok(convert_ini_top_to_nu_value(&v, span))
} }
pub fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.name_span;
Ok(out Ok(out
.map(move |a| match a { .values
Value::Primitive(Primitive::String(s)) => match from_ini_string_to_value(s) { .map(move |a| match a.item {
Ok(x) => ReturnValue::Value(x), Value::Primitive(Primitive::String(s)) => match from_ini_string_to_value(s, span) {
Err(e) => { Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( Err(e) => Err(ShellError::maybe_labeled_error(
"Could not parse as INI", "Could not parse as INI",
format!("{:#?}", e), format!("{:#?}", e),
span, span,
)))) )),
}
}, },
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( _ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline", "Expected string values from pipeline",
"expects strings from pipeline", "expects strings from pipeline",
span, span,
)))), )),
}) })
.boxed()) .to_output_stream())
} }

View File

@ -1,55 +1,67 @@
use crate::object::base::OF64; use crate::object::base::OF64;
use crate::object::{Dictionary, Primitive, Value}; use crate::object::{Primitive, SpannedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
fn convert_json_value_to_nu_value(v: &serde_hjson::Value) -> Value { fn convert_json_value_to_nu_value(v: &serde_hjson::Value, span: impl Into<Span>) -> Spanned<Value> {
let span = span.into();
match v { match v {
serde_hjson::Value::Null => Value::Primitive(Primitive::String(String::from(""))), serde_hjson::Value::Null => {
serde_hjson::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)), Value::Primitive(Primitive::String(String::from(""))).spanned(span)
serde_hjson::Value::F64(n) => Value::Primitive(Primitive::Float(OF64::from(*n))), }
serde_hjson::Value::U64(n) => Value::Primitive(Primitive::Int(*n as i64)), serde_hjson::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).spanned(span),
serde_hjson::Value::I64(n) => Value::Primitive(Primitive::Int(*n as i64)), serde_hjson::Value::F64(n) => {
serde_hjson::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))), Value::Primitive(Primitive::Float(OF64::from(*n))).spanned(span)
}
serde_hjson::Value::U64(n) => Value::Primitive(Primitive::Int(*n as i64)).spanned(span),
serde_hjson::Value::I64(n) => Value::Primitive(Primitive::Int(*n as i64)).spanned(span),
serde_hjson::Value::String(s) => {
Value::Primitive(Primitive::String(String::from(s))).spanned(span)
}
serde_hjson::Value::Array(a) => Value::List( serde_hjson::Value::Array(a) => Value::List(
a.iter() a.iter()
.map(|x| convert_json_value_to_nu_value(x)) .map(|x| convert_json_value_to_nu_value(x, span))
.collect(), .collect(),
), )
.spanned(span),
serde_hjson::Value::Object(o) => { serde_hjson::Value::Object(o) => {
let mut collected = Dictionary::default(); let mut collected = SpannedDictBuilder::new(span);
for (k, v) in o.iter() { for (k, v) in o.iter() {
collected.add(k.clone(), convert_json_value_to_nu_value(v)); collected.insert_spanned(k.clone(), convert_json_value_to_nu_value(v, span));
} }
Value::Object(collected)
collected.into_spanned_value()
} }
} }
} }
pub fn from_json_string_to_value(s: String) -> serde_hjson::Result<Value> { pub fn from_json_string_to_value(
s: String,
span: impl Into<Span>,
) -> serde_hjson::Result<Spanned<Value>> {
let v: serde_hjson::Value = serde_hjson::from_str(&s)?; let v: serde_hjson::Value = serde_hjson::from_str(&s)?;
Ok(convert_json_value_to_nu_value(&v)) Ok(convert_json_value_to_nu_value(&v, span))
} }
pub fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.name_span;
Ok(out Ok(out
.map(move |a| match a { .values
Value::Primitive(Primitive::String(s)) => match from_json_string_to_value(s) { .map(move |a| match a.item {
Ok(x) => ReturnValue::Value(x), Value::Primitive(Primitive::String(s)) => match from_json_string_to_value(s, span) {
Err(_) => { Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( Err(_) => Err(ShellError::maybe_labeled_error(
"Could not parse as JSON", "Could not parse as JSON",
"piped data failed JSON parse", "piped data failed JSON parse",
span, span,
)))) )),
}
}, },
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( _ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline", "Expected string values from pipeline",
"expects strings from pipeline", "expects strings from pipeline",
span, span,
)))), )),
}) })
.boxed()) .to_output_stream())
} }

View File

@ -1,54 +1,65 @@
use crate::object::base::OF64; use crate::object::base::OF64;
use crate::object::{Dictionary, Primitive, Value}; use crate::object::{Primitive, SpannedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
fn convert_toml_value_to_nu_value(v: &toml::Value) -> Value { fn convert_toml_value_to_nu_value(v: &toml::Value, span: impl Into<Span>) -> Spanned<Value> {
let span = span.into();
match v { match v {
toml::Value::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)), toml::Value::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)).spanned(span),
toml::Value::Integer(n) => Value::Primitive(Primitive::Int(*n)), toml::Value::Integer(n) => Value::Primitive(Primitive::Int(*n)).spanned(span),
toml::Value::Float(n) => Value::Primitive(Primitive::Float(OF64::from(*n))), toml::Value::Float(n) => Value::Primitive(Primitive::Float(OF64::from(*n))).spanned(span),
toml::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))), toml::Value::String(s) => {
Value::Primitive(Primitive::String(String::from(s))).spanned(span)
}
toml::Value::Array(a) => Value::List( toml::Value::Array(a) => Value::List(
a.iter() a.iter()
.map(|x| convert_toml_value_to_nu_value(x)) .map(|x| convert_toml_value_to_nu_value(x, span))
.collect(), .collect(),
), )
toml::Value::Datetime(dt) => Value::Primitive(Primitive::String(dt.to_string())), .spanned(span),
toml::Value::Table(t) => { toml::Value::Datetime(dt) => {
let mut collected = Dictionary::default(); Value::Primitive(Primitive::String(dt.to_string())).spanned(span)
for (k, v) in t.iter() {
collected.add(k.clone(), convert_toml_value_to_nu_value(v));
} }
Value::Object(collected) toml::Value::Table(t) => {
let mut collected = SpannedDictBuilder::new(span);
for (k, v) in t.iter() {
collected.insert_spanned(k.clone(), convert_toml_value_to_nu_value(v, span));
}
collected.into_spanned_value()
} }
} }
} }
pub fn from_toml_string_to_value(s: String) -> Result<Value, Box<dyn std::error::Error>> { pub fn from_toml_string_to_value(
s: String,
span: impl Into<Span>,
) -> Result<Spanned<Value>, Box<dyn std::error::Error>> {
let v: toml::Value = s.parse::<toml::Value>()?; let v: toml::Value = s.parse::<toml::Value>()?;
Ok(convert_toml_value_to_nu_value(&v)) Ok(convert_toml_value_to_nu_value(&v, span))
} }
pub fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.name_span;
Ok(out Ok(out
.map(move |a| match a { .values
Value::Primitive(Primitive::String(s)) => match from_toml_string_to_value(s) { .map(move |a| match a.item {
Ok(x) => ReturnValue::Value(x), Value::Primitive(Primitive::String(s)) => match from_toml_string_to_value(s, span) {
Err(_) => { Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( Err(_) => Err(ShellError::maybe_labeled_error(
"Could not parse as TOML", "Could not parse as TOML",
"piped data failed TOML parse", "piped data failed TOML parse",
span, span,
)))) )),
}
}, },
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( _ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline", "Expected string values from pipeline",
"expects strings from pipeline", "expects strings from pipeline",
span, span,
)))), )),
}) })
.boxed()) .to_output_stream())
} }

View File

@ -1,19 +1,27 @@
use crate::object::{Dictionary, Primitive, Value}; use crate::object::{Primitive, SpannedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
fn from_node_to_value<'a, 'd>(n: &roxmltree::Node<'a, 'd>) -> Value { fn from_node_to_value<'a, 'd>(
n: &roxmltree::Node<'a, 'd>,
span: impl Into<Span>,
) -> Spanned<Value> {
let span = span.into();
if n.is_element() { if n.is_element() {
let name = n.tag_name().name().trim().to_string(); let name = n.tag_name().name().trim().to_string();
let mut children_values = vec![]; let mut children_values = vec![];
for c in n.children() { for c in n.children() {
children_values.push(from_node_to_value(&c)); children_values.push(from_node_to_value(&c, span));
} }
let children_values: Vec<Value> = children_values let children_values: Vec<Spanned<Value>> = children_values
.into_iter() .into_iter()
.filter(|x| match x { .filter(|x| match x {
Value::Primitive(Primitive::String(f)) => { Spanned {
item: Value::Primitive(Primitive::String(f)),
..
} => {
if f.trim() == "" { if f.trim() == "" {
false false
} else { } else {
@ -24,50 +32,52 @@ fn from_node_to_value<'a, 'd>(n: &roxmltree::Node<'a, 'd>) -> Value {
}) })
.collect(); .collect();
let mut collected = Dictionary::default(); let mut collected = SpannedDictBuilder::new(span);
collected.add(name.clone(), Value::List(children_values)); collected.insert(name.clone(), Value::List(children_values));
Value::Object(collected) collected.into_spanned_value()
} else if n.is_comment() { } else if n.is_comment() {
Value::string("<comment>") Value::string("<comment>").spanned(span)
} else if n.is_pi() { } else if n.is_pi() {
Value::string("<processing_instruction>") Value::string("<processing_instruction>").spanned(span)
} else if n.is_text() { } else if n.is_text() {
Value::string(n.text().unwrap()) Value::string(n.text().unwrap()).spanned(span)
} else { } else {
Value::string("<unknown>") Value::string("<unknown>").spanned(span)
} }
} }
fn from_document_to_value(d: &roxmltree::Document) -> Value { fn from_document_to_value(d: &roxmltree::Document, span: impl Into<Span>) -> Spanned<Value> {
from_node_to_value(&d.root_element()) from_node_to_value(&d.root_element(), span)
} }
pub fn from_xml_string_to_value(s: String) -> Result<Value, Box<dyn std::error::Error>> { pub fn from_xml_string_to_value(
s: String,
span: impl Into<Span>,
) -> Result<Spanned<Value>, Box<dyn std::error::Error>> {
let parsed = roxmltree::Document::parse(&s)?; let parsed = roxmltree::Document::parse(&s)?;
Ok(from_document_to_value(&parsed)) Ok(from_document_to_value(&parsed, span))
} }
pub fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.name_span;
Ok(out Ok(out
.map(move |a| match a { .values
Value::Primitive(Primitive::String(s)) => match from_xml_string_to_value(s) { .map(move |a| match a.item {
Ok(x) => ReturnValue::Value(x), Value::Primitive(Primitive::String(s)) => match from_xml_string_to_value(s, span) {
Err(_) => { Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( Err(_) => Err(ShellError::maybe_labeled_error(
"Could not parse as XML", "Could not parse as XML",
"piped data failed XML parse", "piped data failed XML parse",
span, span,
)))) )),
}
}, },
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( _ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline", "Expected string values from pipeline",
"expects strings from pipeline", "expects strings from pipeline",
span, span,
)))), )),
}) })
.boxed()) .to_output_stream())
} }

View File

@ -1,64 +1,72 @@
use crate::object::base::OF64; use crate::object::base::OF64;
use crate::object::{Dictionary, Primitive, Value}; use crate::object::{Primitive, SpannedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value) -> Value { fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, span: impl Into<Span>) -> Spanned<Value> {
let span = span.into();
match v { match v {
serde_yaml::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)), serde_yaml::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).spanned(span),
serde_yaml::Value::Number(n) if n.is_i64() => { serde_yaml::Value::Number(n) if n.is_i64() => {
Value::Primitive(Primitive::Int(n.as_i64().unwrap())) Value::Primitive(Primitive::Int(n.as_i64().unwrap())).spanned(span)
} }
serde_yaml::Value::Number(n) if n.is_f64() => { serde_yaml::Value::Number(n) if n.is_f64() => {
Value::Primitive(Primitive::Float(OF64::from(n.as_f64().unwrap()))) Value::Primitive(Primitive::Float(OF64::from(n.as_f64().unwrap()))).spanned(span)
} }
serde_yaml::Value::String(s) => Value::string(s), serde_yaml::Value::String(s) => Value::string(s).spanned(span),
serde_yaml::Value::Sequence(a) => Value::List( serde_yaml::Value::Sequence(a) => Value::List(
a.iter() a.iter()
.map(|x| convert_yaml_value_to_nu_value(x)) .map(|x| convert_yaml_value_to_nu_value(x, span))
.collect(), .collect(),
), )
.spanned(span),
serde_yaml::Value::Mapping(t) => { serde_yaml::Value::Mapping(t) => {
let mut collected = Dictionary::default(); let mut collected = SpannedDictBuilder::new(span);
for (k, v) in t.iter() { for (k, v) in t.iter() {
match k { match k {
serde_yaml::Value::String(k) => { serde_yaml::Value::String(k) => {
collected.add(k.clone(), convert_yaml_value_to_nu_value(v)); collected
.insert_spanned(k.clone(), convert_yaml_value_to_nu_value(v, span));
} }
_ => unimplemented!("Unknown key type"), _ => unimplemented!("Unknown key type"),
} }
} }
Value::Object(collected)
collected.into_spanned_value()
} }
serde_yaml::Value::Null => Value::Primitive(Primitive::Nothing), serde_yaml::Value::Null => Value::Primitive(Primitive::Nothing).spanned(span),
x => unimplemented!("Unsupported yaml case: {:?}", x), x => unimplemented!("Unsupported yaml case: {:?}", x),
} }
} }
pub fn from_yaml_string_to_value(s: String) -> serde_yaml::Result<Value> { pub fn from_yaml_string_to_value(
s: String,
span: impl Into<Span>,
) -> serde_yaml::Result<Spanned<Value>> {
let v: serde_yaml::Value = serde_yaml::from_str(&s)?; let v: serde_yaml::Value = serde_yaml::from_str(&s)?;
Ok(convert_yaml_value_to_nu_value(&v)) Ok(convert_yaml_value_to_nu_value(&v, span))
} }
pub fn from_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn from_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let span = args.name_span;
Ok(out Ok(out
.map(move |a| match a { .values
Value::Primitive(Primitive::String(s)) => match from_yaml_string_to_value(s) { .map(move |a| match a.item {
Ok(x) => ReturnValue::Value(x), Value::Primitive(Primitive::String(s)) => match from_yaml_string_to_value(s, span) {
Err(_) => { Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( Err(_) => Err(ShellError::maybe_labeled_error(
"Could not parse as YAML", "Could not parse as YAML",
"piped data failed YAML parse", "piped data failed YAML parse",
span, span,
)))) )),
}
}, },
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( _ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline", "Expected string values from pipeline",
"expects strings from pipeline", "expects strings from pipeline",
span, span,
)))), )),
}) })
.boxed()) .to_output_stream())
} }

View File

@ -3,22 +3,22 @@ use crate::object::Value;
use crate::parser::Span; use crate::parser::Span;
use crate::prelude::*; use crate::prelude::*;
fn get_member(path: &str, span: Span, obj: &Value) -> Option<Value> { fn get_member(path: &str, span: Span, obj: &Spanned<Value>) -> Result<Spanned<Value>, ShellError> {
let mut current = obj; let mut current = obj;
for p in path.split(".") { for p in path.split(".") {
match current.get_data_by_key(p) { match current.get_data_by_key(p) {
Some(v) => current = v, Some(v) => current = v,
None => { None => {
return Some(Value::Error(Box::new(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Unknown field", "Unknown field",
"object missing field", "object missing field",
span, span,
)))); ));
} }
} }
} }
Some(current.copy()) Ok(current.clone())
} }
pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
@ -36,10 +36,10 @@ pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
if let Ok(amount) = amount { if let Ok(amount) = amount {
return Ok(args return Ok(args
.input .input
.values
.skip(amount as u64) .skip(amount as u64)
.take(1) .take(1)
.map(|v| ReturnValue::Value(v)) .from_input_stream());
.boxed());
} }
let fields: Result<Vec<(String, Span)>, _> = args let fields: Result<Vec<(String, Span)>, _> = args
@ -51,17 +51,21 @@ pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
let stream = args let stream = args
.input .input
.values
.map(move |item| { .map(move |item| {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
for field in &fields { for field in &fields {
match get_member(&field.0, field.1, &item) { match get_member(&field.0, field.1, &item) {
Some(Value::List(l)) => { Ok(Spanned {
item: Value::List(l),
..
}) => {
for item in l { for item in l {
result.push_back(ReturnValue::Value(item.copy())); result.push_back(ReturnSuccess::value(item.clone()));
} }
} }
Some(x) => result.push_back(ReturnValue::Value(x.copy())), Ok(x) => result.push_back(ReturnSuccess::value(x.clone())),
None => {} Err(x) => result.push_back(Err(x)),
} }
} }
@ -69,5 +73,5 @@ pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
}) })
.flatten(); .flatten();
Ok(stream.boxed()) Ok(stream.to_output_stream())
} }

View File

@ -10,7 +10,8 @@ pub fn lines(args: CommandArgs) -> Result<OutputStream, ShellError> {
let span = args.name_span; let span = args.name_span;
let stream = input let stream = input
.map(move |v| match v { .values
.map(move |v| match v.item {
Value::Primitive(Primitive::String(s)) => { Value::Primitive(Primitive::String(s)) => {
let split_result: Vec<_> = s.lines().filter(|s| s.trim() != "").collect(); let split_result: Vec<_> = s.lines().filter(|s| s.trim() != "").collect();
@ -18,25 +19,23 @@ pub fn lines(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
for s in split_result { for s in split_result {
result.push_back(ReturnValue::Value(Value::Primitive(Primitive::String( result.push_back(ReturnSuccess::value(
s.into(), Value::Primitive(Primitive::String(s.into())).spanned_unknown(),
)))); ));
} }
result result
} }
_ => { _ => {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
result.push_back(ReturnValue::Value(Value::Error(Box::new( result.push_back(Err(ShellError::maybe_labeled_error(
ShellError::maybe_labeled_error(
"Expected string values from pipeline", "Expected string values from pipeline",
"expects strings from pipeline", "expects strings from pipeline",
span, span,
), )));
))));
result result
} }
}) })
.flatten(); .flatten();
Ok(stream.boxed()) Ok(stream.to_output_stream())
} }

View File

@ -18,7 +18,7 @@ pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
_ => {} _ => {}
} }
match obj { match obj.item {
Value::Filesystem => { Value::Filesystem => {
let entries = std::fs::read_dir(&full_path); let entries = std::fs::read_dir(&full_path);
@ -44,10 +44,10 @@ pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut shell_entries = VecDeque::new(); let mut shell_entries = VecDeque::new();
for entry in entries { for entry in entries {
let value = Value::Object(dir_entry_dict(&entry?)?); let value = dir_entry_dict(&entry?, args.name_span)?;
shell_entries.push_back(ReturnValue::Value(value)) shell_entries.push_back(ReturnSuccess::value(value))
} }
Ok(shell_entries.boxed()) Ok(shell_entries.to_output_stream())
} }
_ => { _ => {
let mut entries = VecDeque::new(); let mut entries = VecDeque::new();
@ -97,7 +97,11 @@ pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
"Index not closed", "Index not closed",
format!("path missing closing ']'"), format!("path missing closing ']'"),
if args.len() > 0 { Some(args.nth(0).unwrap().span) } else { args.name_span }, if args.len() > 0 {
Some(args.nth(0).unwrap().span)
} else {
args.name_span
},
)) ))
} }
} }
@ -121,16 +125,19 @@ pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
} }
} }
match viewed { match viewed {
Value::List(l) => { Spanned {
item: Value::List(l),
..
} => {
for item in l { for item in l {
entries.push_back(ReturnValue::Value(item.copy())); entries.push_back(ReturnSuccess::value(item.clone()));
} }
} }
x => { x => {
entries.push_back(ReturnValue::Value(x.clone())); entries.push_back(ReturnSuccess::value(x.clone()));
} }
} }
Ok(entries.boxed()) Ok(entries.to_output_stream())
} }
} }
} }

386
src/commands/macros.rs Normal file
View File

@ -0,0 +1,386 @@
#[macro_export]
macro_rules! command {
(
Named { $export:tt $args:ident $body:block }
Positional { $($number:tt)* }
Rest {}
CommandConfig {
name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt,
named: {
$(
($named_param:tt : $named_type:tt)
)*
}
}
Function {
$( ( $param_name:tt : $param_type:tt ) )*
}
Extract {
$($extract:tt)*
}
) => {
#[allow(non_camel_case_types)]
pub struct $export;
impl Command for $export {
fn run(&self, $args: CommandArgs) -> Result<OutputStream, ShellError> {
fn command($args: CommandArgs, ( $($param_name),*, ): ( $($param_type),*, )) -> Result<OutputStream, ShellError> {
let output = $body;
Ok(output.boxed().to_output_stream())
}
let tuple = ( $($extract),*, );
command( $args, tuple )
}
fn name(&self) -> &str {
stringify!($config_name)
}
fn config(&self) -> $crate::parser::registry::CommandConfig {
$crate::parser::registry::CommandConfig {
name: self.name().to_string(),
positional: vec![$($mandatory_positional)*],
rest_positional: false,
is_filter: false,
is_sink: false,
named: {
use $crate::parser::registry::NamedType;
#[allow(unused_mut)]
let mut named: indexmap::IndexMap<String, NamedType> = indexmap::IndexMap::new();
$(
named.insert(stringify!($named_param).to_string(), NamedType::$named_type);
)*
named
}
}
}
}
};
// switch
(
Named { $export:tt $args:ident $body:block }
Positional { $($positional_count:tt)* }
Rest { , -- $param_name:ident : Switch $($rest:tt)* }
CommandConfig {
name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt,
named: {
$($config_named:tt)*
}
}
Function {
$($function:tt)*
}
Extract {
$($extract:tt)*
}
) => {
command!(
Named { $export $args $body }
Positional { $($positional_count)* + 1 }
Rest { $($rest)* }
CommandConfig {
name: $config_name,
mandatory_positional: vec![ $($mandatory_positional)* ],
optional_positional: vec![ $($optional_positional)* ],
rest_positional: $rest_positional,
named: {
$($config_named)*
($param_name : Switch)
}
}
Function {
$($function)* ($param_name : Switch)
}
Extract {
($($extract)* {
use std::convert::TryInto;
$args.get(stringify!($param_name)).clone().try_into()?
})
}
);
};
// mandatory named arguments
(
Named { $export:tt $args:ident $body:block }
Positional { $($positional_count:tt)* }
Rest { , -- $param_name:ident : $param_kind:tt $($rest:tt)* }
CommandConfig {
name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt,
named: {
$($config_named:tt)*
}
}
Function {
$($function:tt)*
}
Extract {
$($extract:tt)*
}
) => {
command!(
Named { $export $args $body }
Positional { $($positional_count)* + 1 }
Rest { $($rest)* }
CommandConfig {
name: $config_name,
mandatory_positional: vec![ $($mandatory_positional)* ],
optional_positional: vec![ $($optional_positional)* ],
rest_positional: $rest_positional,
named: {
$($config_named)*
($param_name : Mandatory(NamedValue::Single))
}
}
Function {
$($function)* ($param_name : $param_kind)
}
Extract {
($($extract)* {
use std::convert::TryInto;
$args.get(stringify!($param_name)).clone().try_into()?
})
}
);
};
// optional named arguments
(
Named { $export:tt $args:ident $body:block }
Positional { $($positional_count:tt)* }
Rest { , -- $param_name:ident ? : $param_kind:tt $($rest:tt)* }
CommandConfig {
name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt,
named: {
$($config_named:tt)*
}
}
Function {
$($function:tt)*
}
Extract {
$($extract:tt)*
}
) => {
command!(
Named { $export $args $body }
Positional { $($positional_count)* + 1 }
Rest { $($rest)* }
CommandConfig {
name: $config_name,
mandatory_positional: vec![ $($mandatory_positional)* ],
optional_positional: vec![ $($optional_positional)* ],
rest_positional: $rest_positional,
named: {
$($config_named)*
($param_name : Optional(NamedValue::Single))
}
}
Function {
$($function)* ($param_name : $param_kind)
}
Extract {
($($extract)* {
use std::convert::TryInto;
$args.get(stringify!($param_name)).clone().try_into()?
})
}
);
};
// mandatory positional block
(
Named { $export:ident $args:ident $body:block }
Positional { $($positional_count:tt)* }
Rest { , $param_name:ident : Block $($rest:tt)* }
CommandConfig {
name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt,
named: {
$($config_named:tt)*
}
}
Function {
$($function:tt)*
}
Extract {
$($extract:tt)*
}
) => {
command!(
Named { $export $args $body }
Positional { $($positional_count)* + 1 }
Rest { $($rest)* }
CommandConfig {
name: $config_name,
mandatory_positional: vec![ $($mandatory_positional)* $crate::parser::registry::PositionalType::mandatory_block(
stringify!($param_name)
), ],
optional_positional: vec![ $($optional_positional)* ],
rest_positional: $rest_positional,
named: {
$($config_named)*
}
}
Function {
$($function)* ($param_name : Block)
}
Extract {
$($extract:tt)* {
use $crate::object::types::ExtractType;
let value = $args.expect_nth($($positional_count)*)?;
Block::extract(value)?
}
}
);
};
// mandatory positional argument
(
Named { $export:ident $args:ident $body:block }
Positional { $($positional_count:tt)* }
Rest { , $param_name:ident : $param_kind:tt $($rest:tt)* }
CommandConfig {
name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt,
named: {
$($config_named:tt)*
}
}
Function {
$($function:tt)*
}
Extract {
$($extract:tt)*
}
) => {
command!(
Named { $export $args $body }
Positional { $($positional_count)* + 1 }
Rest { $($rest)* }
CommandConfig {
name: $config_name,
mandatory_positional: vec![ $($mandatory_positional)* $crate::parser::registry::PositionalType::mandatory(
stringify!($param_name)
), ],
optional_positional: vec![ $($optional_positional)* ],
rest_positional: $rest_positional,
named: {
$($config_named)*
}
}
Function {
$($function)* ($param_name : $param_kind)
}
Extract {
$($extract:tt)* {
use $crate::object::types::ExtractType;
let value = $args.expect_nth($($positional_count)*)?;
// let value = $param_kind.check(value)?;
$param_kind::extract(value)?
}
}
);
};
($export:ident as $config_name:tt ( $args:ident $($command_rest:tt)* ) $body:block) => {
command!(
Named { $export $args $body }
Positional { 0 }
Rest { $($command_rest)* }
CommandConfig {
name: $config_name,
mandatory_positional: vec![],
optional_positional: vec![],
rest_positional: false,
named: {}
}
Function {
}
Extract {
}
);
};
// ($export:ident as $name:tt ( $args:ident, -- $param:ident : $kind:ident ) $body:block) => {
// #[allow(non_camel_case_types)]
// pub struct $export;
// impl Command for $export {
// fn run(&self, $args: CommandArgs) -> Result<OutputStream, ShellError> {
// fn command($args: CommandArgs, $param: $kind) -> Result<OutputStream, ShellError> {
// $body
// }
// use std::convert::TryInto;
// let param = $args.get(stringify!($param)).try_into()?;
// command($args, param)
// }
// fn name(&self) -> &str {
// stringify!($name)
// }
// fn config(&self) -> CommandConfig {
// let mut named: IndexMap<String, NamedType> = IndexMap::new();
// named.insert(stringify!($param).to_string(), NamedType::$kind);
// CommandConfig {
// name: self.name().to_string(),
// mandatory_positional: vec![],
// optional_positional: vec![],
// rest_positional: false,
// named,
// }
// }
// }
// };
}

View File

@ -1,38 +1,80 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::{Primitive, Value}; use crate::object::{Primitive, Switch, Value};
use crate::parser::parse::span::Span; use crate::parser::parse::span::Span;
use crate::parser::registry::{CommandConfig, NamedType};
use crate::prelude::*; use crate::prelude::*;
use indexmap::IndexMap;
use mime::Mime; use mime::Mime;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
pub struct Open; command! {
Open as open(args, --raw: Switch) {
let span = args.name_span;
impl Command for Open { let cwd = args
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { .env
open(args) .lock()
} .unwrap()
fn name(&self) -> &str { .front()
"open" .unwrap()
} .path()
.to_path_buf();
fn config(&self) -> CommandConfig { let full_path = PathBuf::from(cwd);
let mut named: IndexMap<String, NamedType> = IndexMap::new();
named.insert("raw".to_string(), NamedType::Switch);
CommandConfig { let (file_extension, contents, contents_span) = match &args.expect_nth(0)?.item {
name: self.name().to_string(), Value::Primitive(Primitive::String(s)) => fetch(&full_path, s, args.expect_nth(0)?.span)?,
mandatory_positional: vec![], _ => {
optional_positional: vec![], return Err(ShellError::labeled_error(
rest_positional: false, "Expected string value for filename",
named, "expected filename",
is_filter: true, args.expect_nth(0)?.span,
is_sink: false, ));
can_load: vec![],
can_save: vec![],
} }
};
let mut stream = VecDeque::new();
let file_extension = if raw.is_present() {
None
} else if args.has("json") {
Some("json".to_string())
} else if args.has("xml") {
Some("xml".to_string())
} else if args.has("ini") {
Some("ini".to_string())
} else if args.has("yaml") {
Some("yaml".to_string())
} else if args.has("toml") {
Some("toml".to_string())
} else {
if let Some(ref named_args) = args.args.named {
for named in named_args.iter() {
return Err(ShellError::labeled_error(
"Unknown flag for open",
"unknown flag",
named.1.span.clone(),
));
}
file_extension
} else {
file_extension
}
};
match contents {
Value::Primitive(Primitive::String(string)) =>
stream.push_back(ReturnSuccess::value(parse_as_value(
file_extension,
string,
contents_span,
span,
)?)
),
other => stream.push_back(ReturnSuccess::value(other.spanned(span))),
};
stream
} }
} }
@ -40,7 +82,7 @@ pub fn fetch(
cwd: &PathBuf, cwd: &PathBuf,
location: &str, location: &str,
span: Span, span: Span,
) -> Result<(Option<String>, Value), ShellError> { ) -> Result<(Option<String>, Value, Span), ShellError> {
let mut cwd = cwd.clone(); let mut cwd = cwd.clone();
if location.starts_with("http:") || location.starts_with("https:") { if location.starts_with("http:") || location.starts_with("https:") {
let response = reqwest::get(location); let response = reqwest::get(location);
@ -71,7 +113,7 @@ pub fn fetch(
None => path_extension, None => path_extension,
}; };
Ok((extension, Value::string(s))) Ok((extension, Value::string(s), span))
} }
Err(_) => { Err(_) => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
@ -97,8 +139,9 @@ pub fn fetch(
cwd.extension() cwd.extension()
.map(|name| name.to_string_lossy().to_string()), .map(|name| name.to_string_lossy().to_string()),
Value::string(s), Value::string(s),
span,
)), )),
Err(_) => Ok((None, Value::Binary(bytes))), Err(_) => Ok((None, Value::Binary(bytes), span)),
}, },
Err(_) => { Err(_) => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
@ -114,128 +157,76 @@ pub fn fetch(
pub fn parse_as_value( pub fn parse_as_value(
extension: Option<String>, extension: Option<String>,
contents: String, contents: String,
contents_span: Span,
name_span: Option<Span>, name_span: Option<Span>,
) -> Result<Value, ShellError> { ) -> Result<Spanned<Value>, ShellError> {
match extension { match extension {
Some(x) if x == "toml" => crate::commands::from_toml::from_toml_string_to_value(contents) Some(x) if x == "toml" => {
crate::commands::from_toml::from_toml_string_to_value(contents, contents_span)
.map(|c| c.spanned(contents_span))
.map_err(move |_| { .map_err(move |_| {
ShellError::maybe_labeled_error( ShellError::maybe_labeled_error(
"Could not open as TOML", "Could not open as TOML",
"could not open as TOML", "could not open as TOML",
name_span, name_span,
) )
}), })
Some(x) if x == "json" => crate::commands::from_json::from_json_string_to_value(contents) }
Some(x) if x == "json" => {
crate::commands::from_json::from_json_string_to_value(contents, contents_span)
.map(|c| c.spanned(contents_span))
.map_err(move |_| { .map_err(move |_| {
ShellError::maybe_labeled_error( ShellError::maybe_labeled_error(
"Could not open as JSON", "Could not open as JSON",
"could not open as JSON", "could not open as JSON",
name_span, name_span,
) )
}), })
Some(x) if x == "ini" => crate::commands::from_ini::from_ini_string_to_value(contents) }
Some(x) if x == "ini" => {
crate::commands::from_ini::from_ini_string_to_value(contents, contents_span)
.map(|c| c.spanned(contents_span))
.map_err(move |_| { .map_err(move |_| {
ShellError::maybe_labeled_error( ShellError::maybe_labeled_error(
"Could not open as INI", "Could not open as INI",
"could not open as INI", "could not open as INI",
name_span, name_span,
) )
}), })
Some(x) if x == "xml" => crate::commands::from_xml::from_xml_string_to_value(contents) }
.map_err(move |_| { Some(x) if x == "xml" => {
crate::commands::from_xml::from_xml_string_to_value(contents, contents_span).map_err(
move |_| {
ShellError::maybe_labeled_error( ShellError::maybe_labeled_error(
"Could not open as XML", "Could not open as XML",
"could not open as XML", "could not open as XML",
name_span, name_span,
) )
}), },
Some(x) if x == "yml" => crate::commands::from_yaml::from_yaml_string_to_value(contents) )
.map_err(move |_| { }
Some(x) if x == "yml" => {
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_span).map_err(
move |_| {
ShellError::maybe_labeled_error( ShellError::maybe_labeled_error(
"Could not open as YAML", "Could not open as YAML",
"could not open as YAML", "could not open as YAML",
name_span, name_span,
) )
}), },
Some(x) if x == "yaml" => crate::commands::from_yaml::from_yaml_string_to_value(contents) )
.map_err(move |_| { }
Some(x) if x == "yaml" => {
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_span).map_err(
move |_| {
ShellError::maybe_labeled_error( ShellError::maybe_labeled_error(
"Could not open as YAML", "Could not open as YAML",
"could not open as YAML", "could not open as YAML",
name_span, name_span,
) )
}), },
_ => Ok(Value::string(contents)), )
}
_ => Ok(Value::string(contents).spanned(contents_span)),
} }
} }
fn open(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.len() == 0 {
return Err(ShellError::maybe_labeled_error(
"Open requires a path or url",
"needs path or url",
args.name_span,
));
}
let span = args.name_span;
let cwd = args
.env
.lock()
.unwrap()
.front()
.unwrap()
.path()
.to_path_buf();
let full_path = PathBuf::from(cwd);
let (file_extension, contents) = 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(
"Expected string value for filename",
"expected filename",
args.expect_nth(0)?.span,
));
}
};
let mut stream = VecDeque::new();
let file_extension = if args.has("raw") {
None
} else if args.has("json") {
Some("json".to_string())
} else if args.has("xml") {
Some("xml".to_string())
} else if args.has("ini") {
Some("ini".to_string())
} else if args.has("yaml") {
Some("yaml".to_string())
} else if args.has("toml") {
Some("toml".to_string())
} else {
if let Some(ref named_args) = args.args.named {
for named in named_args.iter() {
return Err(ShellError::labeled_error(
"Unknown flag for open",
"unknown flag",
named.1.span.clone(),
));
}
file_extension
} else {
file_extension
}
};
match contents {
Value::Primitive(Primitive::String(x)) => {
stream.push_back(ReturnValue::Value(parse_as_value(file_extension, x, span)?));
}
x => stream.push_back(ReturnValue::Value(x)),
}
Ok(stream.boxed())
}

View File

@ -1,6 +1,5 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::base::select_fields; use crate::object::base::select_fields;
use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
pub fn pick(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn pick(args: CommandArgs) -> Result<OutputStream, ShellError> {
@ -14,12 +13,11 @@ pub fn pick(args: CommandArgs) -> Result<OutputStream, ShellError> {
let fields: Result<Vec<String>, _> = args.positional_iter().map(|a| a.as_string()).collect(); let fields: Result<Vec<String>, _> = args.positional_iter().map(|a| a.as_string()).collect();
let fields = fields?; let fields = fields?;
let input = args.input;
let objects = args let objects = input
.input .values
.map(move |item| Value::Object(select_fields(&item, &fields))) .map(move |value| select_fields(&value.item, &fields, value.span));
.map(|item| ReturnValue::Value(item));
let stream = Pin::new(Box::new(objects)); Ok(objects.from_input_stream())
Ok(stream)
} }

View File

@ -43,21 +43,46 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, Sh
let stdin = child.stdin.as_mut().expect("Failed to open stdin"); let stdin = child.stdin.as_mut().expect("Failed to open stdin");
let stdout = child.stdout.as_mut().expect("Failed to open stdout"); let stdout = child.stdout.as_mut().expect("Failed to open stdout");
let _reader = BufReader::new(stdout); let mut reader = BufReader::new(stdout);
let request = JsonRpc::new("begin_filter", args.args); let request = JsonRpc::new("begin_filter", args.args);
let request_raw = serde_json::to_string(&request).unwrap(); let request_raw = serde_json::to_string(&request).unwrap();
stdin.write(format!("{}\n", request_raw).as_bytes())?; stdin.write(format!("{}\n", request_raw).as_bytes())?;
let mut input = String::new();
match reader.read_line(&mut input) {
Ok(_) => {
let response = serde_json::from_str::<NuResult>(&input);
match response {
Ok(NuResult::response { params }) => match params {
Ok(_) => {}
Err(e) => {
return Err(e);
}
},
Err(e) => {
return Err(ShellError::string(format!(
"Error while processing input: {:?} {}",
e, input
)));
}
}
}
_ => {}
}
} }
let mut eos = VecDeque::new(); let mut eos: VecDeque<Spanned<Value>> = VecDeque::new();
eos.push_back(Value::Primitive(Primitive::EndOfStream)); eos.push_back(Value::Primitive(Primitive::EndOfStream).spanned_unknown());
let stream = args let stream = args
.input .input
.values
.chain(eos) .chain(eos)
.map(move |v| match v { .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 stdin = child.stdin.as_mut().expect("Failed to open stdin");
let stdout = child.stdout.as_mut().expect("Failed to open stdout"); let stdout = child.stdout.as_mut().expect("Failed to open stdout");
@ -87,17 +112,15 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, Sh
Ok(params) => params, Ok(params) => params,
Err(e) => { Err(e) => {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
result.push_back(ReturnValue::Value(Value::Error(Box::new(e)))); result.push_back(ReturnValue::Err(e));
result result
} }
}, },
Err(e) => { Err(e) => {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
result.push_back(ReturnValue::Value(Value::Error(Box::new( result.push_back(Err(ShellError::string(format!(
ShellError::string(format!(
"Error while processing input: {:?} {}", "Error while processing input: {:?} {}",
e, input e, input
)),
)))); ))));
result result
} }
@ -105,8 +128,9 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, Sh
} }
Err(e) => { Err(e) => {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
result.push_back(ReturnValue::Value(Value::Error(Box::new( result.push_back(Err(ShellError::string(format!(
ShellError::string(format!("Error while processing input: {:?}", e)), "Error while processing input: {:?}",
e
)))); ))));
result result
} }
@ -115,7 +139,7 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, Sh
}) })
.flatten(); .flatten();
Ok(stream.boxed()) Ok(stream.to_output_stream())
} }
pub fn sink_plugin(path: String, args: SinkCommandArgs) -> Result<(), ShellError> { pub fn sink_plugin(path: String, args: SinkCommandArgs) -> Result<(), ShellError> {

View File

@ -1,18 +1,17 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::process::process_dict; use crate::object::process::process_dict;
use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
use sysinfo::{RefreshKind, SystemExt}; use sysinfo::{RefreshKind, SystemExt};
pub fn ps(_args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn ps(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut system = sysinfo::System::new_with_specifics(RefreshKind::new().with_processes()); let mut system = sysinfo::System::new_with_specifics(RefreshKind::new().with_processes());
system.refresh_processes(); system.refresh_processes();
let list = system.get_process_list(); let list = system.get_process_list();
let list = list let list = list
.into_iter() .into_iter()
.map(|(_, process)| ReturnValue::Value(Value::Object(process_dict(process)))) .map(|(_, process)| process_dict(process, args.name_span))
.collect::<VecDeque<_>>(); .collect::<VecDeque<_>>();
Ok(list.boxed()) Ok(list.from_input_stream())
} }

View File

@ -1,9 +1,10 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::base::reject_fields; use crate::object::base::reject_fields;
use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
pub fn reject(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn reject(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.name_span;
if args.len() == 0 { if args.len() == 0 {
return Err(ShellError::maybe_labeled_error( return Err(ShellError::maybe_labeled_error(
"Reject requires fields", "Reject requires fields",
@ -15,10 +16,11 @@ pub fn reject(args: CommandArgs) -> Result<OutputStream, ShellError> {
let fields: Result<Vec<String>, _> = args.positional_iter().map(|a| a.as_string()).collect(); let fields: Result<Vec<String>, _> = args.positional_iter().map(|a| a.as_string()).collect();
let fields = fields?; let fields = fields?;
let stream = args let stream = args.input.values.map(move |item| {
.input reject_fields(&item, &fields, item.span)
.map(move |item| Value::Object(reject_fields(&item, &fields))) .into_spanned_value()
.map(|item| ReturnValue::Value(item)); .spanned(name_span)
});
Ok(stream.boxed()) Ok(stream.from_input_stream())
} }

View File

@ -1,6 +1,5 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::dict::Dictionary; use crate::object::{SpannedDictBuilder, Value};
use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
@ -24,20 +23,20 @@ pub fn size(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut contents = String::new(); let mut contents = String::new();
let mut list = VecDeque::new(); let mut list: VecDeque<ReturnValue> = VecDeque::new();
for name in args.positional_iter() { for spanned_name in args.positional_iter() {
let name = name.as_string()?; let name = spanned_name.as_string()?;
let path = cwd.join(&name); let path = cwd.join(&name);
let mut file = File::open(path)?; let mut file = File::open(path)?;
file.read_to_string(&mut contents)?; file.read_to_string(&mut contents)?;
list.push_back(count(&name, &contents)); list.push_back(count(&name, &contents, spanned_name).into());
contents.clear(); contents.clear();
} }
Ok(list.boxed()) Ok(list.to_output_stream())
} }
fn count(name: &str, contents: &str) -> ReturnValue { fn count(name: &str, contents: &str, span: impl Into<Span>) -> Spanned<Value> {
let mut lines: i64 = 0; let mut lines: i64 = 0;
let mut words: i64 = 0; let mut words: i64 = 0;
let mut chars: i64 = 0; let mut chars: i64 = 0;
@ -62,12 +61,12 @@ fn count(name: &str, contents: &str) -> ReturnValue {
} }
} }
let mut dict = Dictionary::default(); let mut dict = SpannedDictBuilder::new(span);
dict.add("name", Value::string(name)); dict.insert("name", Value::string(name));
dict.add("lines", Value::int(lines)); dict.insert("lines", Value::int(lines));
dict.add("words", Value::int(words)); dict.insert("words", Value::int(words));
dict.add("chars", Value::int(chars)); dict.insert("chars", Value::int(chars));
dict.add("max length", Value::int(bytes)); dict.insert("max length", Value::int(bytes));
ReturnValue::Value(Value::Object(dict)) dict.into_spanned_value()
} }

View File

@ -16,14 +16,11 @@ impl Command for SkipWhile {
fn config(&self) -> CommandConfig { fn config(&self) -> CommandConfig {
CommandConfig { CommandConfig {
name: self.name().to_string(), name: self.name().to_string(),
mandatory_positional: vec![PositionalType::Block("condition".to_string())], positional: vec![PositionalType::mandatory_block("condition")],
optional_positional: vec![],
rest_positional: false, rest_positional: false,
named: indexmap::IndexMap::new(), named: indexmap::IndexMap::new(),
is_filter: true, is_filter: true,
is_sink: false, is_sink: false,
can_load: vec![],
can_save: vec![],
} }
} }
} }
@ -40,7 +37,7 @@ pub fn skip_while(args: CommandArgs) -> Result<OutputStream, ShellError> {
let block = args.nth(0).unwrap().as_block()?; let block = args.nth(0).unwrap().as_block()?;
let input = args.input; let input = args.input;
let objects = input.skip_while(move |item| { let objects = input.values.skip_while(move |item| {
let result = block.invoke(&item); let result = block.invoke(&item);
let return_value = match result { let return_value = match result {
@ -51,5 +48,5 @@ pub fn skip_while(args: CommandArgs) -> Result<OutputStream, ShellError> {
futures::future::ready(return_value) futures::future::ready(return_value)
}); });
Ok(objects.map(|x| ReturnValue::Value(x)).boxed()) Ok(objects.from_input_stream())
} }

View File

@ -5,21 +5,18 @@ pub fn sort_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
let fields: Result<Vec<_>, _> = args.positional_iter().map(|a| a.as_string()).collect(); let fields: Result<Vec<_>, _> = args.positional_iter().map(|a| a.as_string()).collect();
let fields = fields?; let fields = fields?;
let output = args.input.collect::<Vec<_>>(); let output = args.input.values.collect::<Vec<_>>();
let output = output.map(move |mut vec| { let output = output.map(move |mut vec| {
vec.sort_by_key(|item| { vec.sort_by_key(|item| {
fields fields
.iter() .iter()
.map(|f| item.get_data_by_key(f).map(|i| i.copy())) .map(|f| item.get_data_by_key(f).map(|i| i.clone()))
.collect::<Vec<Option<Value>>>() .collect::<Vec<Option<Spanned<Value>>>>()
}); });
vec.into_iter() vec.into_iter().collect::<VecDeque<_>>()
.map(|v| ReturnValue::Value(v.copy()))
.collect::<VecDeque<_>>()
.boxed()
}); });
Ok(output.flatten_stream().boxed()) Ok(output.flatten_stream().from_input_stream())
} }

View File

@ -1,5 +1,5 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::{Primitive, Value}; use crate::object::{Primitive, SpannedDictBuilder, Value};
use crate::prelude::*; use crate::prelude::*;
use log::trace; use log::trace;
@ -20,7 +20,8 @@ pub fn split_column(args: CommandArgs) -> Result<OutputStream, ShellError> {
let input = args.input; let input = args.input;
Ok(input Ok(input
.map(move |v| match v { .values
.map(move |v| match v.item {
Value::Primitive(Primitive::String(s)) => { Value::Primitive(Primitive::String(s)) => {
let splitter = positional[0].as_string().unwrap().replace("\\n", "\n"); let splitter = positional[0].as_string().unwrap().replace("\\n", "\n");
trace!("splitting with {:?}", splitter); trace!("splitting with {:?}", splitter);
@ -35,36 +36,34 @@ pub fn split_column(args: CommandArgs) -> Result<OutputStream, ShellError> {
gen_columns.push(format!("Column{}", i + 1)); gen_columns.push(format!("Column{}", i + 1));
} }
let mut dict = crate::object::Dictionary::default(); let mut dict = SpannedDictBuilder::new(v.span);
for (&k, v) in split_result.iter().zip(gen_columns.iter()) { for (&k, v) in split_result.iter().zip(gen_columns.iter()) {
dict.add(v.clone(), Value::Primitive(Primitive::String(k.into()))); dict.insert(v.clone(), Primitive::String(k.into()));
} }
ReturnValue::Value(Value::Object(dict))
ReturnSuccess::value(dict.into_spanned_value())
} else if split_result.len() == (positional.len() - 1) { } else if split_result.len() == (positional.len() - 1) {
let mut dict = crate::object::Dictionary::default(); let mut dict = SpannedDictBuilder::new(v.span);
for (&k, v) in split_result.iter().zip(positional.iter().skip(1)) { for (&k, v) in split_result.iter().zip(positional.iter().skip(1)) {
dict.add( dict.insert(
v.as_string().unwrap(), v.as_string().unwrap(),
Value::Primitive(Primitive::String(k.into())), Value::Primitive(Primitive::String(k.into())),
); );
} }
ReturnValue::Value(Value::Object(dict)) ReturnSuccess::value(dict.into_spanned_value())
} else { } else {
let mut dict = crate::object::Dictionary::default(); let mut dict = SpannedDictBuilder::new(v.span);
for k in positional.iter().skip(1) { for k in positional.iter().skip(1) {
dict.add( dict.insert(k.as_string().unwrap().trim(), Primitive::String("".into()));
k.as_string().unwrap().trim(),
Value::Primitive(Primitive::String("".into())),
);
} }
ReturnValue::Value(Value::Object(dict)) ReturnSuccess::value(dict.into_spanned_value())
} }
} }
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( _ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline", "Expected string values from pipeline",
"expects strings from pipeline", "expects strings from pipeline",
span, span,
)))), )),
}) })
.boxed()) .to_output_stream())
} }

View File

@ -21,7 +21,8 @@ pub fn split_row(args: CommandArgs) -> Result<OutputStream, ShellError> {
let input = args.input; let input = args.input;
let stream = input let stream = input
.map(move |v| match v { .values
.map(move |v| match v.item {
Value::Primitive(Primitive::String(s)) => { Value::Primitive(Primitive::String(s)) => {
let splitter = positional[0].as_string().unwrap().replace("\\n", "\n"); let splitter = positional[0].as_string().unwrap().replace("\\n", "\n");
trace!("splitting with {:?}", splitter); trace!("splitting with {:?}", splitter);
@ -31,25 +32,23 @@ pub fn split_row(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
for s in split_result { for s in split_result {
result.push_back(ReturnValue::Value(Value::Primitive(Primitive::String( result.push_back(ReturnSuccess::value(
s.into(), Value::Primitive(Primitive::String(s.into())).spanned(v.span),
)))); ));
} }
result result
} }
_ => { _ => {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
result.push_back(ReturnValue::Value(Value::Error(Box::new( result.push_back(Err(ShellError::maybe_labeled_error(
ShellError::maybe_labeled_error(
"Expected string values from pipeline", "Expected string values from pipeline",
"expects strings from pipeline", "expects strings from pipeline",
span, span,
), )));
))));
result result
} }
}) })
.flatten(); .flatten();
Ok(stream.boxed()) Ok(stream.to_output_stream())
} }

View File

@ -1,95 +1,52 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::base::OF64; use crate::object::base::OF64;
use crate::object::Dictionary; use crate::object::SpannedDictBuilder;
use crate::object::{Primitive, Value}; use crate::object::{Primitive, Value};
use crate::prelude::*; use crate::prelude::*;
use sys_info::*; use sys_info::*;
use sysinfo::{ComponentExt, DiskExt, NetworkExt, RefreshKind, SystemExt}; use sysinfo::{ComponentExt, DiskExt, NetworkExt, RefreshKind, SystemExt};
pub fn sysinfo(_args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn sysinfo(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut idx = indexmap::IndexMap::new(); let mut idx = SpannedDictBuilder::new(args.name_span);
if let (Ok(name), Ok(version)) = (os_type(), os_release()) { if let (Ok(name), Ok(version)) = (os_type(), os_release()) {
let mut os_idx = indexmap::IndexMap::new(); let mut os_idx = SpannedDictBuilder::new(args.name_span);
os_idx.insert( os_idx.insert("name", Primitive::String(name));
"name".to_string(), os_idx.insert("version", Primitive::String(version));
Value::Primitive(Primitive::String(name)),
);
os_idx.insert(
"version".to_string(),
Value::Primitive(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()) { if let (Ok(num_cpu), Ok(cpu_speed)) = (cpu_num(), cpu_speed()) {
let mut cpu_idx = indexmap::IndexMap::new(); let mut cpu_idx = SpannedDictBuilder::new(args.name_span);
cpu_idx.insert( cpu_idx.insert("num", Primitive::Int(num_cpu as i64));
"num".to_string(), cpu_idx.insert("speed", Primitive::Int(cpu_speed as i64));
Value::Primitive(Primitive::Int(num_cpu as i64)),
);
cpu_idx.insert(
"speed".to_string(),
Value::Primitive(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() { if let Ok(x) = loadavg() {
let mut load_idx = indexmap::IndexMap::new(); let mut load_idx = SpannedDictBuilder::new(args.name_span);
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))),
);
idx.insert( load_idx.insert("1min", Primitive::Float(OF64::from(x.one)));
"load avg".to_string(), load_idx.insert("5min", Primitive::Float(OF64::from(x.five)));
Value::Object(Dictionary::from(load_idx)), load_idx.insert("15min", Primitive::Float(OF64::from(x.fifteen)));
);
idx.insert_spanned("load avg", load_idx);
} }
if let Ok(x) = mem_info() { if let Ok(x) = mem_info() {
let mut mem_idx = indexmap::IndexMap::new(); let mut mem_idx = SpannedDictBuilder::new(args.name_span);
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)),
);
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 +64,42 @@ pub fn sysinfo(_args: CommandArgs) -> Result<OutputStream, ShellError> {
*/ */
if let Ok(x) = hostname() { if let Ok(x) = hostname() {
idx.insert( idx.insert("hostname", Primitive::String(x));
"hostname".to_string(),
Value::Primitive(Primitive::String(x)),
);
} }
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
if let Ok(x) = boottime() { if let Ok(x) = boottime() {
let mut boottime_idx = indexmap::IndexMap::new(); let mut boottime_idx = SpannedDictBuilder::new(args.name_span);
boottime_idx.insert( boottime_idx.insert("days", Primitive::Int(x.tv_sec / (24 * 3600)));
"days".to_string(), boottime_idx.insert("hours", Primitive::Int((x.tv_sec / 3600) % 24));
Value::Primitive(Primitive::Int(x.tv_sec / (24 * 3600))), boottime_idx.insert("mins", Primitive::Int((x.tv_sec / 60) % 60));
);
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)),
);
idx.insert( idx.insert_spanned("uptime", boottime_idx);
"uptime".to_string(),
Value::Object(Dictionary::from(boottime_idx)),
);
} }
} }
let system = sysinfo::System::new_with_specifics(RefreshKind::everything().without_processes()); let system = sysinfo::System::new_with_specifics(RefreshKind::everything().without_processes());
let components_list = system.get_components_list(); let components_list = system.get_components_list();
if components_list.len() > 0 { if components_list.len() > 0 {
let mut v = vec![]; let mut v: Vec<Spanned<Value>> = vec![];
for component in components_list { 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( component_idx.insert(
"name".to_string(), "temp",
Value::string(component.get_label().to_string()), Primitive::Float(OF64::from(component.get_temperature() as f64)),
); );
component_idx.insert( component_idx.insert(
"temp".to_string(), "max",
Value::float(component.get_temperature() as f64), 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() { 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(); let disks = system.get_disks();
@ -165,38 +107,26 @@ pub fn sysinfo(_args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut v = vec![]; let mut v = vec![];
for disk in disks { for disk in disks {
let mut disk_idx = indexmap::IndexMap::new(); let mut disk_idx = SpannedDictBuilder::new(args.name_span);
disk_idx.insert( disk_idx.insert("name", Value::string(disk.get_name().to_string_lossy()));
"name".to_string(), disk_idx.insert("available", Value::bytes(disk.get_available_space()));
Value::string(disk.get_name().to_string_lossy()), disk_idx.insert("total", Value::bytes(disk.get_total_space()));
); v.push(disk_idx.into());
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)));
} }
idx.insert("disks".to_string(), Value::List(v)); idx.insert("disks", Value::List(v));
} }
let network = system.get_network(); let network = system.get_network();
let incoming = network.get_income(); let incoming = network.get_income();
let outgoing = network.get_outcome(); let outgoing = network.get_outcome();
let mut network_idx = indexmap::IndexMap::new(); let mut network_idx = SpannedDictBuilder::new(args.name_span);
network_idx.insert("incoming".to_string(), Value::bytes(incoming)); network_idx.insert("incoming", Value::bytes(incoming));
network_idx.insert("outgoing".to_string(), Value::bytes(outgoing)); network_idx.insert("outgoing", Value::bytes(outgoing));
idx.insert( idx.insert_spanned("network", network_idx);
"network".to_string(),
Value::Object(Dictionary::from(network_idx)),
);
// println!("{:#?}", system.get_network()); let stream = stream![idx.into_spanned_value()];
let mut stream = VecDeque::new(); Ok(stream.from_input_stream())
stream.push_back(ReturnValue::Value(Value::Object(Dictionary::from(idx))));
Ok(stream.boxed())
} }

View File

@ -2,9 +2,10 @@ use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
pub fn to_array(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn to_array(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input.collect(); let out = args.input.values.collect();
Ok(out Ok(out
.map(|vec: Vec<_>| single_output(Value::List(vec))) .map(|vec: Vec<_>| stream![Value::List(vec).spanned_unknown()]) // TODO: args.input should have a span
.flatten_stream() .flatten_stream()
.boxed()) .from_input_stream())
} }

View File

@ -22,7 +22,6 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value {
Value::List(l) => { Value::List(l) => {
serde_json::Value::Array(l.iter().map(|x| value_to_json_value(x)).collect()) serde_json::Value::Array(l.iter().map(|x| value_to_json_value(x)).collect())
} }
Value::Error(e) => serde_json::Value::String(e.to_string()),
Value::Block(_) => serde_json::Value::Null, Value::Block(_) => serde_json::Value::Null,
Value::Binary(b) => serde_json::Value::Array( Value::Binary(b) => serde_json::Value::Array(
b.iter() b.iter()
@ -43,19 +42,20 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value {
pub fn to_json(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn to_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let name_span = args.name_span;
Ok(out Ok(out
.values
.map( .map(
move |a| match serde_json::to_string(&value_to_json_value(&a)) { move |a| match serde_json::to_string(&value_to_json_value(&a)) {
Ok(x) => ReturnValue::Value(Value::Primitive(Primitive::String(x))), Ok(x) => {
Err(_) => { ReturnSuccess::value(Value::Primitive(Primitive::String(x)).spanned(name_span))
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( }
Err(_) => Err(ShellError::maybe_labeled_error(
"Can not convert to JSON string", "Can not convert to JSON string",
"can not convert piped data to JSON string", "can not convert piped data to JSON string",
span, name_span,
)))) )),
}
}, },
) )
.boxed()) .to_output_stream())
} }

View File

@ -16,7 +16,6 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value {
Value::Filesystem => toml::Value::String("<Filesystem>".to_string()), Value::Filesystem => toml::Value::String("<Filesystem>".to_string()),
Value::List(l) => toml::Value::Array(l.iter().map(|x| value_to_toml_value(x)).collect()), Value::List(l) => toml::Value::Array(l.iter().map(|x| value_to_toml_value(x)).collect()),
Value::Error(e) => toml::Value::String(e.to_string()),
Value::Block(_) => toml::Value::String("<Block>".to_string()), Value::Block(_) => toml::Value::String("<Block>".to_string()),
Value::Binary(b) => { Value::Binary(b) => {
toml::Value::Array(b.iter().map(|x| toml::Value::Integer(*x as i64)).collect()) toml::Value::Array(b.iter().map(|x| toml::Value::Integer(*x as i64)).collect())
@ -33,15 +32,40 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value {
pub fn to_toml(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn to_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input; let out = args.input;
let span = args.name_span; let name_span = args.name_span;
Ok(out Ok(out
.map(move |a| match toml::to_string(&value_to_toml_value(&a)) { .values
Ok(x) => ReturnValue::Value(Value::Primitive(Primitive::String(x))), .map(move |a| {
Err(_) => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error( match toml::to_string(&value_to_toml_value(&a)) {
"Can not convert to TOML string", Ok(val) => {
"can not convert piped data to TOML string", return ReturnSuccess::value(
span, Value::Primitive(Primitive::String(val)).spanned(name_span),
)))), )
}
Err(err) => Err(ShellError::type_error(
"serializable to toml",
format!("{:?} - {:?}", a.type_name(), err).spanned(name_span),
)), // toml::Value::String(String) => {
// return ReturnSuccess::value(
// Value::Primitive(Primitive::String(x)).spanned(name_span),
// )
// }
// toml::Value::Integer(i64) => "Integer",
// toml::Value::Float(f64) => "Decimal",
// toml::Value::Boolean(bool) => "Boolean",
// toml::Value::Datetime(Datetime) => "Date",
// toml::Value::Array(Array) => "Array",
// toml::Value::Table(Table) => "Table",
}
// return Err(ShellError::type_error("String", ty.spanned(name_span)));
// Err(_) => Err(ShellError::maybe_labeled_error(
// "Can not convert to TOML string",
// "can not convert piped data to TOML string",
// name_span,
// )),
}) })
.boxed()) .to_output_stream())
} }

View File

@ -1,23 +1,17 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::{Primitive, Value}; use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
// TODO: "Amount remaining" wrapper // TODO: "Amount remaining" wrapper
pub fn trim(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn trim(args: CommandArgs) -> Result<OutputStream, ShellError> {
let input = args.input; let input = args.input;
let span = args.name_span;
Ok(input Ok(input
.map(move |v| match v { .values
Value::Primitive(Primitive::String(s)) => { .map(move |v| {
ReturnValue::Value(Value::Primitive(Primitive::String(s.trim().into()))) let string = String::extract(&v)?;
} ReturnSuccess::value(Value::string(string.trim()).spanned(v.span))
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
span,
)))),
}) })
.boxed()) .to_output_stream())
} }

View File

@ -46,5 +46,5 @@ pub fn view(args: CommandArgs) -> Result<OutputStream, ShellError> {
let _ = printer.file(file.display().to_string()); let _ = printer.file(file.display().to_string());
Ok(VecDeque::new().boxed()) Ok(OutputStream::empty())
} }

View File

@ -1,55 +1,23 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::registry::{CommandConfig, PositionalType}; use crate::object::Block;
use crate::prelude::*; use crate::prelude::*;
use futures::future::ready;
use log::trace;
pub struct Where; command! {
Where as where(args, condition: Block) {
let input: InputStream = trace_stream!(target: "nu::trace_stream::where", "where input" = args.input);
impl Command for Where { input.values.filter_map(move |item| {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { let result = condition.invoke(&item);
r#where(args)
}
fn name(&self) -> &str {
"where"
}
fn config(&self) -> CommandConfig {
CommandConfig {
name: self.name().to_string(),
mandatory_positional: vec![PositionalType::Block("condition".to_string())],
optional_positional: vec![],
rest_positional: false,
named: indexmap::IndexMap::new(),
is_filter: true,
is_sink: false,
can_load: vec![],
can_save: vec![],
}
}
}
pub fn r#where(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.len() == 0 {
return Err(ShellError::maybe_labeled_error(
"Where requires a condition",
"needs condition",
args.name_span,
));
}
let block = args.expect_nth(0)?.as_block()?;
let input = args.input;
let objects = input.filter_map(move |item| {
let result = block.invoke(&item);
let return_value = match result { let return_value = match result {
Err(err) => Some(ReturnValue::Value(Value::Error(Box::new(err)))), Err(err) => Some(Err(err)),
Ok(v) if v.is_true() => Some(ReturnValue::Value(item.copy())), Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))),
_ => None, _ => None,
}; };
futures::future::ready(return_value) ready(return_value)
}); })
}
Ok(objects.boxed())
} }

View File

@ -54,7 +54,7 @@ impl Context {
command: Arc<dyn Sink>, command: Arc<dyn Sink>,
name_span: Option<Span>, name_span: Option<Span>,
args: Args, args: Args,
input: Vec<Value>, input: Vec<Spanned<Value>>,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
let command_args = SinkCommandArgs { let command_args = SinkCommandArgs {
ctx: self.clone(), ctx: self.clone(),

View File

@ -1,9 +1,10 @@
use crate::object::base::Value; use crate::object::base::Value;
use crate::prelude::*;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Environment { pub struct Environment {
crate obj: Value, crate obj: Spanned<Value>,
crate path: PathBuf, crate path: PathBuf,
} }
@ -12,7 +13,7 @@ impl Environment {
let path = std::env::current_dir()?; let path = std::env::current_dir()?;
Ok(Environment { Ok(Environment {
obj: Value::Filesystem, obj: Value::Filesystem.spanned_unknown(),
path, path,
}) })
} }
@ -21,7 +22,7 @@ impl Environment {
self.path.as_path() self.path.as_path()
} }
pub fn obj(&self) -> &Value { pub fn obj(&self) -> &Spanned<Value> {
&self.obj &self.obj
} }
} }

View File

@ -5,7 +5,7 @@ use crate::parser::{Span, Spanned};
use ansi_term::Color; use ansi_term::Color;
use derive_new::new; use derive_new::new;
use language_reporting::{Diagnostic, Label, Severity}; use language_reporting::{Diagnostic, Label, Severity};
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
pub enum Description { pub enum Description {
@ -41,26 +41,20 @@ pub enum ArgumentError {
MissingValueForName(String), MissingValueForName(String),
} }
pub fn labelled(
span: impl Into<Option<Span>>,
heading: &'a str,
span_message: &'a str,
) -> impl FnOnce(ShellError) -> ShellError + 'a {
let span = span.into();
move |_| ShellError::maybe_labeled_error(heading, span_message, span)
}
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
pub enum ShellError { pub struct ShellError {
String(StringError), error: ProximateShellError,
TypeError { cause: Option<Box<ProximateShellError>>,
expected: String,
actual: Spanned<Option<String>>,
},
MissingProperty {
subpath: Description,
expr: Description,
},
ArgumentError {
error: ArgumentError,
span: Span,
},
Diagnostic(ShellDiagnostic),
CoerceError {
left: Spanned<String>,
right: Spanned<String>,
},
} }
impl ShellError { impl ShellError {
@ -68,10 +62,39 @@ impl ShellError {
expected: impl Into<String>, expected: impl Into<String>,
actual: Spanned<impl Into<String>>, actual: Spanned<impl Into<String>>,
) -> ShellError { ) -> ShellError {
ShellError::TypeError { ProximateShellError::TypeError {
expected: expected.into(), expected: expected.into(),
actual: actual.map(|i| Some(i.into())), actual: actual.map(|i| Some(i.into())),
} }
.start()
}
crate fn coerce_error(
left: Spanned<impl Into<String>>,
right: Spanned<impl Into<String>>,
) -> ShellError {
ProximateShellError::CoerceError {
left: left.map(|l| l.into()),
right: right.map(|r| r.into()),
}
.start()
}
crate fn missing_property(subpath: Description, expr: Description) -> ShellError {
ProximateShellError::MissingProperty { subpath, expr }.start()
}
crate fn argument_error(
command: impl Into<String>,
kind: ArgumentError,
span: Span,
) -> ShellError {
ProximateShellError::ArgumentError {
command: command.into(),
error: kind,
span: span,
}
.start()
} }
crate fn parse_error( crate fn parse_error(
@ -86,66 +109,56 @@ impl ShellError {
.with_label(Label::new_primary(Span::from(span.0))); .with_label(Label::new_primary(Span::from(span.0)));
ShellError::diagnostic(diagnostic) ShellError::diagnostic(diagnostic)
// nom::Context::Code(span, kind) => { }
// let diagnostic =
// Diagnostic::new(Severity::Error, format!("{}", kind.description()))
// .with_label(Label::new_primary(Span::from(span)));
// ShellError::diagnostic(diagnostic)
// }
} // ParseError::UnrecognizedToken {
// token: (start, SpannedToken { token, .. }, end),
// expected,
// } => {
// let diagnostic = Diagnostic::new(
// Severity::Error,
// format!("Unexpected {:?}, expected {:?}", token, expected),
// )
// .with_label(Label::new_primary(Span::from((start, end))));
// ShellError::diagnostic(diagnostic)
// }
// ParseError::User { error } => error,
// other => ShellError::string(format!("{:?}", other)),
} }
} }
crate fn diagnostic(diagnostic: Diagnostic<Span>) -> ShellError { crate fn diagnostic(diagnostic: Diagnostic<Span>) -> ShellError {
ShellError::Diagnostic(ShellDiagnostic { diagnostic }) ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start()
} }
crate fn to_diagnostic(self) -> Diagnostic<Span> { crate fn to_diagnostic(self) -> Diagnostic<Span> {
match self { match self.error {
ShellError::String(StringError { title, .. }) => { ProximateShellError::String(StringError { title, .. }) => {
Diagnostic::new(Severity::Error, title) Diagnostic::new(Severity::Error, title)
} }
ShellError::ArgumentError { error, span } => match error { ProximateShellError::ArgumentError {
command,
error,
span,
} => match error {
ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new( ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new(
Severity::Error, Severity::Error,
format!( format!(
"Command requires {}{}", "{} requires {}{}",
Color::Cyan.paint("--"), Color::Cyan.paint(command),
Color::Cyan.paint(name) Color::Black.bold().paint("--"),
Color::Black.bold().paint(name)
), ),
) )
.with_label(Label::new_primary(span)), .with_label(Label::new_primary(span)),
ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new( ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new(
Severity::Error, Severity::Error,
format!("Command requires {}", Color::Cyan.paint(name)), format!(
"{} requires {}",
Color::Cyan.paint(command),
Color::Green.bold().paint(name)
),
) )
.with_label(Label::new_primary(span)), .with_label(Label::new_primary(span)),
ArgumentError::MissingValueForName(name) => Diagnostic::new( ArgumentError::MissingValueForName(name) => Diagnostic::new(
Severity::Error, Severity::Error,
format!( format!(
"Missing value for flag {}{}", "{} is missing value for flag {}{}",
Color::Cyan.paint("--"), Color::Cyan.paint(command),
Color::Cyan.paint(name) Color::Black.bold().paint("--"),
Color::Black.bold().paint(name)
), ),
) )
.with_label(Label::new_primary(span)), .with_label(Label::new_primary(span)),
}, },
ShellError::TypeError { ProximateShellError::TypeError {
expected, expected,
actual: actual:
Spanned { Spanned {
@ -157,13 +170,13 @@ impl ShellError {
.with_message(format!("Expected {}, found {}", expected, actual)), .with_message(format!("Expected {}, found {}", expected, actual)),
), ),
ShellError::TypeError { ProximateShellError::TypeError {
expected, expected,
actual: Spanned { item: None, span }, actual: Spanned { item: None, span },
} => Diagnostic::new(Severity::Error, "Type Error") } => Diagnostic::new(Severity::Error, "Type Error")
.with_label(Label::new_primary(span).with_message(expected)), .with_label(Label::new_primary(span).with_message(expected)),
ShellError::MissingProperty { subpath, expr } => { ProximateShellError::MissingProperty { subpath, expr } => {
let subpath = subpath.into_label(); let subpath = subpath.into_label();
let expr = expr.into_label(); let expr = expr.into_label();
@ -181,8 +194,8 @@ impl ShellError {
diag diag
} }
ShellError::Diagnostic(diag) => diag.diagnostic, ProximateShellError::Diagnostic(diag) => diag.diagnostic,
ShellError::CoerceError { left, right } => { ProximateShellError::CoerceError { left, right } => {
Diagnostic::new(Severity::Error, "Coercion error") Diagnostic::new(Severity::Error, "Coercion error")
.with_label(Label::new_primary(left.span).with_message(left.item)) .with_label(Label::new_primary(left.span).with_message(left.item))
.with_label(Label::new_secondary(right.span).with_message(right.item)) .with_label(Label::new_secondary(right.span).with_message(right.item))
@ -190,7 +203,7 @@ impl ShellError {
} }
} }
crate fn labeled_error( pub fn labeled_error(
msg: impl Into<String>, msg: impl Into<String>,
label: impl Into<String>, label: impl Into<String>,
span: Span, span: Span,
@ -201,7 +214,7 @@ impl ShellError {
) )
} }
crate fn maybe_labeled_error( pub fn maybe_labeled_error(
msg: impl Into<String>, msg: impl Into<String>,
label: impl Into<String>, label: impl Into<String>,
span: Option<Span>, span: Option<Span>,
@ -216,7 +229,7 @@ impl ShellError {
} }
pub fn string(title: impl Into<String>) -> ShellError { pub fn string(title: impl Into<String>) -> ShellError {
ShellError::String(StringError::new(title.into(), Value::nothing())) ProximateShellError::String(StringError::new(title.into(), Value::nothing())).start()
} }
crate fn unimplemented(title: impl Into<String>) -> ShellError { crate fn unimplemented(title: impl Into<String>) -> ShellError {
@ -226,13 +239,40 @@ impl ShellError {
crate fn unexpected(title: impl Into<String>) -> ShellError { crate fn unexpected(title: impl Into<String>) -> ShellError {
ShellError::string(&format!("Unexpected: {}", title.into())) ShellError::string(&format!("Unexpected: {}", title.into()))
} }
}
crate fn copy_error(&self) -> ShellError { #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
self.clone() pub enum ProximateShellError {
String(StringError),
TypeError {
expected: String,
actual: Spanned<Option<String>>,
},
MissingProperty {
subpath: Description,
expr: Description,
},
ArgumentError {
command: String,
error: ArgumentError,
span: Span,
},
Diagnostic(ShellDiagnostic),
CoerceError {
left: Spanned<String>,
right: Spanned<String>,
},
}
impl ProximateShellError {
fn start(self) -> ShellError {
ShellError {
cause: None,
error: self,
}
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShellDiagnostic { pub struct ShellDiagnostic {
crate diagnostic: Diagnostic<Span>, crate diagnostic: Diagnostic<Span>,
} }
@ -275,28 +315,6 @@ impl std::cmp::Ord for ShellDiagnostic {
} }
} }
impl Serialize for ShellDiagnostic {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
"<diagnostic>".serialize(serializer)
}
}
impl Deserialize<'de> for ShellDiagnostic {
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(ShellDiagnostic {
diagnostic: Diagnostic::new(
language_reporting::Severity::Error,
"deserialize not implemented for ShellDiagnostic",
),
})
}
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new, Clone, Serialize, Deserialize)] #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new, Clone, Serialize, Deserialize)]
pub struct StringError { pub struct StringError {
title: String, title: String,
@ -305,13 +323,13 @@ pub struct StringError {
impl std::fmt::Display for ShellError { impl std::fmt::Display for ShellError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match &self.error {
ShellError::String(s) => write!(f, "{}", &s.title), ProximateShellError::String(s) => write!(f, "{}", &s.title),
ShellError::TypeError { .. } => write!(f, "TypeError"), ProximateShellError::TypeError { .. } => write!(f, "TypeError"),
ShellError::MissingProperty { .. } => write!(f, "MissingProperty"), ProximateShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
ShellError::ArgumentError { .. } => write!(f, "ArgumentError"), ProximateShellError::ArgumentError { .. } => write!(f, "ArgumentError"),
ShellError::Diagnostic(_) => write!(f, "<diagnostic>"), ProximateShellError::Diagnostic(_) => write!(f, "<diagnostic>"),
ShellError::CoerceError { .. } => write!(f, "CoerceError"), ProximateShellError::CoerceError { .. } => write!(f, "CoerceError"),
} }
} }
} }
@ -320,45 +338,40 @@ impl std::error::Error for ShellError {}
impl std::convert::From<std::io::Error> for ShellError { impl std::convert::From<std::io::Error> for ShellError {
fn from(input: std::io::Error) -> ShellError { fn from(input: std::io::Error) -> ShellError {
ShellError::String(StringError { ProximateShellError::String(StringError {
title: format!("{}", input), title: format!("{}", input),
error: Value::nothing(), error: Value::nothing(),
}) })
.start()
} }
} }
impl std::convert::From<futures_sink::VecSinkError> for ShellError { impl std::convert::From<futures_sink::VecSinkError> for ShellError {
fn from(_input: futures_sink::VecSinkError) -> ShellError { fn from(_input: futures_sink::VecSinkError) -> ShellError {
ShellError::String(StringError { ProximateShellError::String(StringError {
title: format!("Unexpected Vec Sink Error"), title: format!("Unexpected Vec Sink Error"),
error: Value::nothing(), error: Value::nothing(),
}) })
.start()
} }
} }
impl std::convert::From<subprocess::PopenError> for ShellError { impl std::convert::From<subprocess::PopenError> for ShellError {
fn from(input: subprocess::PopenError) -> ShellError { fn from(input: subprocess::PopenError) -> ShellError {
ShellError::String(StringError { ProximateShellError::String(StringError {
title: format!("{}", input), title: format!("{}", input),
error: Value::nothing(), error: Value::nothing(),
}) })
.start()
} }
} }
// impl std::convert::From<nom::Err<(&str, nom::ErrorKind)>> for ShellError {
// fn from(input: nom::Err<(&str, nom::ErrorKind)>) -> ShellError {
// ShellError::String(StringError {
// title: format!("{:?}", input),
// error: Value::nothing(),
// })
// }
// }
impl std::convert::From<toml::ser::Error> for ShellError { impl std::convert::From<toml::ser::Error> for ShellError {
fn from(input: toml::ser::Error) -> ShellError { fn from(input: toml::ser::Error) -> ShellError {
ShellError::String(StringError { ProximateShellError::String(StringError {
title: format!("{:?}", input), title: format!("{:?}", input),
error: Value::nothing(), error: Value::nothing(),
}) })
.start()
} }
} }

View File

@ -10,15 +10,15 @@ use indexmap::IndexMap;
#[derive(new)] #[derive(new)]
crate struct Scope { crate struct Scope {
it: Value, it: Spanned<Value>,
#[new(default)] #[new(default)]
vars: IndexMap<String, Value>, vars: IndexMap<String, Spanned<Value>>,
} }
impl Scope { impl Scope {
crate fn empty() -> Scope { crate fn empty() -> Scope {
Scope { Scope {
it: Value::nothing(), it: Value::nothing().spanned_unknown(),
vars: IndexMap::new(), vars: IndexMap::new(),
} }
} }
@ -39,10 +39,10 @@ crate fn evaluate_baseline_expr(
match left.compare(binary.op(), &*right) { match left.compare(binary.op(), &*right) {
Ok(result) => Ok(Spanned::from_item(Value::boolean(result), *expr.span())), Ok(result) => Ok(Spanned::from_item(Value::boolean(result), *expr.span())),
Err((left_type, right_type)) => Err(ShellError::CoerceError { Err((left_type, right_type)) => Err(ShellError::coerce_error(
left: binary.left().copy_span(left_type), binary.left().copy_span(left_type),
right: binary.right().copy_span(right_type), binary.right().copy_span(right_type),
}), )),
} }
} }
RawExpression::Block(block) => Ok(Spanned::from_item( RawExpression::Block(block) => Ok(Spanned::from_item(
@ -58,14 +58,16 @@ crate fn evaluate_baseline_expr(
match next { match next {
None => { None => {
return Err(ShellError::MissingProperty { return Err(ShellError::missing_property(
subpath: Description::from(item.spanned_type_name()), Description::from(item.spanned_type_name()),
expr: Description::from(name.clone()), Description::from(name.clone()),
}) ))
} }
Some(next) => { Some(next) => {
item = item = Spanned::from_item(
Spanned::from_item(next.clone(), (expr.span().start, name.span().end)) next.clone().item,
(expr.span().start, name.span().end),
)
} }
}; };
} }
@ -93,14 +95,11 @@ fn evaluate_reference(
source: &Text, source: &Text,
) -> Result<Spanned<Value>, ShellError> { ) -> Result<Spanned<Value>, ShellError> {
match name { match name {
hir::Variable::It(span) => Ok(Spanned::from_item(scope.it.copy(), span)), hir::Variable::It(span) => Ok(Spanned::from_item(scope.it.item.clone(), span)),
hir::Variable::Other(span) => Ok(Spanned::from_item( hir::Variable::Other(span) => Ok(scope
scope
.vars .vars
.get(span.slice(source)) .get(span.slice(source))
.map(|v| v.copy()) .map(|v| v.clone())
.unwrap_or_else(|| Value::nothing()), .unwrap_or_else(|| Value::nothing().spanned(span))),
span,
)),
} }
} }

View File

@ -2,7 +2,6 @@ crate mod entries;
crate mod generic; crate mod generic;
crate mod list; crate mod list;
crate mod table; crate mod table;
crate mod tree;
crate mod vtable; crate mod vtable;
use crate::prelude::*; use crate::prelude::*;
@ -10,7 +9,6 @@ use crate::prelude::*;
crate use entries::EntriesView; crate use entries::EntriesView;
crate use generic::GenericView; crate use generic::GenericView;
crate use table::TableView; crate use table::TableView;
crate use tree::TreeView;
crate use vtable::VTableView; crate use vtable::VTableView;
crate trait RenderView { crate trait RenderView {

View File

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

View File

@ -13,7 +13,7 @@ pub struct TableView {
} }
impl TableView { impl TableView {
fn merge_descriptors(values: &[Value]) -> Vec<String> { fn merge_descriptors(values: &[Spanned<Value>]) -> Vec<String> {
let mut ret = vec![]; let mut ret = vec![];
for value in values { for value in values {
for desc in value.data_descriptors() { for desc in value.data_descriptors() {
@ -25,7 +25,7 @@ impl TableView {
ret ret
} }
pub fn from_list(values: &[Value]) -> Option<TableView> { pub fn from_list(values: &[Spanned<Value>]) -> Option<TableView> {
if values.len() == 0 { if values.len() == 0 {
return None; return None;
} }

View File

@ -1,80 +0,0 @@
use crate::format::RenderView;
use crate::prelude::*;
use derive_new::new;
use ptree::item::StringItem;
use ptree::output::print_tree_with;
use ptree::print_config::PrintConfig;
use ptree::style::{Color, Style};
use ptree::TreeBuilder;
#[derive(new)]
pub struct TreeView {
tree: StringItem,
}
impl TreeView {
fn from_value_helper(value: &Value, mut builder: &mut TreeBuilder) {
match value {
Value::Primitive(p) => {
let _ = builder.add_empty_child(p.format(None));
}
Value::Object(o) => {
for (k, v) in o.entries.iter() {
builder = builder.begin_child(k.clone());
Self::from_value_helper(v, builder);
builder = builder.end_child();
}
}
Value::List(l) => {
for elem in l.iter() {
Self::from_value_helper(elem, builder);
}
}
Value::Block(_) => {}
Value::Error(_) => {}
Value::Filesystem => {}
Value::Binary(_) => {}
}
}
crate fn from_value(value: &Value) -> TreeView {
let descs = value.data_descriptors();
let mut tree = TreeBuilder::new("".to_string());
let mut builder = &mut tree;
for desc in descs {
let value = value.get_data(&desc);
builder = builder.begin_child(desc.clone());
Self::from_value_helper(value.borrow(), &mut builder);
builder = builder.end_child();
//entries.push((desc.name.clone(), value.borrow().copy()))
}
TreeView::new(builder.build())
}
}
impl RenderView for TreeView {
fn render_view(&self, _host: &mut dyn Host) -> Result<(), ShellError> {
// Set up the print configuration
let config = {
let mut config = PrintConfig::from_env();
config.branch = Style {
foreground: Some(Color::Green),
dimmed: true,
..Style::default()
};
config.leaf = Style {
bold: true,
..Style::default()
};
config.indent = 4;
config
};
// Print out the tree using custom formatting
print_tree_with(&self.tree, &config)?;
Ok(())
}
}

View File

@ -12,7 +12,7 @@ pub struct VTableView {
} }
impl VTableView { impl VTableView {
pub fn from_list(values: &[Value]) -> Option<VTableView> { pub fn from_list(values: &[Spanned<Value>]) -> Option<VTableView> {
if values.len() == 0 { if values.len() == 0 {
return None; return None;
} }

View File

@ -4,6 +4,10 @@
#![feature(try_trait)] #![feature(try_trait)]
#![feature(bind_by_move_pattern_guards)] #![feature(bind_by_move_pattern_guards)]
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(type_ascription)]
#[macro_use]
mod prelude;
mod cli; mod cli;
mod commands; mod commands;
@ -16,11 +20,10 @@ mod git;
mod object; mod object;
mod parser; mod parser;
mod plugin; mod plugin;
mod prelude;
mod shell; mod shell;
mod stream; mod stream;
pub use crate::commands::command::ReturnValue; pub use crate::commands::command::{ReturnSuccess, ReturnValue};
pub use crate::env::host::BasicHost; pub use crate::env::host::BasicHost;
pub use crate::parser::parse::span::SpannedItem; pub use crate::parser::parse::span::SpannedItem;
pub use crate::parser::Spanned; pub use crate::parser::Spanned;

View File

@ -2,9 +2,10 @@ crate mod base;
crate mod config; crate mod config;
crate mod dict; crate mod dict;
crate mod files; crate mod files;
crate mod into;
crate mod process; crate mod process;
crate mod types; crate mod types;
crate use base::{Primitive, Value}; crate use base::{Block, Primitive, Switch, Value};
crate use dict::Dictionary; crate use dict::{Dictionary, SpannedDictBuilder};
crate use files::dir_entry_dict; crate use files::dir_entry_dict;

View File

@ -1,5 +1,6 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::evaluate::{evaluate_baseline_expr, Scope}; use crate::evaluate::{evaluate_baseline_expr, Scope};
use crate::object::SpannedDictBuilder;
use crate::parser::{hir, Operator, Span, Spanned}; use crate::parser::{hir, Operator, Span, Spanned};
use crate::prelude::*; use crate::prelude::*;
use crate::Text; use crate::Text;
@ -149,17 +150,12 @@ impl Deserialize<'de> for Block {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
unimplemented!("deserialize block") unimplemented!("deserialize block")
// let s = "\"unimplemented deserialize block\"";
// Ok(Block::new(
// TokenTreeBuilder::spanned_string((1, s.len() - 1), (0, s.len())),
// Text::from(s),
// ))
} }
} }
impl Block { impl Block {
pub fn invoke(&self, value: &Value) -> Result<Spanned<Value>, ShellError> { pub fn invoke(&self, value: &Spanned<Value>) -> Result<Spanned<Value>, ShellError> {
let scope = Scope::new(value.copy()); let scope = Scope::new(value.clone());
if self.expressions.len() == 0 { if self.expressions.len() == 0 {
return Ok(Spanned::from_item(Value::nothing(), self.span)); return Ok(Spanned::from_item(Value::nothing(), self.span));
@ -179,23 +175,19 @@ impl Block {
pub enum Value { pub enum Value {
Primitive(Primitive), Primitive(Primitive),
Object(crate::object::Dictionary), Object(crate::object::Dictionary),
List(Vec<Value>),
Binary(Vec<u8>), Binary(Vec<u8>),
List(Vec<Spanned<Value>>),
#[allow(unused)] #[allow(unused)]
Block(Block), Block(Block),
Filesystem, Filesystem,
#[allow(unused)]
Error(Box<ShellError>),
} }
pub fn debug_list(values: &'a Vec<Value>) -> ValuesDebug<'a> { pub fn debug_list(values: &'a Vec<Spanned<Value>>) -> ValuesDebug<'a> {
ValuesDebug { values } ValuesDebug { values }
} }
pub struct ValuesDebug<'a> { pub struct ValuesDebug<'a> {
values: &'a Vec<Value>, values: &'a Vec<Spanned<Value>>,
} }
impl fmt::Debug for ValuesDebug<'a> { impl fmt::Debug for ValuesDebug<'a> {
@ -207,17 +199,16 @@ impl fmt::Debug for ValuesDebug<'a> {
} }
pub struct ValueDebug<'a> { pub struct ValueDebug<'a> {
value: &'a Value, value: &'a Spanned<Value>,
} }
impl fmt::Debug for ValueDebug<'a> { impl fmt::Debug for ValueDebug<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.value { match self.value.item() {
Value::Primitive(p) => p.debug(f), Value::Primitive(p) => p.debug(f),
Value::Object(o) => o.debug(f), Value::Object(o) => o.debug(f),
Value::List(l) => debug_list(l).fmt(f), Value::List(l) => debug_list(l).fmt(f),
Value::Block(_) => write!(f, "[[block]]"), Value::Block(_) => write!(f, "[[block]]"),
Value::Error(err) => write!(f, "[[error :: {} ]]", err),
Value::Filesystem => write!(f, "[[filesystem]]"), Value::Filesystem => write!(f, "[[filesystem]]"),
Value::Binary(_) => write!(f, "[[binary]]"), Value::Binary(_) => write!(f, "[[binary]]"),
} }
@ -231,6 +222,71 @@ impl Spanned<Value> {
} }
} }
impl std::convert::TryFrom<&'a Spanned<Value>> for Block {
type Error = ShellError;
fn try_from(value: &'a Spanned<Value>) -> Result<Block, ShellError> {
match value.item() {
Value::Block(block) => Ok(block.clone()),
v => Err(ShellError::type_error(
"Block",
value.copy_span(v.type_name()),
)),
}
}
}
impl std::convert::TryFrom<&'a Spanned<Value>> for i64 {
type Error = ShellError;
fn try_from(value: &'a Spanned<Value>) -> Result<i64, ShellError> {
match value.item() {
Value::Primitive(Primitive::Int(int)) => Ok(*int),
v => Err(ShellError::type_error(
"Integer",
value.copy_span(v.type_name()),
)),
}
}
}
pub enum Switch {
Present,
Absent,
}
impl Switch {
pub fn is_present(&self) -> bool {
match self {
Switch::Present => true,
Switch::Absent => false,
}
}
}
impl std::convert::TryFrom<Option<&'a Spanned<Value>>> for Switch {
type Error = ShellError;
fn try_from(value: Option<&'a Spanned<Value>>) -> Result<Switch, ShellError> {
match value {
None => Ok(Switch::Absent),
Some(value) => match value.item() {
Value::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present),
v => Err(ShellError::type_error(
"Boolean",
value.copy_span(v.type_name()),
)),
},
}
}
}
impl Spanned<Value> {
crate fn debug(&'a self) -> ValueDebug<'a> {
ValueDebug { value: self }
}
}
impl Value { impl Value {
crate fn type_name(&self) -> String { crate fn type_name(&self) -> String {
match self { match self {
@ -238,16 +294,11 @@ impl Value {
Value::Object(_) => format!("object"), Value::Object(_) => format!("object"),
Value::List(_) => format!("list"), Value::List(_) => format!("list"),
Value::Block(_) => format!("block"), Value::Block(_) => format!("block"),
Value::Error(_) => format!("error"),
Value::Filesystem => format!("filesystem"), Value::Filesystem => format!("filesystem"),
Value::Binary(_) => format!("binary"), Value::Binary(_) => format!("binary"),
} }
} }
crate fn debug(&'a self) -> ValueDebug<'a> {
ValueDebug { value: self }
}
pub fn data_descriptors(&self) -> Vec<String> { pub fn data_descriptors(&self) -> Vec<String> {
match self { match self {
Value::Primitive(_) => vec![], Value::Primitive(_) => vec![],
@ -259,19 +310,21 @@ impl Value {
.collect(), .collect(),
Value::Block(_) => vec![], Value::Block(_) => vec![],
Value::List(_) => vec![], Value::List(_) => vec![],
Value::Error(_) => vec![],
Value::Filesystem => vec![], Value::Filesystem => vec![],
Value::Binary(_) => vec![], Value::Binary(_) => vec![],
} }
} }
crate fn get_data_by_key(&'a self, name: &str) -> Option<&Value> { crate fn get_data_by_key(&'a self, name: &str) -> Option<&Spanned<Value>> {
match self { match self {
Value::Object(o) => o.get_data_by_key(name), Value::Object(o) => o.get_data_by_key(name),
Value::List(l) => { Value::List(l) => {
for item in l { for item in l {
match item { 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), Some(v) => return Some(v),
None => {} None => {}
}, },
@ -284,7 +337,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<Value>> {
match self { match self {
Value::List(l) => l.iter().nth(idx), Value::List(l) => l.iter().nth(idx),
_ => None, _ => None,
@ -298,26 +351,10 @@ impl Value {
Value::Object(o) => o.get_data(desc), Value::Object(o) => o.get_data(desc),
Value::Block(_) => MaybeOwned::Owned(Value::nothing()), Value::Block(_) => MaybeOwned::Owned(Value::nothing()),
Value::List(_) => MaybeOwned::Owned(Value::nothing()), Value::List(_) => MaybeOwned::Owned(Value::nothing()),
Value::Error(e) => MaybeOwned::Owned(Value::string(&format!("{:#?}", e))),
Value::Binary(_) => MaybeOwned::Owned(Value::nothing()), Value::Binary(_) => MaybeOwned::Owned(Value::nothing()),
} }
} }
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::Error(e) => Value::Error(Box::new(e.copy_error())),
Value::Filesystem => Value::Filesystem,
Value::Binary(b) => Value::Binary(b.clone()),
}
}
crate fn format_leaf(&self, desc: Option<&String>) -> String { crate fn format_leaf(&self, desc: Option<&String>) -> String {
match self { match self {
Value::Primitive(p) => p.format(desc), Value::Primitive(p) => p.format(desc),
@ -329,7 +366,6 @@ impl Value {
), ),
Value::Object(_) => format!("[object Object]"), Value::Object(_) => format!("[object Object]"),
Value::List(_) => format!("[list List]"), Value::List(_) => format!("[list List]"),
Value::Error(e) => format!("{}", e),
Value::Filesystem => format!("<filesystem>"), Value::Filesystem => format!("<filesystem>"),
Value::Binary(_) => format!("<binary>"), Value::Binary(_) => format!("<binary>"),
} }
@ -370,7 +406,7 @@ impl Value {
} }
} }
crate fn as_pair(&self) -> Result<(Value, Value), ShellError> { crate fn as_pair(&self) -> Result<(Spanned<Value>, Spanned<Value>), ShellError> {
match self { match self {
Value::List(list) if list.len() == 2 => Ok((list[0].clone(), list[1].clone())), Value::List(list) if list.len() == 2 => Ok((list[0].clone(), list[1].clone())),
other => Err(ShellError::string(format!( other => Err(ShellError::string(format!(
@ -459,52 +495,39 @@ impl Value {
Ok(Value::Primitive(Primitive::Date(date))) Ok(Value::Primitive(Primitive::Date(date)))
} }
#[allow(unused)]
pub fn system_date_result(s: Result<SystemTime, std::io::Error>) -> Value {
match s {
Ok(time) => Value::Primitive(Primitive::Date(time.into())),
Err(err) => Value::Error(Box::new(ShellError::string(format!("{}", err)))),
}
}
pub fn nothing() -> Value { pub fn nothing() -> Value {
Value::Primitive(Primitive::Nothing) Value::Primitive(Primitive::Nothing)
} }
#[allow(unused)]
pub fn list(values: impl Into<Vec<Value>>) -> Value {
Value::List(values.into())
}
} }
crate fn select_fields(obj: &Value, fields: &[String]) -> crate::object::Dictionary { crate fn select_fields(obj: &Value, fields: &[String], span: impl Into<Span>) -> Spanned<Value> {
let mut out = crate::object::Dictionary::default(); let mut out = SpannedDictBuilder::new(span);
let descs = obj.data_descriptors(); let descs = obj.data_descriptors();
for field in fields { for field in fields {
match descs.iter().find(|d| *d == field) { match descs.iter().find(|d| *d == field) {
None => out.add(field, Value::nothing()), None => out.insert(field, Value::nothing()),
Some(desc) => out.add(desc.clone(), obj.get_data(desc).borrow().copy()), Some(desc) => out.insert(desc.clone(), obj.get_data(desc).borrow().clone()),
} }
} }
out out.into_spanned_value()
} }
crate fn reject_fields(obj: &Value, fields: &[String]) -> crate::object::Dictionary { crate fn reject_fields(obj: &Value, fields: &[String], span: impl Into<Span>) -> Spanned<Value> {
let mut out = crate::object::Dictionary::default(); let mut out = SpannedDictBuilder::new(span);
let descs = obj.data_descriptors(); let descs = obj.data_descriptors();
for desc in descs { for desc in descs {
match desc { match desc {
x if fields.iter().any(|field| *field == x) => continue, x if fields.iter().any(|field| *field == x) => continue,
_ => out.add(desc.clone(), obj.get_data(&desc).borrow().copy()), _ => out.insert(desc.clone(), obj.get_data(&desc).borrow().clone()),
} }
} }
out out.into_spanned_value()
} }
#[allow(unused)] #[allow(unused)]
@ -513,7 +536,7 @@ crate fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool {
match descs.iter().find(|d| *d == field) { match descs.iter().find(|d| *d == field) {
None => false, None => false,
Some(desc) => { Some(desc) => {
let v = obj.get_data(desc).borrow().copy(); let v = obj.get_data(desc).borrow().clone();
match v { match v {
Value::Primitive(Primitive::Boolean(b)) => match (op, rhs) { Value::Primitive(Primitive::Boolean(b)) => match (op, rhs) {

View File

@ -16,24 +16,28 @@ const APP_INFO: AppInfo = AppInfo {
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
struct Config { struct Config {
#[serde(flatten)] #[serde(flatten)]
extra: IndexMap<String, Value>, extra: IndexMap<String, Spanned<Value>>,
} }
crate fn write_config(map: &IndexMap<String, Value>) -> Result<(), ShellError> { crate fn write_config(config: &IndexMap<String, Spanned<Value>>) -> Result<(), ShellError> {
let location = app_root(AppDataType::UserConfig, &APP_INFO) let location = app_root(AppDataType::UserConfig, &APP_INFO)
.map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?; .map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?;
let filename = location.join("config.toml"); let filename = location.join("config.toml");
touch(&filename)?; 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.clone())).collect(),
})?;
fs::write(&filename, &contents)?; fs::write(&filename, &contents)?;
Ok(()) Ok(())
} }
crate fn config() -> Result<IndexMap<String, Value>, ShellError> { crate fn config(span: impl Into<Span>) -> Result<IndexMap<String, Spanned<Value>>, ShellError> {
let span = span.into();
let location = app_root(AppDataType::UserConfig, &APP_INFO) let location = app_root(AppDataType::UserConfig, &APP_INFO)
.map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?; .map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?;
@ -43,6 +47,7 @@ crate fn config() -> Result<IndexMap<String, Value>, ShellError> {
trace!("config file = {}", filename.display()); trace!("config file = {}", filename.display());
let contents = fs::read_to_string(filename) 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)))?; .map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?;
let parsed: Config = toml::from_str(&contents) let parsed: Config = toml::from_str(&contents)

View File

@ -9,7 +9,7 @@ use std::fmt;
#[derive(Debug, Default, Eq, PartialEq, Serialize, Deserialize, Clone, new)] #[derive(Debug, Default, Eq, PartialEq, Serialize, Deserialize, Clone, new)]
pub struct Dictionary { pub struct Dictionary {
pub entries: IndexMap<String, Value>, pub entries: IndexMap<String, Spanned<Value>>,
} }
impl PartialOrd for Dictionary { impl PartialOrd for Dictionary {
@ -21,15 +21,15 @@ impl PartialOrd for Dictionary {
return this.partial_cmp(&that); return this.partial_cmp(&that);
} }
let this: Vec<&Value> = self.entries.values().collect(); let this: Vec<&Value> = self.entries.values().map(|v| v.item()).collect();
let that: Vec<&Value> = self.entries.values().collect(); let that: Vec<&Value> = self.entries.values().map(|v| v.item()).collect();
this.partial_cmp(&that) this.partial_cmp(&that)
} }
} }
impl From<IndexMap<String, Value>> for Dictionary { impl From<IndexMap<String, Spanned<Value>>> for Dictionary {
fn from(input: IndexMap<String, Value>) -> Dictionary { fn from(input: IndexMap<String, Spanned<Value>>) -> Dictionary {
let mut out = IndexMap::default(); let mut out = IndexMap::default();
for (key, value) in input { for (key, value) in input {
@ -49,8 +49,8 @@ impl Ord for Dictionary {
return this.cmp(&that); return this.cmp(&that);
} }
let this: Vec<&Value> = self.entries.values().collect(); let this: Vec<&Value> = self.entries.values().map(|v| v.item()).collect();
let that: Vec<&Value> = self.entries.values().collect(); let that: Vec<&Value> = self.entries.values().map(|v| v.item()).collect();
this.cmp(&that) this.cmp(&that)
} }
@ -72,20 +72,6 @@ impl PartialEq<Value> for Dictionary {
} }
impl Dictionary { impl Dictionary {
crate fn add(&mut self, name: impl Into<String>, value: Value) {
self.entries.insert(name.into(), value);
}
crate fn copy_dict(&self) -> Dictionary {
let mut out = Dictionary::default();
for (key, value) in self.entries.iter() {
out.add(key.clone(), value.copy());
}
out
}
pub fn get_data(&'a self, desc: &String) -> MaybeOwned<'a, Value> { pub fn get_data(&'a self, desc: &String) -> MaybeOwned<'a, Value> {
match self.entries.get(desc) { match self.entries.get(desc) {
Some(v) => MaybeOwned::Borrowed(v), Some(v) => MaybeOwned::Borrowed(v),
@ -93,7 +79,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<Value>> {
match self match self
.entries .entries
.iter() .iter()
@ -114,3 +100,73 @@ impl Dictionary {
debug.finish() debug.finish()
} }
} }
pub struct SpannedListBuilder {
span: Span,
list: Vec<Spanned<Value>>,
}
impl SpannedListBuilder {
pub fn new(span: impl Into<Span>) -> SpannedListBuilder {
SpannedListBuilder {
span: span.into(),
list: vec![],
}
}
pub fn push(&mut self, value: impl Into<Value>) {
self.list.push(value.into().spanned(self.span));
}
pub fn insert_spanned(&mut self, value: impl Into<Spanned<Value>>) {
self.list.push(value.into());
}
pub fn into_spanned_value(self) -> Spanned<Value> {
Value::List(self.list).spanned(self.span)
}
}
impl From<SpannedListBuilder> for Spanned<Value> {
fn from(input: SpannedListBuilder) -> Spanned<Value> {
input.into_spanned_value()
}
}
#[derive(Debug)]
pub struct SpannedDictBuilder {
span: Span,
dict: IndexMap<String, Spanned<Value>>,
}
impl SpannedDictBuilder {
pub fn new(span: impl Into<Span>) -> SpannedDictBuilder {
SpannedDictBuilder {
span: span.into(),
dict: IndexMap::default(),
}
}
pub fn insert(&mut self, key: impl Into<String>, value: impl Into<Value>) {
self.dict
.insert(key.into(), value.into().spanned(self.span));
}
pub fn insert_spanned(&mut self, key: impl Into<String>, value: impl Into<Spanned<Value>>) {
self.dict.insert(key.into(), value.into());
}
pub fn into_spanned_value(self) -> Spanned<Value> {
self.into_spanned_dict().map(Value::Object)
}
pub fn into_spanned_dict(self) -> Spanned<Dictionary> {
Dictionary { entries: self.dict }.spanned(self.span)
}
}
impl From<SpannedDictBuilder> for Spanned<Value> {
fn from(input: SpannedDictBuilder) -> Spanned<Value> {
input.into_spanned_value()
}
}

View File

@ -1,5 +1,6 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::{Dictionary, Value}; use crate::object::{SpannedDictBuilder, Value};
use crate::prelude::*;
#[derive(Debug)] #[derive(Debug)]
pub enum FileType { pub enum FileType {
@ -8,10 +9,13 @@ pub enum FileType {
Symlink, Symlink,
} }
crate fn dir_entry_dict(entry: &std::fs::DirEntry) -> Result<Dictionary, ShellError> { crate fn dir_entry_dict(
let mut dict = Dictionary::default(); entry: &std::fs::DirEntry,
span: impl Into<Span>,
) -> Result<Spanned<Value>, ShellError> {
let mut dict = SpannedDictBuilder::new(span);
let filename = entry.file_name(); let filename = entry.file_name();
dict.add("file name", Value::string(filename.to_string_lossy())); dict.insert("file name", Value::string(filename.to_string_lossy()));
let metadata = entry.metadata()?; let metadata = entry.metadata()?;
@ -23,28 +27,28 @@ crate fn dir_entry_dict(entry: &std::fs::DirEntry) -> Result<Dictionary, ShellEr
FileType::Symlink FileType::Symlink
}; };
dict.add("file type", Value::string(format!("{:?}", kind))); dict.insert("file type", Value::string(format!("{:?}", kind)));
dict.add( dict.insert(
"readonly", "readonly",
Value::boolean(metadata.permissions().readonly()), Value::boolean(metadata.permissions().readonly()),
); );
dict.add("size", Value::bytes(metadata.len() as u64)); dict.insert("size", Value::bytes(metadata.len() as u64));
match metadata.created() { match metadata.created() {
Ok(c) => dict.add("created", Value::system_date(c)), Ok(c) => dict.insert("created", Value::system_date(c)),
Err(_) => {} Err(_) => {}
} }
match metadata.accessed() { match metadata.accessed() {
Ok(a) => dict.add("accessed", Value::system_date(a)), Ok(a) => dict.insert("accessed", Value::system_date(a)),
Err(_) => {} Err(_) => {}
} }
match metadata.modified() { match metadata.modified() {
Ok(m) => dict.add("modified", Value::system_date(m)), Ok(m) => dict.insert("modified", Value::system_date(m)),
Err(_) => {} Err(_) => {}
} }
Ok(dict) Ok(dict.into_spanned_value())
} }

23
src/object/into.rs Normal file
View File

@ -0,0 +1,23 @@
use crate::object::{Primitive, Value};
use crate::prelude::*;
impl From<Primitive> for Value {
fn from(input: Primitive) -> Value {
Value::Primitive(input)
}
}
impl From<String> for Value {
fn from(input: String) -> Value {
Value::Primitive(Primitive::String(input))
}
}
impl<T: Into<Value>> Spanned<T> {
pub fn into_spanned_value(self) -> Spanned<Value> {
let Spanned { item, span } = self;
let value = item.into();
value.spanned(span)
}
}

View File

@ -1,11 +1,11 @@
use crate::object::base::Value; use crate::object::{SpannedDictBuilder, Value};
use crate::object::dict::Dictionary; use crate::prelude::*;
use itertools::join; use itertools::join;
use sysinfo::ProcessExt; use sysinfo::ProcessExt;
crate fn process_dict(proc: &sysinfo::Process) -> Dictionary { crate fn process_dict(proc: &sysinfo::Process, span: impl Into<Span>) -> Spanned<Value> {
let mut dict = Dictionary::default(); let mut dict = SpannedDictBuilder::new(span);
dict.add("name", Value::string(proc.name())); dict.insert("name", Value::string(proc.name()));
let cmd = proc.cmd(); let cmd = proc.cmd();
@ -15,10 +15,10 @@ crate fn process_dict(proc: &sysinfo::Process) -> Dictionary {
Value::string(join(cmd, "")) Value::string(join(cmd, ""))
}; };
dict.add("cmd", cmd_value); dict.insert("cmd", cmd_value);
dict.add("cpu", Value::float(proc.cpu_usage() as f64)); dict.insert("cpu", Value::float(proc.cpu_usage() as f64));
dict.add("pid", Value::int(proc.pid() as i64)); dict.insert("pid", Value::int(proc.pid() as i64));
dict.add("status", Value::string(proc.status().to_string())); dict.insert("status", Value::string(proc.status().to_string()));
dict dict.into_spanned_value()
} }

View File

@ -1,7 +1,139 @@
use crate::object::base as value;
use crate::parser::hir;
use crate::prelude::*;
use derive_new::new; use derive_new::new;
use serde_derive::{Deserialize, Serialize}; use serde_derive::Deserialize;
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)] pub trait Type: std::fmt::Debug + Send {
pub enum Type { type Extractor: ExtractType;
Any,
fn name(&self) -> &'static str;
fn coerce(&self) -> Option<hir::ExpressionKindHint> {
None
}
}
pub trait ExtractType: Sized {
fn extract(value: &Spanned<Value>) -> Result<Self, ShellError>;
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError>;
}
#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)]
pub struct Any;
impl Type for Any {
type Extractor = Spanned<Value>;
fn name(&self) -> &'static str {
"Any"
}
}
impl ExtractType for Spanned<Value> {
fn extract(value: &Spanned<Value>) -> Result<Self, ShellError> {
Ok(value.clone())
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
Ok(value)
}
}
#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)]
pub struct Integer;
impl Type for Integer {
type Extractor = i64;
fn name(&self) -> &'static str {
"Integer"
}
}
impl ExtractType for i64 {
fn extract(value: &Spanned<Value>) -> Result<i64, ShellError> {
match value {
&Spanned {
item: Value::Primitive(Primitive::Int(int)),
..
} => Ok(int),
other => Err(ShellError::type_error("Integer", other.spanned_type_name())),
}
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, 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"
}
}
impl ExtractType for String {
fn extract(value: &Spanned<Value>) -> Result<String, ShellError> {
match value {
Spanned {
item: Value::Primitive(Primitive::String(string)),
..
} => Ok(string.clone()),
other => Err(ShellError::type_error("String", other.spanned_type_name())),
}
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
match value {
v @ Spanned {
item: Value::Primitive(Primitive::String(_)),
..
} => Ok(v),
other => Err(ShellError::type_error("String", other.spanned_type_name())),
}
}
}
#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)]
pub struct Block;
impl Type for Block {
type Extractor = value::Block;
fn name(&self) -> &'static str {
"Block"
}
}
impl ExtractType for value::Block {
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
match value {
v @ Spanned {
item: Value::Block(_),
..
} => Ok(v),
other => Err(ShellError::type_error("Block", other.spanned_type_name())),
}
}
fn extract(value: &Spanned<Value>) -> Result<value::Block, ShellError> {
match value {
Spanned {
item: Value::Block(block),
..
} => Ok(block.clone()),
other => Err(ShellError::type_error("Block", other.spanned_type_name())),
}
}
} }

View File

@ -12,7 +12,7 @@ crate use parse::flag::Flag;
crate use parse::operator::Operator; crate use parse::operator::Operator;
crate use parse::parser::{nom_input, pipeline}; crate use parse::parser::{nom_input, pipeline};
crate use parse::pipeline::{Pipeline, PipelineElement}; 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::text::Text;
crate use parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode}; crate use parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode};
crate use parse::tokens::{RawToken, Token}; crate use parse::tokens::{RawToken, Token};

View File

@ -23,7 +23,7 @@ impl language_reporting::ReportingFiles for Files {
0 0
} }
fn file_name(&self, _file: Self::FileId) -> FileName { fn file_name(&self, _file: Self::FileId) -> FileName {
FileName::Verbatim(format!("<eval>")) FileName::Verbatim(format!("shell"))
} }
fn byte_index(&self, _file: Self::FileId, _line: usize, _column: usize) -> Option<usize> { fn byte_index(&self, _file: Self::FileId, _line: usize, _column: usize) -> Option<usize> {
unimplemented!("byte_index") unimplemented!("byte_index")

View File

@ -53,15 +53,15 @@ fn trace_step<'a, T: Debug>(
name: &str, name: &str,
block: impl FnOnce(NomSpan<'a>) -> IResult<NomSpan<'a>, T>, block: impl FnOnce(NomSpan<'a>) -> IResult<NomSpan<'a>, T>,
) -> IResult<NomSpan<'a>, T> { ) -> IResult<NomSpan<'a>, T> {
trace!("+ before {} @ {:?}", name, input); trace!(target: "nu::lite_parse", "+ before {} @ {:?}", name, input);
match block(input) { match block(input) {
Ok((input, result)) => { Ok((input, result)) => {
trace!("after {} @ {:?} -> {:?}", name, input, result); trace!(target: "nu::lite_parse", "after {} @ {:?} -> {:?}", name, input, result);
Ok((input, result)) Ok((input, result))
} }
Err(e) => { Err(e) => {
trace!("- failed {} :: {:?}", name, e); trace!(target: "nu::lite_parse", "- failed {} :: {:?}", name, e);
Err(e) Err(e)
} }
} }

View File

@ -1,7 +1,8 @@
use crate::Text; use crate::Text;
use derive_new::new; use derive_new::new;
use getset::Getters; use getset::Getters;
use serde_derive::{Deserialize, Serialize}; use serde::Serialize;
use serde_derive::Deserialize;
#[derive( #[derive(
new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters, new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters,
@ -12,10 +13,23 @@ pub struct Spanned<T> {
pub item: T, pub item: T,
} }
impl<T> Spanned<T> {
pub fn spanned(self, span: impl Into<Span>) -> Spanned<T> {
Spanned::from_item(self.item, span.into())
}
}
pub trait SpannedItem: Sized { pub trait SpannedItem: Sized {
fn spanned(self, span: impl Into<Span>) -> Spanned<Self> { fn spanned(self, span: impl Into<Span>) -> Spanned<Self> {
Spanned::from_item(self, span.into()) 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<Self> {
Spanned::from_item(self, (0, 0))
}
} }
impl<T> SpannedItem for T {} impl<T> SpannedItem for T {}
@ -64,6 +78,15 @@ pub struct Span {
// source: &'source str, // source: &'source str,
} }
impl From<Option<Span>> for Span {
fn from(input: Option<Span>) -> Span {
match input {
None => Span { start: 0, end: 0 },
Some(span) => span,
}
}
}
impl<T> From<&Spanned<T>> for Span { impl<T> From<&Spanned<T>> for Span {
fn from(input: &Spanned<T>) -> Span { fn from(input: &Spanned<T>) -> Span {
input.span input.span
@ -113,6 +136,14 @@ impl From<&std::ops::Range<usize>> for Span {
} }
impl 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 { pub fn slice(&self, source: &'a str) -> &'a str {
&source[self.start..self.end] &source[self.start..self.end]
} }

View File

@ -1,5 +1,5 @@
use crate::errors::{ArgumentError, ShellError}; use crate::errors::{ArgumentError, ShellError};
use crate::parser::registry::{CommandConfig, CommandRegistry, NamedType}; use crate::parser::registry::{CommandConfig, CommandRegistry, NamedType, PositionalType};
use crate::parser::{baseline_parse_tokens, CallNode, Span, Spanned}; use crate::parser::{baseline_parse_tokens, CallNode, Span, Spanned};
use crate::parser::{ use crate::parser::{
hir::{self, NamedArguments}, hir::{self, NamedArguments},
@ -78,7 +78,7 @@ fn parse_command_tail(
trace_remaining("nodes", tail.clone(), source); trace_remaining("nodes", tail.clone(), source);
for (name, kind) in config.named() { for (name, kind) in config.named() {
trace!("looking for {} : {:?}", name, kind); trace!(target: "nu::parse", "looking for {} : {:?}", name, kind);
match kind { match kind {
NamedType::Switch => { NamedType::Switch => {
@ -86,17 +86,18 @@ fn parse_command_tail(
named.insert_switch(name, flag); named.insert_switch(name, flag);
} }
NamedType::Mandatory(kind) => match extract_mandatory(name, tail, source, command_span) NamedType::Mandatory(kind) => {
{ match extract_mandatory(config, name, tail, source, command_span) {
Err(err) => return Err(err), // produce a correct diagnostic Err(err) => return Err(err), // produce a correct diagnostic
Ok((pos, flag)) => { Ok((pos, flag)) => {
tail.move_to(pos); tail.move_to(pos);
if tail.at_end() { if tail.at_end() {
return Err(ShellError::ArgumentError { return Err(ShellError::argument_error(
error: ArgumentError::MissingValueForName(name.to_string()), config.name.clone(),
span: flag.span, ArgumentError::MissingValueForName(name.to_string()),
}); flag.span,
));
} }
let expr = hir::baseline_parse_next_expr( let expr = hir::baseline_parse_next_expr(
@ -109,17 +110,19 @@ fn parse_command_tail(
tail.restart(); tail.restart();
named.insert_mandatory(name, expr); named.insert_mandatory(name, expr);
} }
}, }
}
NamedType::Optional(kind) => match extract_optional(name, tail, source) { NamedType::Optional(kind) => match extract_optional(name, tail, source) {
Err(err) => return Err(err), // produce a correct diagnostic Err(err) => return Err(err), // produce a correct diagnostic
Ok(Some((pos, flag))) => { Ok(Some((pos, flag))) => {
tail.move_to(pos); tail.move_to(pos);
if tail.at_end() { if tail.at_end() {
return Err(ShellError::ArgumentError { return Err(ShellError::argument_error(
error: ArgumentError::MissingValueForName(name.to_string()), config.name().clone(),
span: flag.span, ArgumentError::MissingValueForName(name.to_string()),
}); flag.span,
));
} }
let expr = hir::baseline_parse_next_expr( let expr = hir::baseline_parse_next_expr(
@ -144,38 +147,34 @@ fn parse_command_tail(
trace_remaining("after named", tail.clone(), source); trace_remaining("after named", tail.clone(), source);
let mut positional = vec![]; let mut positional = vec![];
let mandatory = config.mandatory_positional();
for arg in mandatory { for arg in config.positional() {
trace!("Processing mandatory {:?}", arg); trace!("Processing positional {:?}", arg);
match arg {
PositionalType::Mandatory(..) => {
if tail.len() == 0 { if tail.len() == 0 {
return Err(ShellError::ArgumentError { return Err(ShellError::argument_error(
error: ArgumentError::MissingMandatoryPositional(arg.name().to_string()), config.name().clone(),
span: command_span, ArgumentError::MissingMandatoryPositional(arg.name().to_string()),
}); command_span,
));
}
} }
let result = hir::baseline_parse_next_expr(tail, registry, source, arg.to_coerce_hint())?; PositionalType::Optional(..) => {
positional.push(result);
}
trace_remaining("after mandatory", tail.clone(), source);
let optional = config.optional_positional();
for arg in optional {
if tail.len() == 0 { if tail.len() == 0 {
break; break;
} }
}
}
let result = hir::baseline_parse_next_expr(tail, registry, source, arg.to_coerce_hint())?; let result = hir::baseline_parse_next_expr(tail, registry, source, arg.to_coerce_hint())?;
positional.push(result); positional.push(result);
} }
trace_remaining("after optional", tail.clone(), source); trace_remaining("after positional", tail.clone(), source);
// TODO: Only do this if rest params are specified // TODO: Only do this if rest params are specified
let remainder = baseline_parse_tokens(tail, registry, source)?; let remainder = baseline_parse_tokens(tail, registry, source)?;
@ -207,6 +206,7 @@ fn extract_switch(name: &str, tokens: &mut hir::TokensIterator<'_>, source: &Tex
} }
fn extract_mandatory( fn extract_mandatory(
config: &CommandConfig,
name: &str, name: &str,
tokens: &mut hir::TokensIterator<'a>, tokens: &mut hir::TokensIterator<'a>,
source: &Text, source: &Text,
@ -215,10 +215,11 @@ fn extract_mandatory(
let flag = tokens.extract(|t| t.as_flag(name, source)); let flag = tokens.extract(|t| t.as_flag(name, source));
match flag { match flag {
None => Err(ShellError::ArgumentError { None => Err(ShellError::argument_error(
error: ArgumentError::MissingMandatoryFlag(name.to_string()), config.name().clone(),
ArgumentError::MissingMandatoryFlag(name.to_string()),
span, span,
}), )),
Some((pos, flag)) => { Some((pos, flag)) => {
tokens.remove(pos); tokens.remove(pos);

View File

@ -36,22 +36,39 @@ impl NamedValue {
#[allow(unused)] #[allow(unused)]
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PositionalType { pub enum PositionalType {
Value(String), Mandatory(String, PositionalValue),
Block(String), Optional(String, PositionalValue),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PositionalValue {
Value,
Block,
} }
impl PositionalType { impl PositionalType {
pub fn mandatory(name: &str) -> PositionalType {
PositionalType::Mandatory(name.to_string(), PositionalValue::Value)
}
pub fn mandatory_block(name: &str) -> PositionalType {
PositionalType::Mandatory(name.to_string(), PositionalValue::Block)
}
crate fn to_coerce_hint(&self) -> Option<ExpressionKindHint> { crate fn to_coerce_hint(&self) -> Option<ExpressionKindHint> {
match self { match self {
PositionalType::Value(_) => None, PositionalType::Mandatory(_, PositionalValue::Block)
PositionalType::Block(_) => Some(ExpressionKindHint::Block), | PositionalType::Optional(_, PositionalValue::Block) => {
Some(ExpressionKindHint::Block)
}
_ => None,
} }
} }
crate fn name(&self) -> &str { crate fn name(&self) -> &str {
match self { match self {
PositionalType::Value(s) => s, PositionalType::Mandatory(s, _) => s,
PositionalType::Block(s) => s, PositionalType::Optional(s, _) => s,
} }
} }
} }
@ -60,14 +77,11 @@ impl PositionalType {
#[get = "crate"] #[get = "crate"]
pub struct CommandConfig { pub struct CommandConfig {
pub name: String, pub name: String,
pub mandatory_positional: Vec<PositionalType>, pub positional: Vec<PositionalType>,
pub optional_positional: Vec<PositionalType>,
pub rest_positional: bool, pub rest_positional: bool,
pub named: IndexMap<String, NamedType>, pub named: IndexMap<String, NamedType>,
pub is_filter: bool, pub is_filter: bool,
pub is_sink: bool, pub is_sink: bool,
pub can_load: Vec<String>,
pub can_save: Vec<String>,
} }
#[derive(Debug, Default, new, Serialize, Deserialize)] #[derive(Debug, Default, new, Serialize, Deserialize)]
@ -87,7 +101,7 @@ impl fmt::Debug for DebugPositional<'a> {
None => write!(f, "None"), None => write!(f, "None"),
Some(positional) => f Some(positional) => f
.debug_list() .debug_list()
.entries(positional.iter().map(|p| p.item().debug())) .entries(positional.iter().map(|p| p.debug()))
.finish(), .finish(),
} }
} }
@ -104,7 +118,7 @@ impl fmt::Debug for DebugNamed<'a> {
None => write!(f, "None"), None => write!(f, "None"),
Some(named) => f Some(named) => f
.debug_map() .debug_map()
.entries(named.iter().map(|(k, v)| (k, v.item().debug()))) .entries(named.iter().map(|(k, v)| (k, v.debug())))
.finish(), .finish(),
} }
} }

View File

@ -1,4 +1,4 @@
use crate::{Args, CommandConfig, ReturnValue, ShellError, Value}; use crate::{Args, CommandConfig, ReturnValue, ShellError, Spanned, Value};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io; use std::io;
@ -11,11 +11,11 @@ pub trait Plugin {
)) ))
} }
#[allow(unused)] #[allow(unused)]
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, ShellError> { fn filter(&mut self, input: Spanned<Value>) -> Result<Vec<ReturnValue>, ShellError> {
Err(ShellError::string("`filter` not implemented in plugin")) Err(ShellError::string("`filter` not implemented in plugin"))
} }
#[allow(unused)] #[allow(unused)]
fn sink(&mut self, args: Args, input: Vec<Value>) {} fn sink(&mut self, args: Args, input: Vec<Spanned<Value>>) {}
fn quit(&mut self) { fn quit(&mut self) {
return; return;
@ -33,7 +33,11 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) {
send_response(plugin.config()); send_response(plugin.config());
} }
Ok(NuCommand::begin_filter { params }) => { Ok(NuCommand::begin_filter { params }) => {
let _ = plugin.begin_filter(params); send_response(
plugin
.begin_filter(params)
.map(|_| Vec::<ReturnValue>::new()),
);
} }
Ok(NuCommand::filter { params }) => { Ok(NuCommand::filter { params }) => {
send_response(plugin.filter(params)); send_response(plugin.filter(params));
@ -66,7 +70,11 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) {
send_response(plugin.config()); send_response(plugin.config());
} }
Ok(NuCommand::begin_filter { params }) => { Ok(NuCommand::begin_filter { params }) => {
let _ = plugin.begin_filter(params); send_response(
plugin
.begin_filter(params)
.map(|_| Vec::<ReturnValue>::new()),
);
} }
Ok(NuCommand::filter { params }) => { Ok(NuCommand::filter { params }) => {
send_response(plugin.filter(params)); send_response(plugin.filter(params));
@ -127,7 +135,7 @@ fn send_response<T: Serialize>(result: T) {
pub enum NuCommand { pub enum NuCommand {
config, config,
begin_filter { params: Args }, begin_filter { params: Args },
filter { params: Value }, filter { params: Spanned<Value> },
sink { params: (Args, Vec<Value>) }, sink { params: (Args, Vec<Spanned<Value>>) },
quit, quit,
} }

View File

@ -1,6 +1,6 @@
use crossterm::{cursor, terminal, Attribute, RawScreen}; use crossterm::{cursor, terminal, Attribute, RawScreen};
use indexmap::IndexMap; use indexmap::IndexMap;
use nu::{serve_plugin, Args, CommandConfig, Plugin, ShellError, Value}; use nu::{serve_plugin, Args, CommandConfig, Plugin, ShellError, Spanned, Value};
use pretty_hex::*; use pretty_hex::*;
struct BinaryView; struct BinaryView;
@ -15,10 +15,7 @@ impl Plugin for BinaryView {
fn config(&mut self) -> Result<CommandConfig, ShellError> { fn config(&mut self) -> Result<CommandConfig, ShellError> {
Ok(CommandConfig { Ok(CommandConfig {
name: "binaryview".to_string(), name: "binaryview".to_string(),
mandatory_positional: vec![], positional: vec![],
optional_positional: vec![],
can_load: vec![],
can_save: vec![],
is_filter: false, is_filter: false,
is_sink: true, is_sink: true,
named: IndexMap::new(), named: IndexMap::new(),
@ -26,10 +23,13 @@ impl Plugin for BinaryView {
}) })
} }
fn sink(&mut self, _args: Args, input: Vec<Value>) { fn sink(&mut self, _args: Args, input: Vec<Spanned<Value>>) {
for v in input { for v in input {
match v { match v {
Value::Binary(b) => { Spanned {
item: Value::Binary(b),
..
} => {
let _ = view_binary(&b); let _ = view_binary(&b);
} }
_ => {} _ => {}

View File

@ -1,7 +1,7 @@
use indexmap::IndexMap; use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, Args, CommandConfig, Plugin, PositionalType, Primitive, ReturnValue, ShellError, serve_plugin, Args, CommandConfig, Plugin, PositionalType, Primitive, ReturnSuccess,
Spanned, Value, ReturnValue, ShellError, Spanned, SpannedItem, Value,
}; };
struct Inc { struct Inc {
@ -17,10 +17,7 @@ impl Plugin for Inc {
fn config(&mut self) -> Result<CommandConfig, ShellError> { fn config(&mut self) -> Result<CommandConfig, ShellError> {
Ok(CommandConfig { Ok(CommandConfig {
name: "inc".to_string(), name: "inc".to_string(),
mandatory_positional: vec![], positional: vec![PositionalType::mandatory("Increment")],
optional_positional: vec![PositionalType::Value("Increment".into())],
can_load: vec![],
can_save: vec![],
is_filter: true, is_filter: true,
is_sink: false, is_sink: false,
named: IndexMap::new(), named: IndexMap::new(),
@ -45,14 +42,16 @@ impl Plugin for Inc {
Ok(()) Ok(())
} }
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, ShellError> { fn filter(&mut self, input: Spanned<Value>) -> Result<Vec<ReturnValue>, ShellError> {
match input { let span = input.span;
Value::Primitive(Primitive::Int(i)) => {
Ok(vec![ReturnValue::Value(Value::int(i + self.inc_by))]) match input.item {
} Value::Primitive(Primitive::Int(i)) => Ok(vec![ReturnSuccess::value(
Value::Primitive(Primitive::Bytes(b)) => Ok(vec![ReturnValue::Value(Value::bytes( Value::int(i + self.inc_by).spanned(span),
b + self.inc_by as u64, )]),
))]), Value::Primitive(Primitive::Bytes(b)) => Ok(vec![ReturnSuccess::value(
Value::bytes(b + self.inc_by as u64).spanned(span),
)]),
x => Err(ShellError::string(format!( x => Err(ShellError::string(format!(
"Unrecognized type in stream: {:?}", "Unrecognized type in stream: {:?}",
x x

View File

@ -1,6 +1,7 @@
use indexmap::IndexMap; use indexmap::IndexMap;
use nu::{ use nu::{
serve_plugin, Args, CommandConfig, Plugin, Primitive, ReturnValue, ShellError, Spanned, Value, serve_plugin, Args, CommandConfig, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError,
Spanned, Value,
}; };
struct NewSkip { struct NewSkip {
@ -16,10 +17,7 @@ impl Plugin for NewSkip {
fn config(&mut self) -> Result<CommandConfig, ShellError> { fn config(&mut self) -> Result<CommandConfig, ShellError> {
Ok(CommandConfig { Ok(CommandConfig {
name: "skip".to_string(), name: "skip".to_string(),
mandatory_positional: vec![], positional: vec![],
optional_positional: vec![],
can_load: vec![],
can_save: vec![],
is_filter: true, is_filter: true,
is_sink: false, is_sink: false,
named: IndexMap::new(), named: IndexMap::new(),
@ -36,7 +34,7 @@ impl Plugin for NewSkip {
} => { } => {
self.skip_amount = i; self.skip_amount = i;
} }
_ => return Err(ShellError::string("Unrecognized type in params")), _ => return Err(ShellError::labeled_error("Unrecognized type in params", "expected an integer", arg.span)),
} }
} }
} }
@ -44,9 +42,9 @@ impl Plugin for NewSkip {
Ok(()) Ok(())
} }
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, ShellError> { fn filter(&mut self, input: Spanned<Value>) -> Result<Vec<ReturnValue>, ShellError> {
if self.skip_amount == 0 { if self.skip_amount == 0 {
Ok(vec![ReturnValue::Value(input)]) Ok(vec![ReturnSuccess::value(input)])
} else { } else {
self.skip_amount -= 1; self.skip_amount -= 1;
Ok(vec![]) Ok(vec![])

View File

@ -1,6 +1,6 @@
use derive_new::new; use derive_new::new;
use indexmap::IndexMap; use indexmap::IndexMap;
use nu::{serve_plugin, Args, CommandConfig, Plugin, ShellError, Value}; use nu::{serve_plugin, Args, CommandConfig, Plugin, ShellError, Spanned, Value};
use ptree::item::StringItem; use ptree::item::StringItem;
use ptree::output::print_tree_with; use ptree::output::print_tree_with;
use ptree::print_config::PrintConfig; use ptree::print_config::PrintConfig;
@ -31,7 +31,6 @@ impl TreeView {
} }
} }
Value::Block(_) => {} Value::Block(_) => {}
Value::Error(_) => {}
Value::Filesystem => {} Value::Filesystem => {}
Value::Binary(_) => {} Value::Binary(_) => {}
} }
@ -85,10 +84,7 @@ impl Plugin for TreeViewer {
fn config(&mut self) -> Result<CommandConfig, ShellError> { fn config(&mut self) -> Result<CommandConfig, ShellError> {
Ok(CommandConfig { Ok(CommandConfig {
name: "tree".to_string(), name: "tree".to_string(),
mandatory_positional: vec![], positional: vec![],
optional_positional: vec![],
can_load: vec![],
can_save: vec![],
is_filter: false, is_filter: false,
is_sink: true, is_sink: true,
named: IndexMap::new(), named: IndexMap::new(),
@ -96,12 +92,11 @@ impl Plugin for TreeViewer {
}) })
} }
fn sink(&mut self, _args: Args, input: Vec<Value>) { fn sink(&mut self, _args: Args, input: Vec<Spanned<Value>>) {
if input.len() > 0 { if input.len() > 0 {
for i in input.iter() { for i in input.iter() {
let view = TreeView::from_value(&i); let view = TreeView::from_value(&i);
let _ = view.render_view(); let _ = view.render_view();
//handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host));
} }
} }

View File

@ -1,13 +1,84 @@
#[macro_export]
macro_rules! stream {
($($expr:expr),*) => {{
let mut v = VecDeque::new();
$(
v.push_back($expr);
)*
v
}}
}
#[macro_export]
macro_rules! trace_stream {
(target: $target:tt, $desc:tt = $expr:expr) => {{
if log::log_enabled!(target: $target, log::Level::Trace) {
use futures::stream::StreamExt;
// Blocking is generally quite bad, but this is for debugging
// let mut local = futures::executor::LocalPool::new();
// let objects = local.run_until($expr.into_vec());
// let objects: Vec<_> = futures::executor::block_on($expr.into_vec());
let objects = $expr.values.inspect(|o| {
trace!(target: $target, "{} = {:#?}", $desc, o.debug());
});
$crate::stream::InputStream::from_stream(objects.boxed())
} else {
$expr
}
}};
}
crate use crate::cli::MaybeOwned; crate use crate::cli::MaybeOwned;
crate use crate::commands::command::{Command, CommandAction, CommandArgs, ReturnValue}; crate use crate::commands::command::{
Command, CommandAction, CommandArgs, ReturnSuccess, ReturnValue,
};
crate use crate::context::Context; crate use crate::context::Context;
crate use crate::env::host::handle_unexpected; crate use crate::env::host::handle_unexpected;
crate use crate::env::{Environment, Host}; crate use crate::env::{Environment, Host};
crate use crate::errors::ShellError; crate use crate::errors::ShellError;
crate use crate::object::types::ExtractType;
crate use crate::object::{Primitive, Value}; crate use crate::object::{Primitive, Value};
crate use crate::stream::{single_output, InputStream, OutputStream}; crate use crate::parser::{Span, Spanned, SpannedItem};
crate use crate::stream::{InputStream, OutputStream};
crate use crate::Text; crate use crate::Text;
crate use futures::stream::BoxStream;
crate use futures::Stream;
crate use futures::{FutureExt, StreamExt}; crate use futures::{FutureExt, StreamExt};
crate use std::collections::VecDeque; crate use std::collections::VecDeque;
crate use std::pin::Pin; crate use std::future::Future;
crate use std::sync::{Arc, Mutex}; crate use std::sync::{Arc, Mutex};
pub trait FromInputStream {
fn from_input_stream(self) -> OutputStream;
}
impl<T> FromInputStream for T
where
T: Stream<Item = Spanned<Value>> + Send + 'static,
{
fn from_input_stream(self) -> OutputStream {
OutputStream {
values: self.map(ReturnSuccess::value).boxed(),
}
}
}
pub trait ToOutputStream {
fn to_output_stream(self) -> OutputStream;
}
impl<T, U> ToOutputStream for T
where
T: Stream<Item = U> + Send + 'static,
U: Into<ReturnValue>,
{
fn to_output_stream(self) -> OutputStream {
OutputStream {
values: self.map(|item| item.into()).boxed(),
}
}
}

View File

@ -1,12 +1,123 @@
use crate::prelude::*; use crate::prelude::*;
use futures::stream::BoxStream;
pub type InputStream = BoxStream<'static, Value>; pub struct InputStream {
pub type OutputStream = BoxStream<'static, ReturnValue>; crate values: BoxStream<'static, Spanned<Value>>,
}
crate fn single_output(item: Value) -> OutputStream {
let value = ReturnValue::Value(item); impl InputStream {
let mut vec = VecDeque::new(); pub fn into_vec(self) -> impl Future<Output = Vec<Spanned<Value>>> {
vec.push_back(value); self.values.collect()
vec.boxed() }
pub fn from_stream(input: impl Stream<Item = Spanned<Value>> + Send + 'static) -> InputStream {
InputStream {
values: input.boxed(),
}
}
}
impl From<BoxStream<'static, Spanned<Value>>> for InputStream {
fn from(input: BoxStream<'static, Spanned<Value>>) -> InputStream {
InputStream { values: input }
}
}
impl From<VecDeque<Spanned<Value>>> for InputStream {
fn from(input: VecDeque<Spanned<Value>>) -> InputStream {
InputStream {
values: input.boxed(),
}
}
}
impl From<Vec<Spanned<Value>>> for InputStream {
fn from(input: Vec<Spanned<Value>>) -> InputStream {
let mut list = VecDeque::default();
list.extend(input);
InputStream {
values: list.boxed(),
}
}
}
pub struct OutputStream {
crate values: BoxStream<'static, ReturnValue>,
}
impl OutputStream {
pub fn empty() -> OutputStream {
let v: VecDeque<ReturnValue> = VecDeque::new();
v.into()
}
pub fn from_input(input: impl Stream<Item = Spanned<Value>> + Send + 'static) -> OutputStream {
OutputStream {
values: input.map(ReturnSuccess::value).boxed(),
}
}
}
impl From<InputStream> for OutputStream {
fn from(input: InputStream) -> OutputStream {
OutputStream {
values: input.values.map(ReturnSuccess::value).boxed(),
}
}
}
impl From<BoxStream<'static, Spanned<Value>>> for OutputStream {
fn from(input: BoxStream<'static, Spanned<Value>>) -> OutputStream {
OutputStream {
values: input.map(ReturnSuccess::value).boxed(),
}
}
}
impl From<BoxStream<'static, ReturnValue>> for OutputStream {
fn from(input: BoxStream<'static, ReturnValue>) -> OutputStream {
OutputStream { values: input }
}
}
impl From<VecDeque<ReturnValue>> for OutputStream {
fn from(input: VecDeque<ReturnValue>) -> OutputStream {
OutputStream {
values: input.boxed(),
}
}
}
impl From<VecDeque<Spanned<Value>>> for OutputStream {
fn from(input: VecDeque<Spanned<Value>>) -> OutputStream {
OutputStream {
values: input
.into_iter()
.map(|i| ReturnSuccess::value(i))
.collect::<VecDeque<ReturnValue>>()
.boxed(),
}
}
}
impl From<Vec<ReturnValue>> for OutputStream {
fn from(input: Vec<ReturnValue>) -> OutputStream {
let mut list = VecDeque::default();
list.extend(input);
OutputStream {
values: list.boxed(),
}
}
}
impl From<Vec<Spanned<Value>>> for OutputStream {
fn from(input: Vec<Spanned<Value>>) -> OutputStream {
let mut list = VecDeque::default();
list.extend(input.into_iter().map(ReturnSuccess::value));
OutputStream {
values: list.boxed(),
}
}
} }