mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
Add pretty errors to commands
This commit is contained in:
parent
2d13069ca3
commit
e94d1d2758
26 changed files with 318 additions and 166 deletions
49
src/cli.rs
49
src/cli.rs
|
@ -122,11 +122,12 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
rl.add_history_entry(line.clone());
|
rl.add_history_entry(line.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
LineResult::Error(err) => match err {
|
LineResult::Error(mut line, err) => match err {
|
||||||
ShellError::Diagnostic(diag, source) => {
|
ShellError::Diagnostic(diag) => {
|
||||||
let host = context.host.lock().unwrap();
|
let host = context.host.lock().unwrap();
|
||||||
let writer = host.err_termcolor();
|
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(
|
language_reporting::emit(
|
||||||
&mut writer.lock(),
|
&mut writer.lock(),
|
||||||
|
@ -149,7 +150,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.stdout(&format!("Missing property {}", subpath)),
|
.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 => {
|
LineResult::Break => {
|
||||||
|
@ -172,7 +173,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
enum LineResult {
|
enum LineResult {
|
||||||
Success(String),
|
Success(String),
|
||||||
Error(ShellError),
|
Error(String, ShellError),
|
||||||
Break,
|
Break,
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
@ -186,13 +187,13 @@ impl std::ops::Try for LineResult {
|
||||||
fn into_result(self) -> Result<Option<String>, ShellError> {
|
fn into_result(self) -> Result<Option<String>, ShellError> {
|
||||||
match self {
|
match self {
|
||||||
LineResult::Success(s) => Ok(Some(s)),
|
LineResult::Success(s) => Ok(Some(s)),
|
||||||
LineResult::Error(s) => Err(s),
|
LineResult::Error(_, s) => Err(s),
|
||||||
LineResult::Break => Ok(None),
|
LineResult::Break => Ok(None),
|
||||||
LineResult::FatalError(err) => Err(err),
|
LineResult::FatalError(err) => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn from_error(v: ShellError) -> Self {
|
fn from_error(v: ShellError) -> Self {
|
||||||
LineResult::Error(v)
|
LineResult::Error(String::new(), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_ok(v: Option<String>) -> Self {
|
fn from_ok(v: Option<String>) -> Self {
|
||||||
|
@ -212,7 +213,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
let result = match crate::parser::parse(&line) {
|
let result = match crate::parser::parse(&line) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return LineResult::Error(err);
|
return LineResult::Error(line.to_string(), err);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
|
@ -228,6 +229,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
||||||
Some(ClassifiedCommand::External(_)) => {}
|
Some(ClassifiedCommand::External(_)) => {}
|
||||||
_ => pipeline.commands.push(ClassifiedCommand::Sink(SinkCommand {
|
_ => pipeline.commands.push(ClassifiedCommand::Sink(SinkCommand {
|
||||||
command: sink("autoview", autoview::autoview),
|
command: sink("autoview", autoview::autoview),
|
||||||
|
name_span: None,
|
||||||
args: Args {
|
args: Args {
|
||||||
positional: vec![],
|
positional: vec![],
|
||||||
named: indexmap::IndexMap::new(),
|
named: indexmap::IndexMap::new(),
|
||||||
|
@ -247,19 +249,19 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
||||||
(None, _) => break,
|
(None, _) => break,
|
||||||
|
|
||||||
(Some(ClassifiedCommand::Expr(_)), _) => {
|
(Some(ClassifiedCommand::Expr(_)), _) => {
|
||||||
return LineResult::Error(ShellError::unimplemented(
|
return LineResult::Error(line.to_string(), ShellError::unimplemented(
|
||||||
"Expression-only commands",
|
"Expression-only commands",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, Some(ClassifiedCommand::Expr(_))) => {
|
(_, Some(ClassifiedCommand::Expr(_))) => {
|
||||||
return LineResult::Error(ShellError::unimplemented(
|
return LineResult::Error(line.to_string(), ShellError::unimplemented(
|
||||||
"Expression-only commands",
|
"Expression-only commands",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
(Some(ClassifiedCommand::Sink(_)), Some(_)) => {
|
(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) => {
|
(Some(ClassifiedCommand::Sink(left)), None) => {
|
||||||
|
@ -276,7 +278,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
||||||
Some(ClassifiedCommand::External(_)),
|
Some(ClassifiedCommand::External(_)),
|
||||||
) => match left.run(ctx, input).await {
|
) => match left.run(ctx, input).await {
|
||||||
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
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(_),
|
Some(_),
|
||||||
) => match left.run(ctx, input).await {
|
) => match left.run(ctx, input).await {
|
||||||
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
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) => {
|
(Some(ClassifiedCommand::Internal(left)), None) => {
|
||||||
match left.run(ctx, input).await {
|
match left.run(ctx, input).await {
|
||||||
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
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(_)),
|
Some(ClassifiedCommand::External(_)),
|
||||||
) => match left.run(ctx, input, StreamNext::External).await {
|
) => match left.run(ctx, input, StreamNext::External).await {
|
||||||
Ok(val) => val,
|
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(_),
|
Some(_),
|
||||||
) => match left.run(ctx, input, StreamNext::Internal).await {
|
) => match left.run(ctx, input, StreamNext::Internal).await {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(err) => return LineResult::Error(err),
|
Err(err) => return LineResult::Error(line.to_string(), err),
|
||||||
},
|
},
|
||||||
|
|
||||||
(Some(ClassifiedCommand::External(left)), None) => {
|
(Some(ClassifiedCommand::External(left)), None) => {
|
||||||
match left.run(ctx, input, StreamNext::Last).await {
|
match left.run(ctx, input, StreamNext::Last).await {
|
||||||
Ok(val) => val,
|
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())
|
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) => {
|
Err(ReadlineError::Eof) => {
|
||||||
println!("CTRL-D");
|
println!("CTRL-D");
|
||||||
LineResult::Break
|
LineResult::Break
|
||||||
|
@ -365,7 +369,7 @@ fn classify_command(
|
||||||
(
|
(
|
||||||
Expression {
|
Expression {
|
||||||
expr: RawExpression::Leaf(Leaf::Bare(name)),
|
expr: RawExpression::Leaf(Leaf::Bare(name)),
|
||||||
..
|
span,
|
||||||
},
|
},
|
||||||
args,
|
args,
|
||||||
) => match context.has_command(&name.to_string()) {
|
) => match context.has_command(&name.to_string()) {
|
||||||
|
@ -381,6 +385,7 @@ fn classify_command(
|
||||||
|
|
||||||
Ok(ClassifiedCommand::Internal(InternalCommand {
|
Ok(ClassifiedCommand::Internal(InternalCommand {
|
||||||
command,
|
command,
|
||||||
|
name_span: Some(span.clone()),
|
||||||
args,
|
args,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -395,7 +400,11 @@ fn classify_command(
|
||||||
None => Args::default(),
|
None => Args::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ClassifiedCommand::Sink(SinkCommand { command, args }))
|
Ok(ClassifiedCommand::Sink(SinkCommand {
|
||||||
|
command,
|
||||||
|
name_span: Some(span.clone()),
|
||||||
|
args,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
let arg_list_strings: Vec<String> = match args {
|
let arg_list_strings: Vec<String> = match args {
|
||||||
|
|
|
@ -11,12 +11,34 @@ pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
},
|
},
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
let target = v.as_string()?.clone();
|
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 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));
|
stream.push_back(ReturnValue::change_cwd(path));
|
||||||
Ok(stream.boxed())
|
Ok(stream.boxed())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::commands::command::Sink;
|
use crate::commands::command::Sink;
|
||||||
use crate::parser::ast::Expression;
|
use crate::parser::ast::Expression;
|
||||||
|
use crate::parser::lexer::Span;
|
||||||
use crate::parser::registry::Args;
|
use crate::parser::registry::Args;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
|
@ -86,17 +87,19 @@ crate enum ClassifiedCommand {
|
||||||
|
|
||||||
crate struct SinkCommand {
|
crate struct SinkCommand {
|
||||||
crate command: Arc<dyn Sink>,
|
crate command: Arc<dyn Sink>,
|
||||||
|
crate name_span: Option<Span>,
|
||||||
crate args: Args,
|
crate args: Args,
|
||||||
}
|
}
|
||||||
|
|
||||||
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<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 struct InternalCommand {
|
||||||
crate command: Arc<dyn Command>,
|
crate command: Arc<dyn Command>,
|
||||||
|
crate name_span: Option<Span>,
|
||||||
crate args: Args,
|
crate args: Args,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +109,12 @@ impl InternalCommand {
|
||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
input: ClassifiedInputStream,
|
input: ClassifiedInputStream,
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> 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 env = context.env.clone();
|
||||||
|
|
||||||
let stream = result.filter_map(move |v| match v {
|
let stream = result.filter_map(move |v| match v {
|
||||||
|
|
|
@ -1,56 +1,28 @@
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::object::Value;
|
use crate::object::Value;
|
||||||
|
use crate::parser::lexer::Span;
|
||||||
|
use crate::parser::lexer::Spanned;
|
||||||
use crate::parser::CommandConfig;
|
use crate::parser::CommandConfig;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use core::future::Future;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub struct CommandArgs {
|
pub struct CommandArgs {
|
||||||
pub host: Arc<Mutex<dyn Host + Send>>,
|
pub host: Arc<Mutex<dyn Host + Send>>,
|
||||||
pub env: Arc<Mutex<Environment>>,
|
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 named: indexmap::IndexMap<String, Value>,
|
||||||
pub input: InputStream,
|
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 struct SinkCommandArgs {
|
||||||
pub ctx: Context,
|
pub ctx: Context,
|
||||||
pub positional: Vec<Value>,
|
pub name_span: Option<Span>,
|
||||||
|
pub positional: Vec<Spanned<Value>>,
|
||||||
pub named: indexmap::IndexMap<String, Value>,
|
pub named: indexmap::IndexMap<String, Value>,
|
||||||
pub input: Vec<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)]
|
#[derive(Debug)]
|
||||||
pub enum CommandAction {
|
pub enum CommandAction {
|
||||||
ChangeCwd(PathBuf),
|
ChangeCwd(PathBuf),
|
||||||
|
|
|
@ -4,7 +4,30 @@ use crate::prelude::*;
|
||||||
// TODO: "Amount remaining" wrapper
|
// TODO: "Amount remaining" wrapper
|
||||||
|
|
||||||
pub fn first(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
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;
|
let input = args.input;
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,16 @@ fn get_member(path: &str, obj: &Value) -> Option<Value> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
if args.positional.is_empty() {
|
if args.positional.len() == 0 {
|
||||||
return Err(ShellError::string("select requires a field"));
|
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();
|
let fields: Result<Vec<String>, _> = args.positional.iter().map(|a| a.as_string()).collect();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::object::{dir_entry_dict, Primitive, Value};
|
use crate::object::{dir_entry_dict, Primitive, Value};
|
||||||
|
use crate::parser::lexer::Spanned;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::path::{Path, PathBuf};
|
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 cwd = args.env.lock().unwrap().cwd().to_path_buf();
|
||||||
let mut full_path = PathBuf::from(cwd);
|
let mut full_path = PathBuf::from(cwd);
|
||||||
match &args.positional.get(0) {
|
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 =
|
let entries = std::fs::read_dir(&full_path);
|
||||||
std::fs::read_dir(&full_path).map_err(|e| ShellError::string(format!("{:?}", e)))?;
|
|
||||||
|
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();
|
let mut shell_entries = VecDeque::new();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::object::{Primitive, Value};
|
use crate::object::{Primitive, Value};
|
||||||
|
use crate::parser::lexer::Spanned;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::path::{Path, PathBuf};
|
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 cwd = args.env.lock().unwrap().cwd().to_path_buf();
|
||||||
let mut full_path = PathBuf::from(cwd);
|
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 mut stream = VecDeque::new();
|
||||||
|
|
||||||
let open_raw = match args.positional.get(1) {
|
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,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,16 @@ 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> {
|
||||||
if args.positional.is_empty() {
|
if args.positional.len() == 0 {
|
||||||
return Err(ShellError::string("select requires a field"));
|
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();
|
let fields: Result<Vec<String>, _> = args.positional.iter().map(|a| a.as_string()).collect();
|
||||||
|
|
|
@ -4,8 +4,16 @@ 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> {
|
||||||
if args.positional.is_empty() {
|
if args.positional.len() == 0 {
|
||||||
return Err(ShellError::string("select requires a field"));
|
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();
|
let fields: Result<Vec<String>, _> = args.positional.iter().map(|a| a.as_string()).collect();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::commands::command::SinkCommandArgs;
|
use crate::commands::command::SinkCommandArgs;
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::object::{Primitive, Value};
|
use crate::object::{Primitive, Value};
|
||||||
use crate::prelude::*;
|
use crate::parser::lexer::Spanned;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> {
|
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 cwd = args.ctx.env.lock().unwrap().cwd().to_path_buf();
|
||||||
let mut full_path = PathBuf::from(cwd);
|
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)),
|
Value::Primitive(Primitive::String(s)) => full_path.push(Path::new(s)),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let save_raw = match args.positional.get(1) {
|
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,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,30 @@ use crate::errors::ShellError;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn skip(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
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;
|
let input = args.input;
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,31 @@ use crate::prelude::*;
|
||||||
use prettyprint::PrettyPrinter;
|
use prettyprint::PrettyPrinter;
|
||||||
|
|
||||||
pub fn view(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
pub fn view(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
let target = match args.positional.first() {
|
if args.positional.len() == 0 {
|
||||||
// TODO: This needs better infra
|
if let Some(span) = args.name_span {
|
||||||
None => return Err(ShellError::string(format!("cat must take one arg"))),
|
return Err(ShellError::labeled_error(
|
||||||
Some(v) => v.as_string()?.clone(),
|
"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();
|
let cwd = args.env.lock().unwrap().cwd().to_path_buf();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::commands::command::Sink;
|
use crate::commands::command::Sink;
|
||||||
use crate::commands::command::SinkCommandArgs;
|
use crate::commands::command::SinkCommandArgs;
|
||||||
|
use crate::parser::lexer::Span;
|
||||||
use crate::parser::Args;
|
use crate::parser::Args;
|
||||||
use crate::prelude::*;
|
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 {
|
crate fn has_sink(&self, name: &str) -> bool {
|
||||||
self.sinks.contains_key(name)
|
self.sinks.contains_key(name)
|
||||||
}
|
}
|
||||||
|
@ -52,11 +49,13 @@ impl Context {
|
||||||
crate fn run_sink(
|
crate fn run_sink(
|
||||||
&mut self,
|
&mut self,
|
||||||
command: Arc<dyn Sink>,
|
command: Arc<dyn Sink>,
|
||||||
|
name_span: Option<Span>,
|
||||||
args: Args,
|
args: Args,
|
||||||
input: Vec<Value>,
|
input: Vec<Value>,
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
let command_args = SinkCommandArgs {
|
let command_args = SinkCommandArgs {
|
||||||
ctx: self.clone(),
|
ctx: self.clone(),
|
||||||
|
name_span,
|
||||||
positional: args.positional,
|
positional: args.positional,
|
||||||
named: args.named,
|
named: args.named,
|
||||||
input,
|
input,
|
||||||
|
@ -80,12 +79,14 @@ impl Context {
|
||||||
crate fn run_command(
|
crate fn run_command(
|
||||||
&mut self,
|
&mut self,
|
||||||
command: Arc<dyn Command>,
|
command: Arc<dyn Command>,
|
||||||
|
name_span: Option<Span>,
|
||||||
args: Args,
|
args: Args,
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let command_args = CommandArgs {
|
let command_args = CommandArgs {
|
||||||
host: self.host.clone(),
|
host: self.host.clone(),
|
||||||
env: self.env.clone(),
|
env: self.env.clone(),
|
||||||
|
name_span,
|
||||||
positional: args.positional,
|
positional: args.positional,
|
||||||
named: args.named,
|
named: args.named,
|
||||||
input,
|
input,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::parser::lexer::{Span, SpannedToken};
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use language_reporting::Diagnostic;
|
use language_reporting::{Diagnostic, Label, Severity};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -11,13 +11,12 @@ pub enum ShellError {
|
||||||
String(StringError),
|
String(StringError),
|
||||||
TypeError(String),
|
TypeError(String),
|
||||||
MissingProperty { subpath: String, expr: String },
|
MissingProperty { subpath: String, expr: String },
|
||||||
Diagnostic(ShellDiagnostic, String),
|
Diagnostic(ShellDiagnostic),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShellError {
|
impl ShellError {
|
||||||
crate fn parse_error(
|
crate fn parse_error(
|
||||||
error: lalrpop_util::ParseError<usize, SpannedToken, ShellError>,
|
error: lalrpop_util::ParseError<usize, SpannedToken, ShellError>,
|
||||||
source: String,
|
|
||||||
) -> ShellError {
|
) -> ShellError {
|
||||||
use lalrpop_util::ParseError;
|
use lalrpop_util::ParseError;
|
||||||
use language_reporting::*;
|
use language_reporting::*;
|
||||||
|
@ -33,15 +32,26 @@ impl ShellError {
|
||||||
)
|
)
|
||||||
.with_label(Label::new_primary(Span::from((start, end))));
|
.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)),
|
other => ShellError::string(format!("{:?}", other)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
crate fn diagnostic(diagnostic: Diagnostic<Span>, source: String) -> ShellError {
|
crate fn diagnostic(diagnostic: Diagnostic<Span>) -> ShellError {
|
||||||
ShellError::Diagnostic(ShellDiagnostic { diagnostic }, source)
|
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 {
|
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::String(s) => write!(f, "{}", &s.title),
|
||||||
ShellError::TypeError { .. } => write!(f, "TypeError"),
|
ShellError::TypeError { .. } => write!(f, "TypeError"),
|
||||||
ShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
|
ShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
|
||||||
ShellError::Diagnostic(_, _) => write!(f, "<diagnostic>"),
|
ShellError::Diagnostic(_) => write!(f, "<diagnostic>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::object::Primitive;
|
use crate::object::Primitive;
|
||||||
use crate::parser::ast;
|
use crate::parser::ast;
|
||||||
|
use crate::parser::lexer::Spanned;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use indexmap::IndexMap;
|
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::*;
|
use ast::*;
|
||||||
match &expr.expr {
|
match &expr.expr {
|
||||||
RawExpression::Call(_) => Err(ShellError::unimplemented("Evaluating call expression")),
|
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::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::Block(b) => evaluate_block(&b, scope),
|
||||||
RawExpression::Path(p) => evaluate_path(&p, scope),
|
RawExpression::Path(p) => evaluate_path(&p, scope),
|
||||||
RawExpression::Binary(b) => evaluate_binary(b, 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 left = evaluate_expr(&binary.left, scope)?;
|
||||||
let right = evaluate_expr(&binary.right, scope)?;
|
let right = evaluate_expr(&binary.right, scope)?;
|
||||||
|
|
||||||
match left.compare(&binary.operator, &right) {
|
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!(
|
None => Err(ShellError::TypeError(format!(
|
||||||
"Can't compare {} and {}",
|
"Can't compare {} and {}",
|
||||||
left.type_name(),
|
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> {
|
fn evaluate_block(block: &ast::Block, _scope: &Scope) -> Result<Spanned<Value>, ShellError> {
|
||||||
Ok(Value::block(block.expr.clone()))
|
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 head = path.head();
|
||||||
let mut value = &evaluate_expr(head, scope)?;
|
let mut value = evaluate_expr(head, scope)?;
|
||||||
let mut seen = vec![];
|
let mut seen = vec![];
|
||||||
|
|
||||||
for name in path.tail() {
|
for name in path.tail() {
|
||||||
|
@ -93,9 +108,9 @@ fn evaluate_path(path: &ast::Path, scope: &Scope) -> Result<Value, ShellError> {
|
||||||
subpath: itertools::join(seen, "."),
|
subpath: itertools::join(seen, "."),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some(v) => value = v,
|
Some(v) => value = Spanned::from_item(v.copy(), name.span.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(value.copy())
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,8 @@ crate mod tree;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
crate use entries::{EntriesListView, EntriesView};
|
crate use entries::EntriesView;
|
||||||
crate use generic::GenericView;
|
crate use generic::GenericView;
|
||||||
crate use list::ListView;
|
|
||||||
crate use table::TableView;
|
crate use table::TableView;
|
||||||
crate use tree::TreeView;
|
crate use tree::TreeView;
|
||||||
|
|
||||||
|
|
|
@ -55,36 +55,3 @@ impl RenderView for EntriesView {
|
||||||
Ok(())
|
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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::format::{EntriesView, ListView, RenderView, TableView, TreeView};
|
use crate::format::{EntriesView, RenderView, TableView};
|
||||||
use crate::object::Value;
|
use crate::object::Value;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
|
|
@ -21,7 +21,9 @@ pub struct TreeView {
|
||||||
impl TreeView {
|
impl TreeView {
|
||||||
fn from_value_helper(value: &Value, mut builder: &mut TreeBuilder) {
|
fn from_value_helper(value: &Value, mut builder: &mut TreeBuilder) {
|
||||||
match value {
|
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) => {
|
Value::Object(o) => {
|
||||||
for (k, v) in o.entries.iter() {
|
for (k, v) in o.entries.iter() {
|
||||||
builder = builder.begin_child(k.name.display().to_string());
|
builder = builder.begin_child(k.name.display().to_string());
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::errors::ShellError;
|
||||||
use crate::evaluate::{evaluate_expr, Scope};
|
use crate::evaluate::{evaluate_expr, Scope};
|
||||||
use crate::object::DataDescriptor;
|
use crate::object::DataDescriptor;
|
||||||
use crate::parser::ast::{self, Operator};
|
use crate::parser::ast::{self, Operator};
|
||||||
|
use crate::parser::lexer::Spanned;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use ansi_term::Color;
|
use ansi_term::Color;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
@ -142,7 +143,7 @@ impl Deserialize<'de> for Block {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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());
|
let scope = Scope::new(value.copy());
|
||||||
evaluate_expr(&self.expression, &scope)
|
evaluate_expr(&self.expression, &scope)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub fn parse(input: &str) -> Result<Pipeline, ShellError> {
|
||||||
|
|
||||||
match parser.parse(tokens) {
|
match parser.parse(tokens) {
|
||||||
Ok(val) => Ok(val),
|
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) {
|
fn assert_parse(source: &str, expected: Pipeline) {
|
||||||
let parsed = match parse(source) {
|
let parsed = match parse(source) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(ShellError::Diagnostic(diag, source)) => {
|
Err(ShellError::Diagnostic(diag)) => {
|
||||||
use language_reporting::termcolor;
|
use language_reporting::termcolor;
|
||||||
|
|
||||||
let writer = termcolor::StandardStream::stdout(termcolor::ColorChoice::Auto);
|
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(
|
language_reporting::emit(
|
||||||
&mut writer.lock(),
|
&mut writer.lock(),
|
||||||
|
|
|
@ -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::*;
|
use language_reporting::*;
|
||||||
|
|
||||||
ShellError::diagnostic(
|
ShellError::diagnostic(
|
||||||
Diagnostic::new(Severity::Error, "Lex error")
|
Diagnostic::new(Severity::Error, "Lex error")
|
||||||
.with_label(Label::new_primary(Span::new(range))),
|
.with_label(Label::new_primary(Span::new(range))),
|
||||||
source.to_string(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::evaluate::{evaluate_expr, Scope};
|
use crate::evaluate::{evaluate_expr, Scope};
|
||||||
|
use crate::parser::lexer::Spanned;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use indexmap::IndexMap;
|
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 {
|
match self {
|
||||||
PositionalType::Value(_) => evaluate_expr(&arg, scope),
|
PositionalType::Value(_) => evaluate_expr(&arg, scope),
|
||||||
PositionalType::Block(_) => match arg {
|
PositionalType::Block(_) => match arg {
|
||||||
ast::Expression {
|
ast::Expression {
|
||||||
expr: ast::RawExpression::Block(b),
|
expr: ast::RawExpression::Block(b),
|
||||||
..
|
..
|
||||||
} => Ok(Value::block(b.expr)),
|
} => Ok(Spanned::from_item(Value::block(b.expr), arg.span.clone())),
|
||||||
ast::Expression {
|
ast::Expression {
|
||||||
expr: ast::RawExpression::Binary(binary),
|
expr: ast::RawExpression::Binary(binary),
|
||||||
..
|
..
|
||||||
|
@ -52,11 +57,14 @@ impl PositionalType {
|
||||||
// TODO: Use original spans
|
// TODO: Use original spans
|
||||||
let mut b = ast::ExpressionBuilder::new();
|
let mut b = ast::ExpressionBuilder::new();
|
||||||
if let Some(s) = binary.left.as_string() {
|
if let Some(s) = binary.left.as_string() {
|
||||||
Ok(Value::block(b.binary((
|
Ok(Spanned::from_item(
|
||||||
&|b| b.path((&|b| b.var("it"), vec![s.clone()])),
|
Value::block(b.binary((
|
||||||
&|_| binary.operator.clone(),
|
&|b| b.path((&|b| b.var("it"), vec![s.clone()])),
|
||||||
&|_| binary.right.clone(),
|
&|_| binary.operator.clone(),
|
||||||
))))
|
&|_| binary.right.clone(),
|
||||||
|
))),
|
||||||
|
arg.span.clone(),
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
let mut b = ast::ExpressionBuilder::new();
|
let mut b = ast::ExpressionBuilder::new();
|
||||||
let expr = b.binary((
|
let expr = b.binary((
|
||||||
|
@ -65,10 +73,13 @@ impl PositionalType {
|
||||||
&|_| binary.right.clone(),
|
&|_| 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)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
pub positional: Vec<Value>,
|
pub positional: Vec<Spanned<Value>>,
|
||||||
pub named: IndexMap<String, Value>,
|
pub named: IndexMap<String, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +106,7 @@ impl CommandConfig {
|
||||||
args: impl Iterator<Item = &'expr ast::Expression>,
|
args: impl Iterator<Item = &'expr ast::Expression>,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
) -> Result<Args, ShellError> {
|
) -> 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 named: IndexMap<String, Value> = IndexMap::default();
|
||||||
|
|
||||||
let mut args: Vec<ast::Expression> = args.cloned().collect();
|
let mut args: Vec<ast::Expression> = args.cloned().collect();
|
||||||
|
@ -152,7 +163,7 @@ impl CommandConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.rest_positional {
|
if self.rest_positional {
|
||||||
let rest: Result<Vec<Value>, _> =
|
let rest: Result<Vec<Spanned<Value>>, _> =
|
||||||
args.map(|i| evaluate_expr(&i, &Scope::empty())).collect();
|
args.map(|i| evaluate_expr(&i, &Scope::empty())).collect();
|
||||||
positional.extend(rest?);
|
positional.extend(rest?);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,7 +7,7 @@ crate use crate::errors::ShellError;
|
||||||
crate use crate::object::Value;
|
crate use crate::object::Value;
|
||||||
crate use crate::parser::ast;
|
crate use crate::parser::ast;
|
||||||
crate use crate::stream::{single_output, InputStream, OutputStream};
|
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::collections::VecDeque;
|
||||||
crate use std::pin::Pin;
|
crate use std::pin::Pin;
|
||||||
crate use std::sync::{Arc, Mutex};
|
crate use std::sync::{Arc, Mutex};
|
||||||
|
|
|
@ -4,10 +4,6 @@ use futures::stream::BoxStream;
|
||||||
pub type InputStream = BoxStream<'static, Value>;
|
pub type InputStream = BoxStream<'static, Value>;
|
||||||
pub type OutputStream = BoxStream<'static, ReturnValue>;
|
pub type OutputStream = BoxStream<'static, ReturnValue>;
|
||||||
|
|
||||||
crate fn empty_stream() -> OutputStream {
|
|
||||||
VecDeque::new().boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
crate fn single_output(item: Value) -> OutputStream {
|
crate fn single_output(item: Value) -> OutputStream {
|
||||||
let value = ReturnValue::Value(item);
|
let value = ReturnValue::Value(item);
|
||||||
let mut vec = VecDeque::new();
|
let mut vec = VecDeque::new();
|
||||||
|
|
Loading…
Reference in a new issue