Add pretty errors to commands

This commit is contained in:
Jonathan Turner 2019-06-08 10:35:07 +12:00
parent 2d13069ca3
commit e94d1d2758
26 changed files with 318 additions and 166 deletions

View File

@ -122,11 +122,12 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
rl.add_history_entry(line.clone());
}
LineResult::Error(err) => match err {
ShellError::Diagnostic(diag, source) => {
LineResult::Error(mut line, err) => match err {
ShellError::Diagnostic(diag) => {
let host = context.host.lock().unwrap();
let writer = host.err_termcolor();
let files = crate::parser::span::Files::new(source);
line.push_str(" ");
let files = crate::parser::span::Files::new(line);
language_reporting::emit(
&mut writer.lock(),
@ -149,7 +150,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
.unwrap()
.stdout(&format!("Missing property {}", subpath)),
ShellError::String(s) => context.host.lock().unwrap().stdout(&format!("{:?}", s)),
ShellError::String(_) => context.host.lock().unwrap().stdout(&format!("{}", err)),
},
LineResult::Break => {
@ -172,7 +173,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
enum LineResult {
Success(String),
Error(ShellError),
Error(String, ShellError),
Break,
#[allow(unused)]
@ -186,13 +187,13 @@ impl std::ops::Try for LineResult {
fn into_result(self) -> Result<Option<String>, ShellError> {
match self {
LineResult::Success(s) => Ok(Some(s)),
LineResult::Error(s) => Err(s),
LineResult::Error(_, s) => Err(s),
LineResult::Break => Ok(None),
LineResult::FatalError(err) => Err(err),
}
}
fn from_error(v: ShellError) -> Self {
LineResult::Error(v)
LineResult::Error(String::new(), v)
}
fn from_ok(v: Option<String>) -> Self {
@ -212,7 +213,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
Ok(line) => {
let result = match crate::parser::parse(&line) {
Err(err) => {
return LineResult::Error(err);
return LineResult::Error(line.to_string(), err);
}
Ok(val) => val,
@ -228,6 +229,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
Some(ClassifiedCommand::External(_)) => {}
_ => pipeline.commands.push(ClassifiedCommand::Sink(SinkCommand {
command: sink("autoview", autoview::autoview),
name_span: None,
args: Args {
positional: vec![],
named: indexmap::IndexMap::new(),
@ -247,19 +249,19 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
(None, _) => break,
(Some(ClassifiedCommand::Expr(_)), _) => {
return LineResult::Error(ShellError::unimplemented(
return LineResult::Error(line.to_string(), ShellError::unimplemented(
"Expression-only commands",
))
}
(_, Some(ClassifiedCommand::Expr(_))) => {
return LineResult::Error(ShellError::unimplemented(
return LineResult::Error(line.to_string(), ShellError::unimplemented(
"Expression-only commands",
))
}
(Some(ClassifiedCommand::Sink(_)), Some(_)) => {
return LineResult::Error(ShellError::string("Commands like table, save, and autoview must come last in the pipeline"))
return LineResult::Error(line.to_string(), ShellError::string("Commands like table, save, and autoview must come last in the pipeline"))
}
(Some(ClassifiedCommand::Sink(left)), None) => {
@ -276,7 +278,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
Some(ClassifiedCommand::External(_)),
) => match left.run(ctx, input).await {
Ok(val) => ClassifiedInputStream::from_input_stream(val),
Err(err) => return LineResult::Error(err),
Err(err) => return LineResult::Error(line.to_string(), err),
},
(
@ -284,13 +286,13 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
Some(_),
) => match left.run(ctx, input).await {
Ok(val) => ClassifiedInputStream::from_input_stream(val),
Err(err) => return LineResult::Error(err),
Err(err) => return LineResult::Error(line.to_string(), err),
},
(Some(ClassifiedCommand::Internal(left)), None) => {
match left.run(ctx, input).await {
Ok(val) => ClassifiedInputStream::from_input_stream(val),
Err(err) => return LineResult::Error(err),
Err(err) => return LineResult::Error(line.to_string(), err),
}
}
@ -299,7 +301,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
Some(ClassifiedCommand::External(_)),
) => match left.run(ctx, input, StreamNext::External).await {
Ok(val) => val,
Err(err) => return LineResult::Error(err),
Err(err) => return LineResult::Error(line.to_string(), err),
},
(
@ -307,13 +309,13 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
Some(_),
) => match left.run(ctx, input, StreamNext::Internal).await {
Ok(val) => val,
Err(err) => return LineResult::Error(err),
Err(err) => return LineResult::Error(line.to_string(), err),
},
(Some(ClassifiedCommand::External(left)), None) => {
match left.run(ctx, input, StreamNext::Last).await {
Ok(val) => val,
Err(err) => return LineResult::Error(err),
Err(err) => return LineResult::Error(line.to_string(), err),
}
}
}
@ -321,7 +323,9 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
LineResult::Success(line.to_string())
}
Err(ReadlineError::Interrupted) => LineResult::Error(ShellError::string("CTRL-C")),
Err(ReadlineError::Interrupted) => {
LineResult::Error("".to_string(), ShellError::string("CTRL-C"))
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
LineResult::Break
@ -365,7 +369,7 @@ fn classify_command(
(
Expression {
expr: RawExpression::Leaf(Leaf::Bare(name)),
..
span,
},
args,
) => match context.has_command(&name.to_string()) {
@ -381,6 +385,7 @@ fn classify_command(
Ok(ClassifiedCommand::Internal(InternalCommand {
command,
name_span: Some(span.clone()),
args,
}))
}
@ -395,7 +400,11 @@ fn classify_command(
None => Args::default(),
};
Ok(ClassifiedCommand::Sink(SinkCommand { command, args }))
Ok(ClassifiedCommand::Sink(SinkCommand {
command,
name_span: Some(span.clone()),
args,
}))
}
false => {
let arg_list_strings: Vec<String> = match args {

View File

@ -11,12 +11,34 @@ pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
},
Some(v) => {
let target = v.as_string()?.clone();
dunce::canonicalize(cwd.join(&target).as_path())?
match dunce::canonicalize(cwd.join(&target).as_path()) {
Ok(p) => p,
Err(_) => {
return Err(ShellError::labeled_error(
"Can not change to directory",
"directory not found",
args.positional[0].span.clone(),
));
}
}
}
};
let mut stream = VecDeque::new();
let _ = env::set_current_dir(&path);
match env::set_current_dir(&path) {
Ok(_) => {}
Err(_) => {
if args.positional.len() > 0 {
return Err(ShellError::labeled_error(
"Can not change to directory",
"directory not found",
args.positional[0].span.clone(),
));
} else {
return Err(ShellError::string("Can not change to directory"));
}
}
}
stream.push_back(ReturnValue::change_cwd(path));
Ok(stream.boxed())
}

View File

@ -1,5 +1,6 @@
use crate::commands::command::Sink;
use crate::parser::ast::Expression;
use crate::parser::lexer::Span;
use crate::parser::registry::Args;
use crate::prelude::*;
use bytes::{BufMut, BytesMut};
@ -86,17 +87,19 @@ crate enum ClassifiedCommand {
crate struct SinkCommand {
crate command: Arc<dyn Sink>,
crate name_span: Option<Span>,
crate args: Args,
}
impl SinkCommand {
crate fn run(self, context: &mut Context, input: Vec<Value>) -> Result<(), ShellError> {
context.run_sink(self.command, self.args, input)
context.run_sink(self.command, self.name_span.clone(), self.args, input)
}
}
crate struct InternalCommand {
crate command: Arc<dyn Command>,
crate name_span: Option<Span>,
crate args: Args,
}
@ -106,7 +109,12 @@ impl InternalCommand {
context: &mut Context,
input: ClassifiedInputStream,
) -> Result<InputStream, ShellError> {
let result = context.run_command(self.command, self.args, input.objects)?;
let result = context.run_command(
self.command,
self.name_span.clone(),
self.args,
input.objects,
)?;
let env = context.env.clone();
let stream = result.filter_map(move |v| match v {

View File

@ -1,56 +1,28 @@
use crate::errors::ShellError;
use crate::object::Value;
use crate::parser::lexer::Span;
use crate::parser::lexer::Spanned;
use crate::parser::CommandConfig;
use crate::prelude::*;
use core::future::Future;
use std::path::PathBuf;
pub struct CommandArgs {
pub host: Arc<Mutex<dyn Host + Send>>,
pub env: Arc<Mutex<Environment>>,
pub positional: Vec<Value>,
pub name_span: Option<Span>,
pub positional: Vec<Spanned<Value>>,
pub named: indexmap::IndexMap<String, Value>,
pub input: InputStream,
}
impl CommandArgs {
crate fn from_context(
ctx: &'caller mut Context,
positional: Vec<Value>,
input: InputStream,
) -> CommandArgs {
CommandArgs {
host: ctx.host.clone(),
env: ctx.env.clone(),
positional,
named: indexmap::IndexMap::default(),
input,
}
}
}
pub struct SinkCommandArgs {
pub ctx: Context,
pub positional: Vec<Value>,
pub name_span: Option<Span>,
pub positional: Vec<Spanned<Value>>,
pub named: indexmap::IndexMap<String, Value>,
pub input: Vec<Value>,
}
impl SinkCommandArgs {
crate fn from_context(
ctx: &'caller mut Context,
positional: Vec<Value>,
input: Vec<Value>,
) -> SinkCommandArgs {
SinkCommandArgs {
ctx: ctx.clone(),
positional,
named: indexmap::IndexMap::default(),
input,
}
}
}
#[derive(Debug)]
pub enum CommandAction {
ChangeCwd(PathBuf),

View File

@ -4,7 +4,30 @@ use crate::prelude::*;
// TODO: "Amount remaining" wrapper
pub fn first(args: CommandArgs) -> Result<OutputStream, ShellError> {
let amount = args.positional[0].as_i64()?;
if args.positional.len() == 0 {
if let Some(span) = args.name_span {
return Err(ShellError::labeled_error(
"First requires an amount",
"needs parameter",
span,
));
} else {
return Err(ShellError::string("first requires an amount."));
}
}
let amount = args.positional[0].as_i64();
let amount = match amount {
Ok(o) => o,
Err(_) => {
return Err(ShellError::labeled_error(
"Value is not a number",
"expected integer",
args.positional[0].span,
))
}
};
let input = args.input;

View File

@ -20,8 +20,16 @@ fn get_member(path: &str, obj: &Value) -> Option<Value> {
}
pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.positional.is_empty() {
return Err(ShellError::string("select requires a field"));
if args.positional.len() == 0 {
if let Some(span) = args.name_span {
return Err(ShellError::labeled_error(
"Get requires a field or field path",
"needs parameter",
span,
));
} else {
return Err(ShellError::string("get requires fields."));
}
}
let fields: Result<Vec<String>, _> = args.positional.iter().map(|a| a.as_string()).collect();

View File

@ -1,5 +1,6 @@
use crate::errors::ShellError;
use crate::object::{dir_entry_dict, Primitive, Value};
use crate::parser::lexer::Spanned;
use crate::prelude::*;
use std::path::{Path, PathBuf};
@ -7,12 +8,29 @@ pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
let cwd = args.env.lock().unwrap().cwd().to_path_buf();
let mut full_path = PathBuf::from(cwd);
match &args.positional.get(0) {
Some(Value::Primitive(Primitive::String(s))) => full_path.push(Path::new(s)),
Some(Spanned {
item: Value::Primitive(Primitive::String(s)),
..
}) => full_path.push(Path::new(s)),
_ => {}
}
let entries =
std::fs::read_dir(&full_path).map_err(|e| ShellError::string(format!("{:?}", e)))?;
let entries = std::fs::read_dir(&full_path);
let entries = match entries {
Err(e) => {
if let Some(s) = args.positional.get(0) {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
s.span,
));
} else {
return Err(ShellError::string(e.to_string()));
}
}
Ok(o) => o,
};
let mut shell_entries = VecDeque::new();

View File

@ -1,5 +1,6 @@
use crate::errors::ShellError;
use crate::object::{Primitive, Value};
use crate::parser::lexer::Spanned;
use crate::prelude::*;
use std::path::{Path, PathBuf};
@ -10,17 +11,44 @@ pub fn open(args: CommandArgs) -> Result<OutputStream, ShellError> {
let cwd = args.env.lock().unwrap().cwd().to_path_buf();
let mut full_path = PathBuf::from(cwd);
match &args.positional[0] {
Value::Primitive(Primitive::String(s)) => full_path.push(Path::new(s)),
_ => {}
}
let contents = std::fs::read_to_string(&full_path).unwrap();
let contents = match &args.positional[0].item {
Value::Primitive(Primitive::String(s)) => {
full_path.push(Path::new(&s));
match std::fs::read_to_string(&full_path) {
Ok(s) => s,
Err(_) => {
return Err(ShellError::labeled_error(
"File cound not be opened",
"file not found",
args.positional[0].span,
));
}
}
}
_ => {
return Err(ShellError::labeled_error(
"Expected string value for filename",
"expected filename",
args.positional[0].span,
));
}
};
let mut stream = VecDeque::new();
let open_raw = match args.positional.get(1) {
Some(Value::Primitive(Primitive::String(s))) if s == "--raw" => true,
Some(Spanned {
item: Value::Primitive(Primitive::String(s)),
..
}) if s == "--raw" => true,
Some(v) => {
return Err(ShellError::labeled_error(
"Unknown flag for open",
"unknown flag",
v.span,
))
}
_ => false,
};

View File

@ -4,8 +4,16 @@ use crate::object::Value;
use crate::prelude::*;
pub fn pick(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.positional.is_empty() {
return Err(ShellError::string("select requires a field"));
if args.positional.len() == 0 {
if let Some(span) = args.name_span {
return Err(ShellError::labeled_error(
"Pick requires fields",
"needs parameter",
span,
));
} else {
return Err(ShellError::string("pick requires fields."));
}
}
let fields: Result<Vec<String>, _> = args.positional.iter().map(|a| a.as_string()).collect();

View File

@ -4,8 +4,16 @@ use crate::object::Value;
use crate::prelude::*;
pub fn reject(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.positional.is_empty() {
return Err(ShellError::string("select requires a field"));
if args.positional.len() == 0 {
if let Some(span) = args.name_span {
return Err(ShellError::labeled_error(
"Reject requires fields",
"needs parameter",
span,
));
} else {
return Err(ShellError::string("reject requires fields."));
}
}
let fields: Result<Vec<String>, _> = args.positional.iter().map(|a| a.as_string()).collect();

View File

@ -1,7 +1,7 @@
use crate::commands::command::SinkCommandArgs;
use crate::errors::ShellError;
use crate::object::{Primitive, Value};
use crate::prelude::*;
use crate::parser::lexer::Spanned;
use std::path::{Path, PathBuf};
pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> {
@ -11,13 +11,16 @@ pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> {
let cwd = args.ctx.env.lock().unwrap().cwd().to_path_buf();
let mut full_path = PathBuf::from(cwd);
match &args.positional[0] {
match &(args.positional[0].item) {
Value::Primitive(Primitive::String(s)) => full_path.push(Path::new(s)),
_ => {}
}
let save_raw = match args.positional.get(1) {
Some(Value::Primitive(Primitive::String(s))) if s == "--raw" => true,
Some(Spanned {
item: Value::Primitive(Primitive::String(s)),
..
}) if s == "--raw" => true,
_ => false,
};

View File

@ -2,7 +2,30 @@ use crate::errors::ShellError;
use crate::prelude::*;
pub fn skip(args: CommandArgs) -> Result<OutputStream, ShellError> {
let amount = args.positional[0].as_i64()?;
if args.positional.len() == 0 {
if let Some(span) = args.name_span {
return Err(ShellError::labeled_error(
"Skip requires an amount",
"needs parameter",
span,
));
} else {
return Err(ShellError::string("skip requires an amount."));
}
}
let amount = args.positional[0].as_i64();
let amount = match amount {
Ok(o) => o,
Err(_) => {
return Err(ShellError::labeled_error(
"Value is not a number",
"expected integer",
args.positional[0].span,
))
}
};
let input = args.input;

View File

@ -3,10 +3,31 @@ use crate::prelude::*;
use prettyprint::PrettyPrinter;
pub fn view(args: CommandArgs) -> Result<OutputStream, ShellError> {
let target = match args.positional.first() {
// TODO: This needs better infra
None => return Err(ShellError::string(format!("cat must take one arg"))),
Some(v) => v.as_string()?.clone(),
if args.positional.len() == 0 {
if let Some(span) = args.name_span {
return Err(ShellError::labeled_error(
"View requires a filename",
"needs parameter",
span,
));
} else {
return Err(ShellError::string("view requires a filename."));
}
}
let target = match args.positional[0].as_string() {
Ok(s) => s.clone(),
Err(e) => {
if let Some(span) = args.name_span {
return Err(ShellError::labeled_error(
"Expected a string",
"not a filename",
span,
));
} else {
return Err(e);
}
}
};
let cwd = args.env.lock().unwrap().cwd().to_path_buf();

View File

@ -1,5 +1,6 @@
use crate::commands::command::Sink;
use crate::commands::command::SinkCommandArgs;
use crate::parser::lexer::Span;
use crate::parser::Args;
use crate::prelude::*;
@ -37,10 +38,6 @@ impl Context {
}
}
pub fn clone_sinks(&self) -> indexmap::IndexMap<String, Arc<dyn Sink>> {
self.sinks.clone()
}
crate fn has_sink(&self, name: &str) -> bool {
self.sinks.contains_key(name)
}
@ -52,11 +49,13 @@ impl Context {
crate fn run_sink(
&mut self,
command: Arc<dyn Sink>,
name_span: Option<Span>,
args: Args,
input: Vec<Value>,
) -> Result<(), ShellError> {
let command_args = SinkCommandArgs {
ctx: self.clone(),
name_span,
positional: args.positional,
named: args.named,
input,
@ -80,12 +79,14 @@ impl Context {
crate fn run_command(
&mut self,
command: Arc<dyn Command>,
name_span: Option<Span>,
args: Args,
input: InputStream,
) -> Result<OutputStream, ShellError> {
let command_args = CommandArgs {
host: self.host.clone(),
env: self.env.clone(),
name_span,
positional: args.positional,
named: args.named,
input,

View File

@ -2,7 +2,7 @@ use crate::parser::lexer::{Span, SpannedToken};
#[allow(unused)]
use crate::prelude::*;
use derive_new::new;
use language_reporting::Diagnostic;
use language_reporting::{Diagnostic, Label, Severity};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_derive::{Deserialize, Serialize};
@ -11,13 +11,12 @@ pub enum ShellError {
String(StringError),
TypeError(String),
MissingProperty { subpath: String, expr: String },
Diagnostic(ShellDiagnostic, String),
Diagnostic(ShellDiagnostic),
}
impl ShellError {
crate fn parse_error(
error: lalrpop_util::ParseError<usize, SpannedToken, ShellError>,
source: String,
) -> ShellError {
use lalrpop_util::ParseError;
use language_reporting::*;
@ -33,15 +32,26 @@ impl ShellError {
)
.with_label(Label::new_primary(Span::from((start, end))));
ShellError::diagnostic(diagnostic, source)
ShellError::diagnostic(diagnostic)
}
ParseError::User { error } => error,
other => ShellError::string(format!("{:?}", other)),
}
}
crate fn diagnostic(diagnostic: Diagnostic<Span>, source: String) -> ShellError {
ShellError::Diagnostic(ShellDiagnostic { diagnostic }, source)
crate fn diagnostic(diagnostic: Diagnostic<Span>) -> ShellError {
ShellError::Diagnostic(ShellDiagnostic { diagnostic })
}
crate fn labeled_error(
msg: impl Into<String>,
label: impl Into<String>,
span: Span,
) -> ShellError {
ShellError::diagnostic(
Diagnostic::new(Severity::Error, msg.into())
.with_label(Label::new_primary(span).with_message(label.into())),
)
}
crate fn string(title: impl Into<String>) -> ShellError {
@ -134,7 +144,7 @@ impl std::fmt::Display for ShellError {
ShellError::String(s) => write!(f, "{}", &s.title),
ShellError::TypeError { .. } => write!(f, "TypeError"),
ShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
ShellError::Diagnostic(_, _) => write!(f, "<diagnostic>"),
ShellError::Diagnostic(_) => write!(f, "<diagnostic>"),
}
}
}

View File

@ -1,5 +1,6 @@
use crate::object::Primitive;
use crate::parser::ast;
use crate::parser::lexer::Spanned;
use crate::prelude::*;
use derive_new::new;
use indexmap::IndexMap;
@ -20,17 +21,25 @@ impl Scope {
}
}
crate fn evaluate_expr(expr: &ast::Expression, scope: &Scope) -> Result<Value, ShellError> {
crate fn evaluate_expr(
expr: &ast::Expression,
scope: &Scope,
) -> Result<Spanned<Value>, ShellError> {
use ast::*;
match &expr.expr {
RawExpression::Call(_) => Err(ShellError::unimplemented("Evaluating call expression")),
RawExpression::Leaf(l) => Ok(evaluate_leaf(l)),
RawExpression::Leaf(l) => Ok(Spanned::from_item(evaluate_leaf(l), expr.span.clone())),
RawExpression::Parenthesized(p) => evaluate_expr(&p.expr, scope),
RawExpression::Flag(f) => Ok(Value::Primitive(Primitive::String(f.print()))),
RawExpression::Flag(f) => Ok(Spanned::from_item(
Value::Primitive(Primitive::String(f.print())),
expr.span.clone(),
)),
RawExpression::Block(b) => evaluate_block(&b, scope),
RawExpression::Path(p) => evaluate_path(&p, scope),
RawExpression::Binary(b) => evaluate_binary(b, scope),
RawExpression::VariableReference(r) => evaluate_reference(r, scope),
RawExpression::VariableReference(r) => {
evaluate_reference(r, scope).map(|x| Spanned::from_item(x, expr.span.clone()))
}
}
}
@ -59,12 +68,15 @@ fn evaluate_reference(r: &ast::Variable, scope: &Scope) -> Result<Value, ShellEr
}
}
fn evaluate_binary(binary: &ast::Binary, scope: &Scope) -> Result<Value, ShellError> {
fn evaluate_binary(binary: &ast::Binary, scope: &Scope) -> Result<Spanned<Value>, ShellError> {
let left = evaluate_expr(&binary.left, scope)?;
let right = evaluate_expr(&binary.right, scope)?;
match left.compare(&binary.operator, &right) {
Some(v) => Ok(Value::boolean(v)),
Some(v) => Ok(Spanned::from_item(
Value::boolean(v),
binary.operator.span.clone(),
)),
None => Err(ShellError::TypeError(format!(
"Can't compare {} and {}",
left.type_name(),
@ -73,13 +85,16 @@ fn evaluate_binary(binary: &ast::Binary, scope: &Scope) -> Result<Value, ShellEr
}
}
fn evaluate_block(block: &ast::Block, _scope: &Scope) -> Result<Value, ShellError> {
Ok(Value::block(block.expr.clone()))
fn evaluate_block(block: &ast::Block, _scope: &Scope) -> Result<Spanned<Value>, ShellError> {
Ok(Spanned::from_item(
Value::block(block.expr.clone()),
block.expr.span.clone(),
))
}
fn evaluate_path(path: &ast::Path, scope: &Scope) -> Result<Value, ShellError> {
fn evaluate_path(path: &ast::Path, scope: &Scope) -> Result<Spanned<Value>, ShellError> {
let head = path.head();
let mut value = &evaluate_expr(head, scope)?;
let mut value = evaluate_expr(head, scope)?;
let mut seen = vec![];
for name in path.tail() {
@ -93,9 +108,9 @@ fn evaluate_path(path: &ast::Path, scope: &Scope) -> Result<Value, ShellError> {
subpath: itertools::join(seen, "."),
});
}
Some(v) => value = v,
Some(v) => value = Spanned::from_item(v.copy(), name.span.clone()),
}
}
Ok(value.copy())
Ok(value)
}

View File

@ -6,9 +6,8 @@ crate mod tree;
use crate::prelude::*;
crate use entries::{EntriesListView, EntriesView};
crate use entries::EntriesView;
crate use generic::GenericView;
crate use list::ListView;
crate use table::TableView;
crate use tree::TreeView;

View File

@ -55,36 +55,3 @@ impl RenderView for EntriesView {
Ok(())
}
}
pub struct EntriesListView {
values: VecDeque<Value>,
}
impl EntriesListView {
crate async fn from_stream(values: InputStream) -> EntriesListView {
EntriesListView {
values: values.collect().await,
}
}
}
impl RenderView for EntriesListView {
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> {
if self.values.len() == 0 {
return Ok(());
}
let last = self.values.len() - 1;
for (i, item) in self.values.iter().enumerate() {
let view = EntriesView::from_value(item);
view.render_view(host)?;
if i != last {
host.stdout("\n");
}
}
Ok(())
}
}

View File

@ -1,4 +1,4 @@
use crate::format::{EntriesView, ListView, RenderView, TableView, TreeView};
use crate::format::{EntriesView, RenderView, TableView};
use crate::object::Value;
use crate::prelude::*;
use derive_new::new;

View File

@ -21,7 +21,9 @@ pub struct TreeView {
impl TreeView {
fn from_value_helper(value: &Value, mut builder: &mut TreeBuilder) {
match value {
Value::Primitive(p) => builder = builder.add_empty_child(p.format(None)),
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.name.display().to_string());

View File

@ -2,6 +2,7 @@ use crate::errors::ShellError;
use crate::evaluate::{evaluate_expr, Scope};
use crate::object::DataDescriptor;
use crate::parser::ast::{self, Operator};
use crate::parser::lexer::Spanned;
use crate::prelude::*;
use ansi_term::Color;
use chrono::{DateTime, Utc};
@ -142,7 +143,7 @@ impl Deserialize<'de> for Block {
}
impl Block {
pub fn invoke(&self, value: &Value) -> Result<Value, ShellError> {
pub fn invoke(&self, value: &Value) -> Result<Spanned<Value>, ShellError> {
let scope = Scope::new(value.copy());
evaluate_expr(&self.expression, &scope)
}

View File

@ -26,7 +26,7 @@ pub fn parse(input: &str) -> Result<Pipeline, ShellError> {
match parser.parse(tokens) {
Ok(val) => Ok(val),
Err(err) => Err(ShellError::parse_error(err, input.to_string())),
Err(err) => Err(ShellError::parse_error(err)),
}
}
@ -39,11 +39,11 @@ mod tests {
fn assert_parse(source: &str, expected: Pipeline) {
let parsed = match parse(source) {
Ok(p) => p,
Err(ShellError::Diagnostic(diag, source)) => {
Err(ShellError::Diagnostic(diag)) => {
use language_reporting::termcolor;
let writer = termcolor::StandardStream::stdout(termcolor::ColorChoice::Auto);
let files = crate::parser::span::Files::new(source);
let files = crate::parser::span::Files::new(source.to_string());
language_reporting::emit(
&mut writer.lock(),

View File

@ -558,13 +558,12 @@ impl Iterator for Lexer<'source> {
}
}
fn lex_error(range: &Range<usize>, source: &str) -> ShellError {
fn lex_error(range: &Range<usize>, _source: &str) -> ShellError {
use language_reporting::*;
ShellError::diagnostic(
Diagnostic::new(Severity::Error, "Lex error")
.with_label(Label::new_primary(Span::new(range))),
source.to_string(),
)
}

View File

@ -1,4 +1,5 @@
use crate::evaluate::{evaluate_expr, Scope};
use crate::parser::lexer::Spanned;
use crate::prelude::*;
use indexmap::IndexMap;
@ -37,14 +38,18 @@ impl PositionalType {
}
}
crate fn evaluate(&self, arg: ast::Expression, scope: &Scope) -> Result<Value, ShellError> {
crate fn evaluate(
&self,
arg: ast::Expression,
scope: &Scope,
) -> Result<Spanned<Value>, ShellError> {
match self {
PositionalType::Value(_) => evaluate_expr(&arg, scope),
PositionalType::Block(_) => match arg {
ast::Expression {
expr: ast::RawExpression::Block(b),
..
} => Ok(Value::block(b.expr)),
} => Ok(Spanned::from_item(Value::block(b.expr), arg.span.clone())),
ast::Expression {
expr: ast::RawExpression::Binary(binary),
..
@ -52,11 +57,14 @@ impl PositionalType {
// TODO: Use original spans
let mut b = ast::ExpressionBuilder::new();
if let Some(s) = binary.left.as_string() {
Ok(Value::block(b.binary((
&|b| b.path((&|b| b.var("it"), vec![s.clone()])),
&|_| binary.operator.clone(),
&|_| binary.right.clone(),
))))
Ok(Spanned::from_item(
Value::block(b.binary((
&|b| b.path((&|b| b.var("it"), vec![s.clone()])),
&|_| binary.operator.clone(),
&|_| binary.right.clone(),
))),
arg.span.clone(),
))
} else {
let mut b = ast::ExpressionBuilder::new();
let expr = b.binary((
@ -65,10 +73,13 @@ impl PositionalType {
&|_| binary.right.clone(),
));
Ok(Value::block(expr))
Ok(Spanned::from_item(Value::block(expr), arg.span.clone()))
}
}
other => Ok(Value::block(other)), // other =>
other => {
let span = other.span.clone();
Ok(Spanned::from_item(Value::block(other), span))
}
},
}
}
@ -85,7 +96,7 @@ pub struct CommandConfig {
#[derive(Debug, Default)]
pub struct Args {
pub positional: Vec<Value>,
pub positional: Vec<Spanned<Value>>,
pub named: IndexMap<String, Value>,
}
@ -95,7 +106,7 @@ impl CommandConfig {
args: impl Iterator<Item = &'expr ast::Expression>,
scope: &Scope,
) -> Result<Args, ShellError> {
let mut positional: Vec<Value> = vec![];
let mut positional: Vec<Spanned<Value>> = vec![];
let mut named: IndexMap<String, Value> = IndexMap::default();
let mut args: Vec<ast::Expression> = args.cloned().collect();
@ -152,7 +163,7 @@ impl CommandConfig {
}
if self.rest_positional {
let rest: Result<Vec<Value>, _> =
let rest: Result<Vec<Spanned<Value>>, _> =
args.map(|i| evaluate_expr(&i, &Scope::empty())).collect();
positional.extend(rest?);
} else {

View File

@ -7,7 +7,7 @@ crate use crate::errors::ShellError;
crate use crate::object::Value;
crate use crate::parser::ast;
crate use crate::stream::{single_output, InputStream, OutputStream};
crate use futures::{FutureExt, SinkExt, StreamExt};
crate use futures::{FutureExt, StreamExt};
crate use std::collections::VecDeque;
crate use std::pin::Pin;
crate use std::sync::{Arc, Mutex};

View File

@ -4,10 +4,6 @@ use futures::stream::BoxStream;
pub type InputStream = BoxStream<'static, Value>;
pub type OutputStream = BoxStream<'static, ReturnValue>;
crate fn empty_stream() -> OutputStream {
VecDeque::new().boxed()
}
crate fn single_output(item: Value) -> OutputStream {
let value = ReturnValue::Value(item);
let mut vec = VecDeque::new();