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:
lkg-rust-nightly: "2019-06-28"
lkg-rust-nightly: "2019-07-04"
trigger:
- master

14
Cargo.lock generated
View file

@ -1278,12 +1278,14 @@ dependencies = [
[[package]]
name = "language-reporting"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
source = "git+https://github.com/jonathandturner/language-reporting#0a6c284a19a00b5b6b680480c0ad5f241fc5edac"
dependencies = [
"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)",
"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)",
]
@ -1649,7 +1651,7 @@ dependencies = [
"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)",
"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)",
"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)",
@ -2354,7 +2356,7 @@ dependencies = [
[[package]]
name = "render-tree"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
source = "git+https://github.com/jonathandturner/language-reporting#0a6c284a19a00b5b6b680480c0ad5f241fc5edac"
dependencies = [
"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)",
@ -3497,7 +3499,7 @@ dependencies = [
"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 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 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"
@ -3606,7 +3608,7 @@ dependencies = [
"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 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 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"

View file

@ -40,7 +40,7 @@ serde_derive = "1.0.94"
getset = "0.0.7"
logos = "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"
toml = "0.5.1"
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("cd", Box::new(cd::cd)),
command("view", Box::new(view::view)),
// command("skip", skip::Skip),
command("first", Box::new(first::first)),
command("size", Box::new(size::size)),
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-yaml", Box::new(from_yaml::from_yaml)),
command("get", Box::new(get::get)),
command("enter", Box::new(enter::enter)),
command("exit", Box::new(exit::exit)),
command("lines", Box::new(lines::lines)),
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-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)),
Arc::new(Open),
Arc::new(Where),
Arc::new(Config),
Arc::new(SkipWhile),
command("sort-by", Box::new(sort_by::sort_by)),
Arc::new(Enter),
]);
context.add_sinks(vec![
@ -227,7 +228,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
let last = env.back().unwrap();
(last.obj().clone(), last.path().display().to_string())
};
let readline = match obj {
let readline = match obj.item {
Value::Filesystem => rl.readline(&format!(
"{}{}> ",
cwd,
@ -392,7 +393,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
}
(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) {
return LineResult::Error(line.clone(), err);
}
@ -496,7 +497,7 @@ fn classify_command(
let config = command.config();
let scope = Scope::empty();
trace!("classifying {:?}", config);
trace!(target: "nu::build_pipeline", "classifying {:?}", config);
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 autoview;
crate mod cd;
@ -39,6 +42,7 @@ crate mod where_;
crate use command::command;
crate use config::Config;
crate use enter::Enter;
crate use open::Open;
crate use skip_while::SkipWhile;
crate use where_::Where;

View file

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

View file

@ -6,8 +6,9 @@ use std::path::PathBuf;
pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
let env = args.env.lock().unwrap();
let latest = env.back().unwrap();
let obj = &latest.obj;
match latest.obj {
match obj.item() {
Value::Filesystem => {
let cwd = latest.path().to_path_buf();
@ -52,14 +53,14 @@ pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
}
}
}
stream.push_back(ReturnValue::change_cwd(path));
Ok(stream.boxed())
stream.push_back(ReturnSuccess::change_cwd(path));
Ok(stream.into())
}
_ => {
let mut stream = VecDeque::new();
match args.nth(0) {
None => {
stream.push_back(ReturnValue::change_cwd(PathBuf::from("/")));
stream.push_back(ReturnSuccess::change_cwd(PathBuf::from("/")));
}
Some(v) => {
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 {
crate fn new() -> ClassifiedInputStream {
ClassifiedInputStream {
objects: VecDeque::new().boxed(),
objects: VecDeque::new().into(),
stdin: None,
}
}
crate fn from_input_stream(stream: InputStream) -> ClassifiedInputStream {
crate fn from_input_stream(stream: impl Into<InputStream>) -> ClassifiedInputStream {
ClassifiedInputStream {
objects: stream,
objects: stream.into(),
stdin: None,
}
}
crate fn from_stdout(stdout: std::fs::File) -> ClassifiedInputStream {
ClassifiedInputStream {
objects: VecDeque::new().boxed(),
objects: VecDeque::new().into(),
stdin: Some(stdout),
}
}
@ -86,6 +86,18 @@ crate enum ClassifiedCommand {
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 command: Arc<dyn Sink>,
crate name_span: Option<Span>,
@ -93,7 +105,11 @@ crate struct 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)
}
}
@ -110,31 +126,24 @@ impl InternalCommand {
context: &mut Context,
input: ClassifiedInputStream,
) -> Result<InputStream, ShellError> {
let objects = if log_enabled!(log::Level::Trace) {
trace!("->");
trace!("{}", self.command.name());
trace!("{:?}", 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
};
if log_enabled!(log::Level::Trace) {
trace!(target: "nu::run::internal", "->");
trace!(target: "nu::run::internal", "{}", self.command.name());
trace!(target: "nu::run::internal", "{:?}", self.args.debug());
}
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)?;
let mut result = result.values;
let mut stream = VecDeque::new();
while let Some(item) = result.next().await {
match item {
ReturnValue::Value(Value::Error(err)) => {
return Err(*err);
}
ReturnValue::Action(action) => match action {
match item? {
ReturnSuccess::Action(action) => match action {
CommandAction::ChangePath(path) => {
context.env.lock().unwrap().back_mut().map(|x| {
x.path = path;
@ -150,7 +159,11 @@ impl InternalCommand {
}
CommandAction::Exit => match context.env.lock().unwrap().pop_back() {
Some(Environment {
obj: Value::Filesystem,
obj:
Spanned {
item: Value::Filesystem,
..
},
..
}) => std::process::exit(0),
None => std::process::exit(-1),
@ -158,12 +171,13 @@ impl InternalCommand {
},
},
ReturnValue::Value(v) => {
ReturnSuccess::Value(v) => {
stream.push_back(v);
}
}
}
Ok(stream.boxed() as InputStream)
Ok(stream.into())
}
}
@ -187,9 +201,12 @@ impl ExternalCommand {
input: ClassifiedInputStream,
stream_next: StreamNext,
) -> 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);
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);
}
@ -317,8 +334,11 @@ impl ExternalCommand {
let stdout = popen.stdout.take().unwrap();
let file = futures::io::AllowStdIo::new(stdout);
let stream = Framed::new(file, LinesCodec {});
let stream = stream.map(|line| Value::string(line.unwrap()));
Ok(ClassifiedInputStream::from_input_stream(stream.boxed()))
let stream =
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::errors::ShellError;
use crate::errors::{labelled, ShellError};
use clipboard::{ClipboardContext, ClipboardProvider};
pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> {
let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap();
let mut new_copy_data = String::new();
if args.input.len() > 0 {
let mut first = true;
for i in args.input.iter() {
@ -13,18 +14,17 @@ pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> {
} else {
first = false;
}
match i.as_string() {
Ok(s) => new_copy_data.push_str(&s),
Err(_) => {
return Err(ShellError::maybe_labeled_error(
"Given non-string data",
"expected strings from pipeline",
args.name_span,
))
}
}
let string = i.as_string().map_err(labelled(
args.name_span,
"Given non-string data",
"expected strings from pipeline",
))?;
new_copy_data.push_str(&string);
}
}
clip_context.set_contents(new_copy_data).unwrap();
Ok(())

View file

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

View file

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

View file

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

View file

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

View file

@ -1,55 +1,67 @@
use crate::object::base::OF64;
use crate::object::{Dictionary, Primitive, Value};
use crate::object::{Primitive, SpannedDictBuilder, Value};
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 {
serde_hjson::Value::Null => Value::Primitive(Primitive::String(String::from(""))),
serde_hjson::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)),
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::I64(n) => Value::Primitive(Primitive::Int(*n as i64)),
serde_hjson::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))),
serde_hjson::Value::Null => {
Value::Primitive(Primitive::String(String::from(""))).spanned(span)
}
serde_hjson::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).spanned(span),
serde_hjson::Value::F64(n) => {
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(
a.iter()
.map(|x| convert_json_value_to_nu_value(x))
.map(|x| convert_json_value_to_nu_value(x, span))
.collect(),
),
)
.spanned(span),
serde_hjson::Value::Object(o) => {
let mut collected = Dictionary::default();
let mut collected = SpannedDictBuilder::new(span);
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)?;
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> {
let out = args.input;
let span = args.name_span;
Ok(out
.map(move |a| match a {
Value::Primitive(Primitive::String(s)) => match from_json_string_to_value(s) {
Ok(x) => ReturnValue::Value(x),
Err(_) => {
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Could not parse as JSON",
"piped data failed JSON parse",
span,
))))
}
.values
.map(move |a| match a.item {
Value::Primitive(Primitive::String(s)) => match from_json_string_to_value(s, span) {
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
Err(_) => Err(ShellError::maybe_labeled_error(
"Could not parse as JSON",
"piped data failed JSON parse",
span,
)),
},
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
_ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
span,
)))),
)),
})
.boxed())
.to_output_stream())
}

View file

@ -1,54 +1,65 @@
use crate::object::base::OF64;
use crate::object::{Dictionary, Primitive, Value};
use crate::object::{Primitive, SpannedDictBuilder, Value};
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 {
toml::Value::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)),
toml::Value::Integer(n) => Value::Primitive(Primitive::Int(*n)),
toml::Value::Float(n) => Value::Primitive(Primitive::Float(OF64::from(*n))),
toml::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))),
toml::Value::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)).spanned(span),
toml::Value::Integer(n) => Value::Primitive(Primitive::Int(*n)).spanned(span),
toml::Value::Float(n) => Value::Primitive(Primitive::Float(OF64::from(*n))).spanned(span),
toml::Value::String(s) => {
Value::Primitive(Primitive::String(String::from(s))).spanned(span)
}
toml::Value::Array(a) => Value::List(
a.iter()
.map(|x| convert_toml_value_to_nu_value(x))
.map(|x| convert_toml_value_to_nu_value(x, span))
.collect(),
),
toml::Value::Datetime(dt) => Value::Primitive(Primitive::String(dt.to_string())),
)
.spanned(span),
toml::Value::Datetime(dt) => {
Value::Primitive(Primitive::String(dt.to_string())).spanned(span)
}
toml::Value::Table(t) => {
let mut collected = Dictionary::default();
let mut collected = SpannedDictBuilder::new(span);
for (k, v) in t.iter() {
collected.add(k.clone(), convert_toml_value_to_nu_value(v));
collected.insert_spanned(k.clone(), convert_toml_value_to_nu_value(v, span));
}
Value::Object(collected)
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>()?;
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> {
let out = args.input;
let span = args.name_span;
Ok(out
.map(move |a| match a {
Value::Primitive(Primitive::String(s)) => match from_toml_string_to_value(s) {
Ok(x) => ReturnValue::Value(x),
Err(_) => {
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Could not parse as TOML",
"piped data failed TOML parse",
span,
))))
}
.values
.map(move |a| match a.item {
Value::Primitive(Primitive::String(s)) => match from_toml_string_to_value(s, span) {
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
Err(_) => Err(ShellError::maybe_labeled_error(
"Could not parse as TOML",
"piped data failed TOML parse",
span,
)),
},
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
_ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
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::*;
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() {
let name = n.tag_name().name().trim().to_string();
let mut children_values = vec![];
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()
.filter(|x| match x {
Value::Primitive(Primitive::String(f)) => {
Spanned {
item: Value::Primitive(Primitive::String(f)),
..
} => {
if f.trim() == "" {
false
} else {
@ -24,50 +32,52 @@ fn from_node_to_value<'a, 'd>(n: &roxmltree::Node<'a, 'd>) -> Value {
})
.collect();
let mut collected = Dictionary::default();
collected.add(name.clone(), Value::List(children_values));
let mut collected = SpannedDictBuilder::new(span);
collected.insert(name.clone(), Value::List(children_values));
Value::Object(collected)
collected.into_spanned_value()
} else if n.is_comment() {
Value::string("<comment>")
Value::string("<comment>").spanned(span)
} else if n.is_pi() {
Value::string("<processing_instruction>")
Value::string("<processing_instruction>").spanned(span)
} else if n.is_text() {
Value::string(n.text().unwrap())
Value::string(n.text().unwrap()).spanned(span)
} else {
Value::string("<unknown>")
Value::string("<unknown>").spanned(span)
}
}
fn from_document_to_value(d: &roxmltree::Document) -> Value {
from_node_to_value(&d.root_element())
fn from_document_to_value(d: &roxmltree::Document, span: impl Into<Span>) -> Spanned<Value> {
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)?;
Ok(from_document_to_value(&parsed))
Ok(from_document_to_value(&parsed, span))
}
pub fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input;
let span = args.name_span;
Ok(out
.map(move |a| match a {
Value::Primitive(Primitive::String(s)) => match from_xml_string_to_value(s) {
Ok(x) => ReturnValue::Value(x),
Err(_) => {
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Could not parse as XML",
"piped data failed XML parse",
span,
))))
}
.values
.map(move |a| match a.item {
Value::Primitive(Primitive::String(s)) => match from_xml_string_to_value(s, span) {
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
Err(_) => Err(ShellError::maybe_labeled_error(
"Could not parse as XML",
"piped data failed XML parse",
span,
)),
},
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
_ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
span,
)))),
)),
})
.boxed())
.to_output_stream())
}

View file

@ -1,64 +1,72 @@
use crate::object::base::OF64;
use crate::object::{Dictionary, Primitive, Value};
use crate::object::{Primitive, SpannedDictBuilder, Value};
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 {
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() => {
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() => {
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(
a.iter()
.map(|x| convert_yaml_value_to_nu_value(x))
.map(|x| convert_yaml_value_to_nu_value(x, span))
.collect(),
),
)
.spanned(span),
serde_yaml::Value::Mapping(t) => {
let mut collected = Dictionary::default();
let mut collected = SpannedDictBuilder::new(span);
for (k, v) in t.iter() {
match 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"),
}
}
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),
}
}
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)?;
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> {
let out = args.input;
let span = args.name_span;
Ok(out
.map(move |a| match a {
Value::Primitive(Primitive::String(s)) => match from_yaml_string_to_value(s) {
Ok(x) => ReturnValue::Value(x),
Err(_) => {
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Could not parse as YAML",
"piped data failed YAML parse",
span,
))))
}
.values
.map(move |a| match a.item {
Value::Primitive(Primitive::String(s)) => match from_yaml_string_to_value(s, span) {
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
Err(_) => Err(ShellError::maybe_labeled_error(
"Could not parse as YAML",
"piped data failed YAML parse",
span,
)),
},
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
_ => Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
span,
)))),
)),
})
.boxed())
.to_output_stream())
}

View file

@ -3,22 +3,22 @@ use crate::object::Value;
use crate::parser::Span;
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;
for p in path.split(".") {
match current.get_data_by_key(p) {
Some(v) => current = v,
None => {
return Some(Value::Error(Box::new(ShellError::labeled_error(
return Err(ShellError::labeled_error(
"Unknown field",
"object missing field",
span,
))));
));
}
}
}
Some(current.copy())
Ok(current.clone())
}
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 {
return Ok(args
.input
.values
.skip(amount as u64)
.take(1)
.map(|v| ReturnValue::Value(v))
.boxed());
.from_input_stream());
}
let fields: Result<Vec<(String, Span)>, _> = args
@ -51,17 +51,21 @@ pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
let stream = args
.input
.values
.map(move |item| {
let mut result = VecDeque::new();
for field in &fields {
match get_member(&field.0, field.1, &item) {
Some(Value::List(l)) => {
Ok(Spanned {
item: Value::List(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())),
None => {}
Ok(x) => result.push_back(ReturnSuccess::value(x.clone())),
Err(x) => result.push_back(Err(x)),
}
}
@ -69,5 +73,5 @@ pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
})
.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 stream = input
.map(move |v| match v {
.values
.map(move |v| match v.item {
Value::Primitive(Primitive::String(s)) => {
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();
for s in split_result {
result.push_back(ReturnValue::Value(Value::Primitive(Primitive::String(
s.into(),
))));
result.push_back(ReturnSuccess::value(
Value::Primitive(Primitive::String(s.into())).spanned_unknown(),
));
}
result
}
_ => {
let mut result = VecDeque::new();
result.push_back(ReturnValue::Value(Value::Error(Box::new(
ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
span,
),
))));
result.push_back(Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
span,
)));
result
}
})
.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 => {
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();
for entry in entries {
let value = Value::Object(dir_entry_dict(&entry?)?);
shell_entries.push_back(ReturnValue::Value(value))
let value = dir_entry_dict(&entry?, args.name_span)?;
shell_entries.push_back(ReturnSuccess::value(value))
}
Ok(shell_entries.boxed())
Ok(shell_entries.to_output_stream())
}
_ => {
let mut entries = VecDeque::new();
@ -97,7 +97,11 @@ pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
return Err(ShellError::maybe_labeled_error(
"Index not closed",
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 {
Value::List(l) => {
Spanned {
item: Value::List(l),
..
} => {
for item in l {
entries.push_back(ReturnValue::Value(item.copy()));
entries.push_back(ReturnSuccess::value(item.clone()));
}
}
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::object::{Primitive, Value};
use crate::object::{Primitive, Switch, Value};
use crate::parser::parse::span::Span;
use crate::parser::registry::{CommandConfig, NamedType};
use crate::prelude::*;
use indexmap::IndexMap;
use mime::Mime;
use std::path::{Path, PathBuf};
use std::str::FromStr;
pub struct Open;
command! {
Open as open(args, --raw: Switch) {
let span = args.name_span;
impl Command for Open {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
open(args)
}
fn name(&self) -> &str {
"open"
}
let cwd = args
.env
.lock()
.unwrap()
.front()
.unwrap()
.path()
.to_path_buf();
fn config(&self) -> CommandConfig {
let mut named: IndexMap<String, NamedType> = IndexMap::new();
named.insert("raw".to_string(), NamedType::Switch);
let full_path = PathBuf::from(cwd);
CommandConfig {
name: self.name().to_string(),
mandatory_positional: vec![],
optional_positional: vec![],
rest_positional: false,
named,
is_filter: true,
is_sink: false,
can_load: vec![],
can_save: vec![],
}
let (file_extension, contents, contents_span) = match &args.expect_nth(0)?.item {
Value::Primitive(Primitive::String(s)) => fetch(&full_path, s, args.expect_nth(0)?.span)?,
_ => {
return Err(ShellError::labeled_error(
"Expected string value for filename",
"expected filename",
args.expect_nth(0)?.span,
));
}
};
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,
location: &str,
span: Span,
) -> Result<(Option<String>, Value), ShellError> {
) -> Result<(Option<String>, Value, Span), ShellError> {
let mut cwd = cwd.clone();
if location.starts_with("http:") || location.starts_with("https:") {
let response = reqwest::get(location);
@ -71,7 +113,7 @@ pub fn fetch(
None => path_extension,
};
Ok((extension, Value::string(s)))
Ok((extension, Value::string(s), span))
}
Err(_) => {
return Err(ShellError::labeled_error(
@ -97,8 +139,9 @@ pub fn fetch(
cwd.extension()
.map(|name| name.to_string_lossy().to_string()),
Value::string(s),
span,
)),
Err(_) => Ok((None, Value::Binary(bytes))),
Err(_) => Ok((None, Value::Binary(bytes), span)),
},
Err(_) => {
return Err(ShellError::labeled_error(
@ -114,128 +157,76 @@ pub fn fetch(
pub fn parse_as_value(
extension: Option<String>,
contents: String,
contents_span: Span,
name_span: Option<Span>,
) -> Result<Value, ShellError> {
) -> Result<Spanned<Value>, ShellError> {
match extension {
Some(x) if x == "toml" => crate::commands::from_toml::from_toml_string_to_value(contents)
.map_err(move |_| {
ShellError::maybe_labeled_error(
"Could not open as TOML",
"could not open as TOML",
name_span,
)
}),
Some(x) if x == "json" => crate::commands::from_json::from_json_string_to_value(contents)
.map_err(move |_| {
ShellError::maybe_labeled_error(
"Could not open as JSON",
"could not open as JSON",
name_span,
)
}),
Some(x) if x == "ini" => crate::commands::from_ini::from_ini_string_to_value(contents)
.map_err(move |_| {
ShellError::maybe_labeled_error(
"Could not open as INI",
"could not open as INI",
name_span,
)
}),
Some(x) if x == "xml" => crate::commands::from_xml::from_xml_string_to_value(contents)
.map_err(move |_| {
ShellError::maybe_labeled_error(
"Could not open as XML",
"could not open as XML",
name_span,
)
}),
Some(x) if x == "yml" => crate::commands::from_yaml::from_yaml_string_to_value(contents)
.map_err(move |_| {
ShellError::maybe_labeled_error(
"Could not open as YAML",
"could not open as YAML",
name_span,
)
}),
Some(x) if x == "yaml" => crate::commands::from_yaml::from_yaml_string_to_value(contents)
.map_err(move |_| {
ShellError::maybe_labeled_error(
"Could not open as YAML",
"could not open as YAML",
name_span,
)
}),
_ => Ok(Value::string(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 |_| {
ShellError::maybe_labeled_error(
"Could not open as TOML",
"could not open as TOML",
name_span,
)
})
}
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 |_| {
ShellError::maybe_labeled_error(
"Could not open as JSON",
"could not open as JSON",
name_span,
)
})
}
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 |_| {
ShellError::maybe_labeled_error(
"Could not open as INI",
"could not open as INI",
name_span,
)
})
}
Some(x) if x == "xml" => {
crate::commands::from_xml::from_xml_string_to_value(contents, contents_span).map_err(
move |_| {
ShellError::maybe_labeled_error(
"Could not open as XML",
"could not open as XML",
name_span,
)
},
)
}
Some(x) if x == "yml" => {
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_span).map_err(
move |_| {
ShellError::maybe_labeled_error(
"Could not open as YAML",
"could not open as YAML",
name_span,
)
},
)
}
Some(x) if x == "yaml" => {
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_span).map_err(
move |_| {
ShellError::maybe_labeled_error(
"Could not open as YAML",
"could not open as YAML",
name_span,
)
},
)
}
_ => 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::object::base::select_fields;
use crate::object::Value;
use crate::prelude::*;
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 = fields?;
let input = args.input;
let objects = args
.input
.map(move |item| Value::Object(select_fields(&item, &fields)))
.map(|item| ReturnValue::Value(item));
let objects = input
.values
.map(move |value| select_fields(&value.item, &fields, value.span));
let stream = Pin::new(Box::new(objects));
Ok(stream)
Ok(objects.from_input_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 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_raw = serde_json::to_string(&request).unwrap();
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();
eos.push_back(Value::Primitive(Primitive::EndOfStream));
let mut eos: VecDeque<Spanned<Value>> = VecDeque::new();
eos.push_back(Value::Primitive(Primitive::EndOfStream).spanned_unknown());
let stream = args
.input
.values
.chain(eos)
.map(move |v| match v {
Value::Primitive(Primitive::EndOfStream) => {
Spanned {
item: Value::Primitive(Primitive::EndOfStream),
..
} => {
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
let stdout = child.stdout.as_mut().expect("Failed to open stdout");
@ -87,17 +112,15 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, Sh
Ok(params) => params,
Err(e) => {
let mut result = VecDeque::new();
result.push_back(ReturnValue::Value(Value::Error(Box::new(e))));
result.push_back(ReturnValue::Err(e));
result
}
},
Err(e) => {
let mut result = VecDeque::new();
result.push_back(ReturnValue::Value(Value::Error(Box::new(
ShellError::string(format!(
"Error while processing input: {:?} {}",
e, input
)),
result.push_back(Err(ShellError::string(format!(
"Error while processing input: {:?} {}",
e, input
))));
result
}
@ -105,8 +128,9 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, Sh
}
Err(e) => {
let mut result = VecDeque::new();
result.push_back(ReturnValue::Value(Value::Error(Box::new(
ShellError::string(format!("Error while processing input: {:?}", e)),
result.push_back(Err(ShellError::string(format!(
"Error while processing input: {:?}",
e
))));
result
}
@ -115,7 +139,7 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result<OutputStream, Sh
})
.flatten();
Ok(stream.boxed())
Ok(stream.to_output_stream())
}
pub fn sink_plugin(path: String, args: SinkCommandArgs) -> Result<(), ShellError> {

View file

@ -1,18 +1,17 @@
use crate::errors::ShellError;
use crate::object::process::process_dict;
use crate::object::Value;
use crate::prelude::*;
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());
system.refresh_processes();
let list = system.get_process_list();
let list = list
.into_iter()
.map(|(_, process)| ReturnValue::Value(Value::Object(process_dict(process))))
.map(|(_, process)| process_dict(process, args.name_span))
.collect::<VecDeque<_>>();
Ok(list.boxed())
Ok(list.from_input_stream())
}

View file

@ -1,9 +1,10 @@
use crate::errors::ShellError;
use crate::object::base::reject_fields;
use crate::object::Value;
use crate::prelude::*;
pub fn reject(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.name_span;
if args.len() == 0 {
return Err(ShellError::maybe_labeled_error(
"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 = fields?;
let stream = args
.input
.map(move |item| Value::Object(reject_fields(&item, &fields)))
.map(|item| ReturnValue::Value(item));
let stream = args.input.values.map(move |item| {
reject_fields(&item, &fields, item.span)
.into_spanned_value()
.spanned(name_span)
});
Ok(stream.boxed())
Ok(stream.from_input_stream())
}

View file

@ -1,6 +1,5 @@
use crate::errors::ShellError;
use crate::object::dict::Dictionary;
use crate::object::Value;
use crate::object::{SpannedDictBuilder, Value};
use crate::prelude::*;
use std::fs::File;
use std::io::prelude::*;
@ -24,20 +23,20 @@ pub fn size(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut contents = String::new();
let mut list = VecDeque::new();
for name in args.positional_iter() {
let name = name.as_string()?;
let mut list: VecDeque<ReturnValue> = VecDeque::new();
for spanned_name in args.positional_iter() {
let name = spanned_name.as_string()?;
let path = cwd.join(&name);
let mut file = File::open(path)?;
file.read_to_string(&mut contents)?;
list.push_back(count(&name, &contents));
list.push_back(count(&name, &contents, spanned_name).into());
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 words: i64 = 0;
let mut chars: i64 = 0;
@ -62,12 +61,12 @@ fn count(name: &str, contents: &str) -> ReturnValue {
}
}
let mut dict = Dictionary::default();
dict.add("name", Value::string(name));
dict.add("lines", Value::int(lines));
dict.add("words", Value::int(words));
dict.add("chars", Value::int(chars));
dict.add("max length", Value::int(bytes));
let mut dict = SpannedDictBuilder::new(span);
dict.insert("name", Value::string(name));
dict.insert("lines", Value::int(lines));
dict.insert("words", Value::int(words));
dict.insert("chars", Value::int(chars));
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 {
CommandConfig {
name: self.name().to_string(),
mandatory_positional: vec![PositionalType::Block("condition".to_string())],
optional_positional: vec![],
positional: vec![PositionalType::mandatory_block("condition")],
rest_positional: false,
named: indexmap::IndexMap::new(),
is_filter: true,
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 input = args.input;
let objects = input.skip_while(move |item| {
let objects = input.values.skip_while(move |item| {
let result = block.invoke(&item);
let return_value = match result {
@ -51,5 +48,5 @@ pub fn skip_while(args: CommandArgs) -> Result<OutputStream, ShellError> {
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 = fields?;
let output = args.input.collect::<Vec<_>>();
let output = args.input.values.collect::<Vec<_>>();
let output = output.map(move |mut vec| {
vec.sort_by_key(|item| {
fields
.iter()
.map(|f| item.get_data_by_key(f).map(|i| i.copy()))
.collect::<Vec<Option<Value>>>()
.map(|f| item.get_data_by_key(f).map(|i| i.clone()))
.collect::<Vec<Option<Spanned<Value>>>>()
});
vec.into_iter()
.map(|v| ReturnValue::Value(v.copy()))
.collect::<VecDeque<_>>()
.boxed()
vec.into_iter().collect::<VecDeque<_>>()
});
Ok(output.flatten_stream().boxed())
Ok(output.flatten_stream().from_input_stream())
}

View file

@ -1,5 +1,5 @@
use crate::errors::ShellError;
use crate::object::{Primitive, Value};
use crate::object::{Primitive, SpannedDictBuilder, Value};
use crate::prelude::*;
use log::trace;
@ -8,7 +8,7 @@ use log::trace;
pub fn split_column(args: CommandArgs) -> Result<OutputStream, ShellError> {
let positional: Vec<_> = args.positional_iter().cloned().collect();
let span = args.name_span;
if positional.len() == 0 {
return Err(ShellError::maybe_labeled_error(
"Split-column needs more information",
@ -20,7 +20,8 @@ pub fn split_column(args: CommandArgs) -> Result<OutputStream, ShellError> {
let input = args.input;
Ok(input
.map(move |v| match v {
.values
.map(move |v| match v.item {
Value::Primitive(Primitive::String(s)) => {
let splitter = positional[0].as_string().unwrap().replace("\\n", "\n");
trace!("splitting with {:?}", splitter);
@ -35,36 +36,34 @@ pub fn split_column(args: CommandArgs) -> Result<OutputStream, ShellError> {
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()) {
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) {
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)) {
dict.add(
dict.insert(
v.as_string().unwrap(),
Value::Primitive(Primitive::String(k.into())),
);
}
ReturnValue::Value(Value::Object(dict))
ReturnSuccess::value(dict.into_spanned_value())
} else {
let mut dict = crate::object::Dictionary::default();
let mut dict = SpannedDictBuilder::new(v.span);
for k in positional.iter().skip(1) {
dict.add(
k.as_string().unwrap().trim(),
Value::Primitive(Primitive::String("".into())),
);
dict.insert(k.as_string().unwrap().trim(), 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",
"expects strings from pipeline",
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 stream = input
.map(move |v| match v {
.values
.map(move |v| match v.item {
Value::Primitive(Primitive::String(s)) => {
let splitter = positional[0].as_string().unwrap().replace("\\n", "\n");
trace!("splitting with {:?}", splitter);
@ -31,25 +32,23 @@ pub fn split_row(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut result = VecDeque::new();
for s in split_result {
result.push_back(ReturnValue::Value(Value::Primitive(Primitive::String(
s.into(),
))));
result.push_back(ReturnSuccess::value(
Value::Primitive(Primitive::String(s.into())).spanned(v.span),
));
}
result
}
_ => {
let mut result = VecDeque::new();
result.push_back(ReturnValue::Value(Value::Error(Box::new(
ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
span,
),
))));
result.push_back(Err(ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
span,
)));
result
}
})
.flatten();
Ok(stream.boxed())
Ok(stream.to_output_stream())
}

View file

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

View file

@ -2,9 +2,10 @@ use crate::object::Value;
use crate::prelude::*;
pub fn to_array(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input.collect();
let out = args.input.values.collect();
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()
.boxed())
.from_input_stream())
}

View file

@ -22,7 +22,6 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value {
Value::List(l) => {
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::Binary(b) => serde_json::Value::Array(
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> {
let out = args.input;
let span = args.name_span;
let name_span = args.name_span;
Ok(out
.values
.map(
move |a| match serde_json::to_string(&value_to_json_value(&a)) {
Ok(x) => ReturnValue::Value(Value::Primitive(Primitive::String(x))),
Err(_) => {
ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Can not convert to JSON string",
"can not convert piped data to JSON string",
span,
))))
Ok(x) => {
ReturnSuccess::value(Value::Primitive(Primitive::String(x)).spanned(name_span))
}
Err(_) => Err(ShellError::maybe_labeled_error(
"Can not convert to JSON string",
"can not convert piped data to JSON string",
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::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::Binary(b) => {
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> {
let out = args.input;
let span = args.name_span;
let name_span = args.name_span;
Ok(out
.map(move |a| match toml::to_string(&value_to_toml_value(&a)) {
Ok(x) => ReturnValue::Value(Value::Primitive(Primitive::String(x))),
Err(_) => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Can not convert to TOML string",
"can not convert piped data to TOML string",
span,
)))),
.values
.map(move |a| {
match toml::to_string(&value_to_toml_value(&a)) {
Ok(val) => {
return ReturnSuccess::value(
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::object::{Primitive, Value};
use crate::object::Value;
use crate::prelude::*;
// TODO: "Amount remaining" wrapper
pub fn trim(args: CommandArgs) -> Result<OutputStream, ShellError> {
let input = args.input;
let span = args.name_span;
Ok(input
.map(move |v| match v {
Value::Primitive(Primitive::String(s)) => {
ReturnValue::Value(Value::Primitive(Primitive::String(s.trim().into())))
}
_ => ReturnValue::Value(Value::Error(Box::new(ShellError::maybe_labeled_error(
"Expected string values from pipeline",
"expects strings from pipeline",
span,
)))),
.values
.map(move |v| {
let string = String::extract(&v)?;
ReturnSuccess::value(Value::string(string.trim()).spanned(v.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());
Ok(VecDeque::new().boxed())
Ok(OutputStream::empty())
}

View file

@ -1,55 +1,23 @@
use crate::errors::ShellError;
use crate::parser::registry::{CommandConfig, PositionalType};
use crate::object::Block;
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 {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
r#where(args)
}
fn name(&self) -> &str {
"where"
}
input.values.filter_map(move |item| {
let result = condition.invoke(&item);
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![],
}
let return_value = match result {
Err(err) => Some(Err(err)),
Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))),
_ => None,
};
ready(return_value)
})
}
}
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 {
Err(err) => Some(ReturnValue::Value(Value::Error(Box::new(err)))),
Ok(v) if v.is_true() => Some(ReturnValue::Value(item.copy())),
_ => None,
};
futures::future::ready(return_value)
});
Ok(objects.boxed())
}

View file

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

View file

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

View file

@ -5,7 +5,7 @@ use crate::parser::{Span, Spanned};
use ansi_term::Color;
use derive_new::new;
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)]
pub enum Description {
@ -41,26 +41,20 @@ pub enum ArgumentError {
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)]
pub enum ShellError {
String(StringError),
TypeError {
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>,
},
pub struct ShellError {
error: ProximateShellError,
cause: Option<Box<ProximateShellError>>,
}
impl ShellError {
@ -68,10 +62,39 @@ impl ShellError {
expected: impl Into<String>,
actual: Spanned<impl Into<String>>,
) -> ShellError {
ShellError::TypeError {
ProximateShellError::TypeError {
expected: expected.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(
@ -86,66 +109,56 @@ impl ShellError {
.with_label(Label::new_primary(Span::from(span.0)));
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 {
ShellError::Diagnostic(ShellDiagnostic { diagnostic })
ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start()
}
crate fn to_diagnostic(self) -> Diagnostic<Span> {
match self {
ShellError::String(StringError { title, .. }) => {
match self.error {
ProximateShellError::String(StringError { title, .. }) => {
Diagnostic::new(Severity::Error, title)
}
ShellError::ArgumentError { error, span } => match error {
ProximateShellError::ArgumentError {
command,
error,
span,
} => match error {
ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new(
Severity::Error,
format!(
"Command requires {}{}",
Color::Cyan.paint("--"),
Color::Cyan.paint(name)
"{} requires {}{}",
Color::Cyan.paint(command),
Color::Black.bold().paint("--"),
Color::Black.bold().paint(name)
),
)
.with_label(Label::new_primary(span)),
ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new(
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)),
ArgumentError::MissingValueForName(name) => Diagnostic::new(
Severity::Error,
format!(
"Missing value for flag {}{}",
Color::Cyan.paint("--"),
Color::Cyan.paint(name)
"{} is missing value for flag {}{}",
Color::Cyan.paint(command),
Color::Black.bold().paint("--"),
Color::Black.bold().paint(name)
),
)
.with_label(Label::new_primary(span)),
},
ShellError::TypeError {
ProximateShellError::TypeError {
expected,
actual:
Spanned {
@ -157,13 +170,13 @@ impl ShellError {
.with_message(format!("Expected {}, found {}", expected, actual)),
),
ShellError::TypeError {
ProximateShellError::TypeError {
expected,
actual: Spanned { item: None, span },
} => Diagnostic::new(Severity::Error, "Type Error")
.with_label(Label::new_primary(span).with_message(expected)),
ShellError::MissingProperty { subpath, expr } => {
ProximateShellError::MissingProperty { subpath, expr } => {
let subpath = subpath.into_label();
let expr = expr.into_label();
@ -181,8 +194,8 @@ impl ShellError {
diag
}
ShellError::Diagnostic(diag) => diag.diagnostic,
ShellError::CoerceError { left, right } => {
ProximateShellError::Diagnostic(diag) => diag.diagnostic,
ProximateShellError::CoerceError { left, right } => {
Diagnostic::new(Severity::Error, "Coercion error")
.with_label(Label::new_primary(left.span).with_message(left.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>,
label: impl Into<String>,
span: Span,
@ -201,7 +214,7 @@ impl ShellError {
)
}
crate fn maybe_labeled_error(
pub fn maybe_labeled_error(
msg: impl Into<String>,
label: impl Into<String>,
span: Option<Span>,
@ -216,7 +229,7 @@ impl 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 {
@ -226,13 +239,40 @@ impl ShellError {
crate fn unexpected(title: impl Into<String>) -> ShellError {
ShellError::string(&format!("Unexpected: {}", title.into()))
}
}
crate fn copy_error(&self) -> ShellError {
self.clone()
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
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 {
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)]
pub struct StringError {
title: String,
@ -305,13 +323,13 @@ pub struct StringError {
impl std::fmt::Display for ShellError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ShellError::String(s) => write!(f, "{}", &s.title),
ShellError::TypeError { .. } => write!(f, "TypeError"),
ShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
ShellError::ArgumentError { .. } => write!(f, "ArgumentError"),
ShellError::Diagnostic(_) => write!(f, "<diagnostic>"),
ShellError::CoerceError { .. } => write!(f, "CoerceError"),
match &self.error {
ProximateShellError::String(s) => write!(f, "{}", &s.title),
ProximateShellError::TypeError { .. } => write!(f, "TypeError"),
ProximateShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
ProximateShellError::ArgumentError { .. } => write!(f, "ArgumentError"),
ProximateShellError::Diagnostic(_) => write!(f, "<diagnostic>"),
ProximateShellError::CoerceError { .. } => write!(f, "CoerceError"),
}
}
}
@ -320,45 +338,40 @@ impl std::error::Error for ShellError {}
impl std::convert::From<std::io::Error> for ShellError {
fn from(input: std::io::Error) -> ShellError {
ShellError::String(StringError {
ProximateShellError::String(StringError {
title: format!("{}", input),
error: Value::nothing(),
})
.start()
}
}
impl std::convert::From<futures_sink::VecSinkError> for ShellError {
fn from(_input: futures_sink::VecSinkError) -> ShellError {
ShellError::String(StringError {
ProximateShellError::String(StringError {
title: format!("Unexpected Vec Sink Error"),
error: Value::nothing(),
})
.start()
}
}
impl std::convert::From<subprocess::PopenError> for ShellError {
fn from(input: subprocess::PopenError) -> ShellError {
ShellError::String(StringError {
ProximateShellError::String(StringError {
title: format!("{}", input),
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 {
fn from(input: toml::ser::Error) -> ShellError {
ShellError::String(StringError {
ProximateShellError::String(StringError {
title: format!("{:?}", input),
error: Value::nothing(),
})
.start()
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,6 @@
use crate::errors::ShellError;
use crate::evaluate::{evaluate_baseline_expr, Scope};
use crate::object::SpannedDictBuilder;
use crate::parser::{hir, Operator, Span, Spanned};
use crate::prelude::*;
use crate::Text;
@ -149,17 +150,12 @@ impl Deserialize<'de> for Block {
D: Deserializer<'de>,
{
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 {
pub fn invoke(&self, value: &Value) -> Result<Spanned<Value>, ShellError> {
let scope = Scope::new(value.copy());
pub fn invoke(&self, value: &Spanned<Value>) -> Result<Spanned<Value>, ShellError> {
let scope = Scope::new(value.clone());
if self.expressions.len() == 0 {
return Ok(Spanned::from_item(Value::nothing(), self.span));
@ -179,23 +175,19 @@ impl Block {
pub enum Value {
Primitive(Primitive),
Object(crate::object::Dictionary),
List(Vec<Value>),
Binary(Vec<u8>),
List(Vec<Spanned<Value>>),
#[allow(unused)]
Block(Block),
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 }
}
pub struct ValuesDebug<'a> {
values: &'a Vec<Value>,
values: &'a Vec<Spanned<Value>>,
}
impl fmt::Debug for ValuesDebug<'a> {
@ -207,17 +199,16 @@ impl fmt::Debug for ValuesDebug<'a> {
}
pub struct ValueDebug<'a> {
value: &'a Value,
value: &'a Spanned<Value>,
}
impl fmt::Debug for ValueDebug<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.value {
match self.value.item() {
Value::Primitive(p) => p.debug(f),
Value::Object(o) => o.debug(f),
Value::List(l) => debug_list(l).fmt(f),
Value::Block(_) => write!(f, "[[block]]"),
Value::Error(err) => write!(f, "[[error :: {} ]]", err),
Value::Filesystem => write!(f, "[[filesystem]]"),
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 {
crate fn type_name(&self) -> String {
match self {
@ -238,16 +294,11 @@ impl Value {
Value::Object(_) => format!("object"),
Value::List(_) => format!("list"),
Value::Block(_) => format!("block"),
Value::Error(_) => format!("error"),
Value::Filesystem => format!("filesystem"),
Value::Binary(_) => format!("binary"),
}
}
crate fn debug(&'a self) -> ValueDebug<'a> {
ValueDebug { value: self }
}
pub fn data_descriptors(&self) -> Vec<String> {
match self {
Value::Primitive(_) => vec![],
@ -259,19 +310,21 @@ impl Value {
.collect(),
Value::Block(_) => vec![],
Value::List(_) => vec![],
Value::Error(_) => vec![],
Value::Filesystem => 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 {
Value::Object(o) => o.get_data_by_key(name),
Value::List(l) => {
for item in l {
match item {
Value::Object(o) => match o.get_data_by_key(name) {
Spanned {
item: Value::Object(o),
..
} => match o.get_data_by_key(name) {
Some(v) => return Some(v),
None => {}
},
@ -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 {
Value::List(l) => l.iter().nth(idx),
_ => None,
@ -298,26 +351,10 @@ impl Value {
Value::Object(o) => o.get_data(desc),
Value::Block(_) => 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()),
}
}
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 {
match self {
Value::Primitive(p) => p.format(desc),
@ -329,7 +366,6 @@ impl Value {
),
Value::Object(_) => format!("[object Object]"),
Value::List(_) => format!("[list List]"),
Value::Error(e) => format!("{}", e),
Value::Filesystem => format!("<filesystem>"),
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 {
Value::List(list) if list.len() == 2 => Ok((list[0].clone(), list[1].clone())),
other => Err(ShellError::string(format!(
@ -459,52 +495,39 @@ impl Value {
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 {
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 {
let mut out = crate::object::Dictionary::default();
crate fn select_fields(obj: &Value, fields: &[String], span: impl Into<Span>) -> Spanned<Value> {
let mut out = SpannedDictBuilder::new(span);
let descs = obj.data_descriptors();
for field in fields {
match descs.iter().find(|d| *d == field) {
None => out.add(field, Value::nothing()),
Some(desc) => out.add(desc.clone(), obj.get_data(desc).borrow().copy()),
None => out.insert(field, Value::nothing()),
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 {
let mut out = crate::object::Dictionary::default();
crate fn reject_fields(obj: &Value, fields: &[String], span: impl Into<Span>) -> Spanned<Value> {
let mut out = SpannedDictBuilder::new(span);
let descs = obj.data_descriptors();
for desc in descs {
match desc {
x if fields.iter().any(|field| *field == x) => continue,
_ => out.add(desc.clone(), obj.get_data(&desc).borrow().copy()),
_ => out.insert(desc.clone(), obj.get_data(&desc).borrow().clone()),
}
}
out
out.into_spanned_value()
}
#[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) {
None => false,
Some(desc) => {
let v = obj.get_data(desc).borrow().copy();
let v = obj.get_data(desc).borrow().clone();
match v {
Value::Primitive(Primitive::Boolean(b)) => match (op, rhs) {

View file

@ -16,24 +16,28 @@ const APP_INFO: AppInfo = AppInfo {
#[derive(Deserialize, Serialize)]
struct Config {
#[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)
.map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?;
let filename = location.join("config.toml");
touch(&filename)?;
let contents = toml::to_string(&Config { extra: map.clone() })?;
let contents = toml::to_string(&Config {
extra: config.iter().map(|(k, v)| (k.clone(), v.clone())).collect(),
})?;
fs::write(&filename, &contents)?;
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)
.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());
let contents = fs::read_to_string(filename)
.map(|v| v.spanned(span))
.map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?;
let parsed: Config = toml::from_str(&contents)

View file

@ -9,7 +9,7 @@ use std::fmt;
#[derive(Debug, Default, Eq, PartialEq, Serialize, Deserialize, Clone, new)]
pub struct Dictionary {
pub entries: IndexMap<String, Value>,
pub entries: IndexMap<String, Spanned<Value>>,
}
impl PartialOrd for Dictionary {
@ -21,15 +21,15 @@ impl PartialOrd for Dictionary {
return this.partial_cmp(&that);
}
let this: Vec<&Value> = self.entries.values().collect();
let that: 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().map(|v| v.item()).collect();
this.partial_cmp(&that)
}
}
impl From<IndexMap<String, Value>> for Dictionary {
fn from(input: IndexMap<String, Value>) -> Dictionary {
impl From<IndexMap<String, Spanned<Value>>> for Dictionary {
fn from(input: IndexMap<String, Spanned<Value>>) -> Dictionary {
let mut out = IndexMap::default();
for (key, value) in input {
@ -49,8 +49,8 @@ impl Ord for Dictionary {
return this.cmp(&that);
}
let this: Vec<&Value> = self.entries.values().collect();
let that: 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().map(|v| v.item()).collect();
this.cmp(&that)
}
@ -72,20 +72,6 @@ impl PartialEq<Value> for 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> {
match self.entries.get(desc) {
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
.entries
.iter()
@ -114,3 +100,73 @@ impl Dictionary {
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::object::{Dictionary, Value};
use crate::object::{SpannedDictBuilder, Value};
use crate::prelude::*;
#[derive(Debug)]
pub enum FileType {
@ -8,10 +9,13 @@ pub enum FileType {
Symlink,
}
crate fn dir_entry_dict(entry: &std::fs::DirEntry) -> Result<Dictionary, ShellError> {
let mut dict = Dictionary::default();
crate fn dir_entry_dict(
entry: &std::fs::DirEntry,
span: impl Into<Span>,
) -> Result<Spanned<Value>, ShellError> {
let mut dict = SpannedDictBuilder::new(span);
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()?;
@ -23,28 +27,28 @@ crate fn dir_entry_dict(entry: &std::fs::DirEntry) -> Result<Dictionary, ShellEr
FileType::Symlink
};
dict.add("file type", Value::string(format!("{:?}", kind)));
dict.add(
dict.insert("file type", Value::string(format!("{:?}", kind)));
dict.insert(
"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() {
Ok(c) => dict.add("created", Value::system_date(c)),
Ok(c) => dict.insert("created", Value::system_date(c)),
Err(_) => {}
}
match metadata.accessed() {
Ok(a) => dict.add("accessed", Value::system_date(a)),
Ok(a) => dict.insert("accessed", Value::system_date(a)),
Err(_) => {}
}
match metadata.modified() {
Ok(m) => dict.add("modified", Value::system_date(m)),
Ok(m) => dict.insert("modified", Value::system_date(m)),
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::dict::Dictionary;
use crate::object::{SpannedDictBuilder, Value};
use crate::prelude::*;
use itertools::join;
use sysinfo::ProcessExt;
crate fn process_dict(proc: &sysinfo::Process) -> Dictionary {
let mut dict = Dictionary::default();
dict.add("name", Value::string(proc.name()));
crate fn process_dict(proc: &sysinfo::Process, span: impl Into<Span>) -> Spanned<Value> {
let mut dict = SpannedDictBuilder::new(span);
dict.insert("name", Value::string(proc.name()));
let cmd = proc.cmd();
@ -15,10 +15,10 @@ crate fn process_dict(proc: &sysinfo::Process) -> Dictionary {
Value::string(join(cmd, ""))
};
dict.add("cmd", cmd_value);
dict.add("cpu", Value::float(proc.cpu_usage() as f64));
dict.add("pid", Value::int(proc.pid() as i64));
dict.add("status", Value::string(proc.status().to_string()));
dict.insert("cmd", cmd_value);
dict.insert("cpu", Value::float(proc.cpu_usage() as f64));
dict.insert("pid", Value::int(proc.pid() as i64));
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 serde_derive::{Deserialize, Serialize};
use serde_derive::Deserialize;
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)]
pub enum Type {
Any,
pub trait Type: std::fmt::Debug + Send {
type Extractor: ExtractType;
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::parser::{nom_input, pipeline};
crate use parse::pipeline::{Pipeline, PipelineElement};
pub use parse::span::{Span, Spanned};
pub use parse::span::{Span, Spanned, SpannedItem};
crate use parse::text::Text;
crate use parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode};
crate use parse::tokens::{RawToken, Token};

View file

@ -23,7 +23,7 @@ impl language_reporting::ReportingFiles for Files {
0
}
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> {
unimplemented!("byte_index")

View file

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

View file

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

View file

@ -1,5 +1,5 @@
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::{
hir::{self, NamedArguments},
@ -78,7 +78,7 @@ fn parse_command_tail(
trace_remaining("nodes", tail.clone(), source);
for (name, kind) in config.named() {
trace!("looking for {} : {:?}", name, kind);
trace!(target: "nu::parse", "looking for {} : {:?}", name, kind);
match kind {
NamedType::Switch => {
@ -86,40 +86,43 @@ fn parse_command_tail(
named.insert_switch(name, flag);
}
NamedType::Mandatory(kind) => match extract_mandatory(name, tail, source, command_span)
{
Err(err) => return Err(err), // produce a correct diagnostic
Ok((pos, flag)) => {
tail.move_to(pos);
NamedType::Mandatory(kind) => {
match extract_mandatory(config, name, tail, source, command_span) {
Err(err) => return Err(err), // produce a correct diagnostic
Ok((pos, flag)) => {
tail.move_to(pos);
if tail.at_end() {
return Err(ShellError::ArgumentError {
error: ArgumentError::MissingValueForName(name.to_string()),
span: flag.span,
});
if tail.at_end() {
return Err(ShellError::argument_error(
config.name.clone(),
ArgumentError::MissingValueForName(name.to_string()),
flag.span,
));
}
let expr = hir::baseline_parse_next_expr(
tail,
registry,
source,
kind.to_coerce_hint(),
)?;
tail.restart();
named.insert_mandatory(name, expr);
}
let expr = hir::baseline_parse_next_expr(
tail,
registry,
source,
kind.to_coerce_hint(),
)?;
tail.restart();
named.insert_mandatory(name, expr);
}
},
}
NamedType::Optional(kind) => match extract_optional(name, tail, source) {
Err(err) => return Err(err), // produce a correct diagnostic
Ok(Some((pos, flag))) => {
tail.move_to(pos);
if tail.at_end() {
return Err(ShellError::ArgumentError {
error: ArgumentError::MissingValueForName(name.to_string()),
span: flag.span,
});
return Err(ShellError::argument_error(
config.name().clone(),
ArgumentError::MissingValueForName(name.to_string()),
flag.span,
));
}
let expr = hir::baseline_parse_next_expr(
@ -144,16 +147,26 @@ fn parse_command_tail(
trace_remaining("after named", tail.clone(), source);
let mut positional = vec![];
let mandatory = config.mandatory_positional();
for arg in mandatory {
trace!("Processing mandatory {:?}", arg);
for arg in config.positional() {
trace!("Processing positional {:?}", arg);
if tail.len() == 0 {
return Err(ShellError::ArgumentError {
error: ArgumentError::MissingMandatoryPositional(arg.name().to_string()),
span: command_span,
});
match arg {
PositionalType::Mandatory(..) => {
if tail.len() == 0 {
return Err(ShellError::argument_error(
config.name().clone(),
ArgumentError::MissingMandatoryPositional(arg.name().to_string()),
command_span,
));
}
}
PositionalType::Optional(..) => {
if tail.len() == 0 {
break;
}
}
}
let result = hir::baseline_parse_next_expr(tail, registry, source, arg.to_coerce_hint())?;
@ -161,21 +174,7 @@ fn parse_command_tail(
positional.push(result);
}
trace_remaining("after mandatory", tail.clone(), source);
let optional = config.optional_positional();
for arg in optional {
if tail.len() == 0 {
break;
}
let result = hir::baseline_parse_next_expr(tail, registry, source, arg.to_coerce_hint())?;
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
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(
config: &CommandConfig,
name: &str,
tokens: &mut hir::TokensIterator<'a>,
source: &Text,
@ -215,10 +215,11 @@ fn extract_mandatory(
let flag = tokens.extract(|t| t.as_flag(name, source));
match flag {
None => Err(ShellError::ArgumentError {
error: ArgumentError::MissingMandatoryFlag(name.to_string()),
None => Err(ShellError::argument_error(
config.name().clone(),
ArgumentError::MissingMandatoryFlag(name.to_string()),
span,
}),
)),
Some((pos, flag)) => {
tokens.remove(pos);

View file

@ -36,22 +36,39 @@ impl NamedValue {
#[allow(unused)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PositionalType {
Value(String),
Block(String),
Mandatory(String, PositionalValue),
Optional(String, PositionalValue),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PositionalValue {
Value,
Block,
}
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> {
match self {
PositionalType::Value(_) => None,
PositionalType::Block(_) => Some(ExpressionKindHint::Block),
PositionalType::Mandatory(_, PositionalValue::Block)
| PositionalType::Optional(_, PositionalValue::Block) => {
Some(ExpressionKindHint::Block)
}
_ => None,
}
}
crate fn name(&self) -> &str {
match self {
PositionalType::Value(s) => s,
PositionalType::Block(s) => s,
PositionalType::Mandatory(s, _) => s,
PositionalType::Optional(s, _) => s,
}
}
}
@ -60,14 +77,11 @@ impl PositionalType {
#[get = "crate"]
pub struct CommandConfig {
pub name: String,
pub mandatory_positional: Vec<PositionalType>,
pub optional_positional: Vec<PositionalType>,
pub positional: Vec<PositionalType>,
pub rest_positional: bool,
pub named: IndexMap<String, NamedType>,
pub is_filter: bool,
pub is_sink: bool,
pub can_load: Vec<String>,
pub can_save: Vec<String>,
}
#[derive(Debug, Default, new, Serialize, Deserialize)]
@ -87,7 +101,7 @@ impl fmt::Debug for DebugPositional<'a> {
None => write!(f, "None"),
Some(positional) => f
.debug_list()
.entries(positional.iter().map(|p| p.item().debug()))
.entries(positional.iter().map(|p| p.debug()))
.finish(),
}
}
@ -104,7 +118,7 @@ impl fmt::Debug for DebugNamed<'a> {
None => write!(f, "None"),
Some(named) => f
.debug_map()
.entries(named.iter().map(|(k, v)| (k, v.item().debug())))
.entries(named.iter().map(|(k, v)| (k, v.debug())))
.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 std::io;
@ -11,11 +11,11 @@ pub trait Plugin {
))
}
#[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"))
}
#[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) {
return;
@ -33,7 +33,11 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) {
send_response(plugin.config());
}
Ok(NuCommand::begin_filter { params }) => {
let _ = plugin.begin_filter(params);
send_response(
plugin
.begin_filter(params)
.map(|_| Vec::<ReturnValue>::new()),
);
}
Ok(NuCommand::filter { params }) => {
send_response(plugin.filter(params));
@ -66,7 +70,11 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) {
send_response(plugin.config());
}
Ok(NuCommand::begin_filter { params }) => {
let _ = plugin.begin_filter(params);
send_response(
plugin
.begin_filter(params)
.map(|_| Vec::<ReturnValue>::new()),
);
}
Ok(NuCommand::filter { params }) => {
send_response(plugin.filter(params));
@ -127,7 +135,7 @@ fn send_response<T: Serialize>(result: T) {
pub enum NuCommand {
config,
begin_filter { params: Args },
filter { params: Value },
sink { params: (Args, Vec<Value>) },
filter { params: Spanned<Value> },
sink { params: (Args, Vec<Spanned<Value>>) },
quit,
}

View file

@ -1,6 +1,6 @@
use crossterm::{cursor, terminal, Attribute, RawScreen};
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::*;
struct BinaryView;
@ -15,10 +15,7 @@ impl Plugin for BinaryView {
fn config(&mut self) -> Result<CommandConfig, ShellError> {
Ok(CommandConfig {
name: "binaryview".to_string(),
mandatory_positional: vec![],
optional_positional: vec![],
can_load: vec![],
can_save: vec![],
positional: vec![],
is_filter: false,
is_sink: true,
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 {
match v {
Value::Binary(b) => {
Spanned {
item: Value::Binary(b),
..
} => {
let _ = view_binary(&b);
}
_ => {}

View file

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

View file

@ -1,6 +1,7 @@
use indexmap::IndexMap;
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 {
@ -16,10 +17,7 @@ impl Plugin for NewSkip {
fn config(&mut self) -> Result<CommandConfig, ShellError> {
Ok(CommandConfig {
name: "skip".to_string(),
mandatory_positional: vec![],
optional_positional: vec![],
can_load: vec![],
can_save: vec![],
positional: vec![],
is_filter: true,
is_sink: false,
named: IndexMap::new(),
@ -36,7 +34,7 @@ impl Plugin for NewSkip {
} => {
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(())
}
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 {
Ok(vec![ReturnValue::Value(input)])
Ok(vec![ReturnSuccess::value(input)])
} else {
self.skip_amount -= 1;
Ok(vec![])

View file

@ -1,6 +1,6 @@
use derive_new::new;
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::output::print_tree_with;
use ptree::print_config::PrintConfig;
@ -31,7 +31,6 @@ impl TreeView {
}
}
Value::Block(_) => {}
Value::Error(_) => {}
Value::Filesystem => {}
Value::Binary(_) => {}
}
@ -85,10 +84,7 @@ impl Plugin for TreeViewer {
fn config(&mut self) -> Result<CommandConfig, ShellError> {
Ok(CommandConfig {
name: "tree".to_string(),
mandatory_positional: vec![],
optional_positional: vec![],
can_load: vec![],
can_save: vec![],
positional: vec![],
is_filter: false,
is_sink: true,
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 {
for i in input.iter() {
let view = TreeView::from_value(&i);
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::commands::command::{Command, CommandAction, CommandArgs, ReturnValue};
crate use crate::commands::command::{
Command, CommandAction, CommandArgs, ReturnSuccess, ReturnValue,
};
crate use crate::context::Context;
crate use crate::env::host::handle_unexpected;
crate use crate::env::{Environment, Host};
crate use crate::errors::ShellError;
crate use crate::object::types::ExtractType;
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 futures::stream::BoxStream;
crate use futures::Stream;
crate use futures::{FutureExt, StreamExt};
crate use std::collections::VecDeque;
crate use std::pin::Pin;
crate use std::future::Future;
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 futures::stream::BoxStream;
pub type InputStream = BoxStream<'static, Value>;
pub type OutputStream = BoxStream<'static, ReturnValue>;
crate fn single_output(item: Value) -> OutputStream {
let value = ReturnValue::Value(item);
let mut vec = VecDeque::new();
vec.push_back(value);
vec.boxed()
pub struct InputStream {
crate values: BoxStream<'static, Spanned<Value>>,
}
impl InputStream {
pub fn into_vec(self) -> impl Future<Output = Vec<Spanned<Value>>> {
self.values.collect()
}
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(),
}
}
}