forked from extern/nushell
Allow handling errors with failure callbacks.
This commit is contained in:
parent
cea8fab307
commit
7614ce4b49
@ -1,8 +1,8 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::data::meta::tag_for_tagged_list;
|
|
||||||
use crate::data::Value;
|
use crate::data::Value;
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::utils::did_you_mean;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
pub struct Get;
|
pub struct Get;
|
||||||
@ -50,56 +50,51 @@ pub fn get_column_path(
|
|||||||
path: &ColumnPath,
|
path: &ColumnPath,
|
||||||
obj: &Tagged<Value>,
|
obj: &Tagged<Value>,
|
||||||
) -> Result<Tagged<Value>, ShellError> {
|
) -> Result<Tagged<Value>, ShellError> {
|
||||||
let mut current = Some(obj);
|
let fields = path.clone();
|
||||||
for p in path.iter() {
|
|
||||||
if let Some(obj) = current {
|
|
||||||
current = match obj.get_data_by_key(&p) {
|
|
||||||
Some(v) => Some(v),
|
|
||||||
None =>
|
|
||||||
// Before we give up, see if they gave us a path that matches a field name by itself
|
|
||||||
{
|
|
||||||
let possibilities = obj.data_descriptors();
|
|
||||||
|
|
||||||
let mut possible_matches: Vec<_> = possibilities
|
let value = obj.get_data_by_column_path(
|
||||||
.iter()
|
obj.tag(),
|
||||||
.map(|x| (natural::distance::levenshtein_distance(x, &p), x))
|
path,
|
||||||
.collect();
|
Box::new(move |(obj_source, column_path_tried)| {
|
||||||
|
match did_you_mean(&obj_source, &column_path_tried) {
|
||||||
possible_matches.sort();
|
Some(suggestions) => {
|
||||||
|
return ShellError::labeled_error(
|
||||||
if possible_matches.len() > 0 {
|
"Unknown column",
|
||||||
return Err(ShellError::labeled_error(
|
format!("did you mean '{}'?", suggestions[0].1),
|
||||||
"Unknown column",
|
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||||
format!("did you mean '{}'?", possible_matches[0].1),
|
)
|
||||||
tag_for_tagged_list(path.iter().map(|p| p.tag())),
|
}
|
||||||
));
|
None => {
|
||||||
} else {
|
return ShellError::labeled_error(
|
||||||
return Err(ShellError::labeled_error(
|
"Unknown column",
|
||||||
"Unknown column",
|
"row does not contain this column",
|
||||||
"row does not contain this column",
|
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||||
tag_for_tagged_list(path.iter().map(|p| p.tag())),
|
)
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
}
|
);
|
||||||
|
|
||||||
match current {
|
let res = match value {
|
||||||
Some(v) => Ok(v.clone()),
|
Ok(fetched) => match fetched {
|
||||||
None => match obj {
|
Some(Tagged { item: v, tag }) => Ok((v.clone()).tagged(&tag)),
|
||||||
// If its None check for certain values.
|
None => match obj {
|
||||||
Tagged {
|
// If its None check for certain values.
|
||||||
item: Value::Primitive(Primitive::String(_)),
|
Tagged {
|
||||||
..
|
item: Value::Primitive(Primitive::String(_)),
|
||||||
} => Ok(obj.clone()),
|
..
|
||||||
Tagged {
|
} => Ok(obj.clone()),
|
||||||
item: Value::Primitive(Primitive::Path(_)),
|
Tagged {
|
||||||
..
|
item: Value::Primitive(Primitive::Path(_)),
|
||||||
} => Ok(obj.clone()),
|
..
|
||||||
_ => Ok(Value::nothing().tagged(&obj.tag)),
|
} => Ok(obj.clone()),
|
||||||
|
_ => Ok(Value::nothing().tagged(&obj.tag)),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
Err(reason) => Err(reason),
|
||||||
|
};
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(
|
pub fn get(
|
||||||
@ -118,26 +113,30 @@ pub fn get(
|
|||||||
|
|
||||||
let member = vec![member.clone()];
|
let member = vec![member.clone()];
|
||||||
|
|
||||||
let fields = vec![&member, &fields]
|
let column_paths = vec![&member, &fields]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect::<Vec<&ColumnPath>>();
|
.collect::<Vec<&ColumnPath>>();
|
||||||
|
|
||||||
for column_path in &fields {
|
for path in column_paths {
|
||||||
match get_column_path(column_path, &item) {
|
let res = get_column_path(&path, &item);
|
||||||
Ok(Tagged {
|
|
||||||
item: Value::Table(l),
|
match res {
|
||||||
..
|
Ok(got) => match got {
|
||||||
}) => {
|
Tagged {
|
||||||
for item in l {
|
item: Value::Table(rows),
|
||||||
result.push_back(ReturnSuccess::value(item.clone()));
|
..
|
||||||
|
} => {
|
||||||
|
for item in rows {
|
||||||
|
result.push_back(ReturnSuccess::value(item.clone()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
other => result
|
||||||
Ok(x) => result.push_back(ReturnSuccess::value(x.clone())),
|
.push_back(ReturnSuccess::value((*other).clone().tagged(&item.tag))),
|
||||||
Err(x) => result.push_back(Err(x)),
|
},
|
||||||
|
Err(reason) => result.push_back(Err(reason)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
|
@ -530,7 +530,8 @@ impl Value {
|
|||||||
&self,
|
&self,
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
path: &Vec<Tagged<String>>,
|
path: &Vec<Tagged<String>>,
|
||||||
) -> Option<Tagged<&Value>> {
|
callback: Box<dyn FnOnce((&Value, &Tagged<String>)) -> ShellError>,
|
||||||
|
) -> Result<Option<Tagged<&Value>>, ShellError> {
|
||||||
let mut current = self;
|
let mut current = self;
|
||||||
for p in path {
|
for p in path {
|
||||||
let value = if p.chars().all(char::is_numeric) {
|
let value = if p.chars().all(char::is_numeric) {
|
||||||
@ -543,11 +544,11 @@ impl Value {
|
|||||||
|
|
||||||
match value {
|
match value {
|
||||||
Some(v) => current = v,
|
Some(v) => current = v,
|
||||||
None => return None,
|
None => return Err(callback((¤t.clone(), &p.clone()))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(current.tagged(tag))
|
Ok(Some(current.tagged(tag)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_data_at_path(
|
pub fn insert_data_at_path(
|
||||||
@ -927,6 +928,7 @@ fn coerce_compare_primitive(
|
|||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::data::meta::*;
|
use crate::data::meta::*;
|
||||||
|
use crate::ShellError;
|
||||||
use crate::Value;
|
use crate::Value;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
@ -942,6 +944,10 @@ mod tests {
|
|||||||
Value::table(list).tagged_unknown()
|
Value::table(list).tagged_unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn error_callback() -> impl FnOnce((&Value, &Tagged<String>)) -> ShellError {
|
||||||
|
move |(_obj_source, _column_path_tried)| ShellError::unimplemented("will never be called.")
|
||||||
|
}
|
||||||
|
|
||||||
fn column_path(paths: &Vec<Tagged<Value>>) -> Tagged<Vec<Tagged<String>>> {
|
fn column_path(paths: &Vec<Tagged<Value>>) -> Tagged<Vec<Tagged<String>>> {
|
||||||
table(
|
table(
|
||||||
&paths
|
&paths
|
||||||
@ -984,7 +990,10 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
**value.get_data_by_column_path(tag, &field_path).unwrap(),
|
**value
|
||||||
|
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
|
||||||
|
.unwrap()
|
||||||
|
.unwrap(),
|
||||||
version
|
version
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1008,7 +1017,10 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
**value.get_data_by_column_path(tag, &field_path).unwrap(),
|
**value
|
||||||
|
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
|
||||||
|
.unwrap()
|
||||||
|
.unwrap(),
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1032,7 +1044,10 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
**value.get_data_by_column_path(tag, &field_path).unwrap(),
|
**value
|
||||||
|
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
|
||||||
|
.unwrap()
|
||||||
|
.unwrap(),
|
||||||
Value::row(indexmap! {
|
Value::row(indexmap! {
|
||||||
"name".into() => string("Andrés N. Robalino")
|
"name".into() => string("Andrés N. Robalino")
|
||||||
})
|
})
|
||||||
|
@ -30,12 +30,12 @@ pub use crate::env::host::BasicHost;
|
|||||||
pub use crate::parser::hir::SyntaxShape;
|
pub use crate::parser::hir::SyntaxShape;
|
||||||
pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder;
|
pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder;
|
||||||
pub use crate::plugin::{serve_plugin, Plugin};
|
pub use crate::plugin::{serve_plugin, Plugin};
|
||||||
pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath};
|
pub use crate::utils::{did_you_mean, AbsoluteFile, AbsolutePath, RelativePath};
|
||||||
pub use cli::cli;
|
pub use cli::cli;
|
||||||
pub use data::base::{Primitive, Value};
|
pub use data::base::{Primitive, Value};
|
||||||
pub use data::config::{config_path, APP_INFO};
|
pub use data::config::{config_path, APP_INFO};
|
||||||
pub use data::dict::{Dictionary, TaggedDictBuilder};
|
pub use data::dict::{Dictionary, TaggedDictBuilder};
|
||||||
pub use data::meta::{Span, Spanned, SpannedItem, Tag, Tagged, TaggedItem};
|
pub use data::meta::{tag_for_tagged_list, Span, Spanned, SpannedItem, Tag, Tagged, TaggedItem};
|
||||||
pub use errors::{CoerceInto, ShellError};
|
pub use errors::{CoerceInto, ShellError};
|
||||||
pub use num_traits::cast::ToPrimitive;
|
pub use num_traits::cast::ToPrimitive;
|
||||||
pub use parser::parse::text::Text;
|
pub use parser::parse::text::Text;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu::{
|
use nu::{
|
||||||
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
|
did_you_mean, serve_plugin, tag_for_tagged_list, CallInfo, Plugin, Primitive, ReturnSuccess,
|
||||||
SyntaxShape, Tagged, TaggedItem, Value,
|
ReturnValue, ShellError, Signature, SyntaxShape, Tagged, TaggedItem, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
@ -93,22 +93,51 @@ impl Inc {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Row(_) => match self.field {
|
Value::Row(_) => match self.field {
|
||||||
Some(ref f) => {
|
Some(ref f) => {
|
||||||
let replacement = match value.item.get_data_by_column_path(value.tag(), f) {
|
let fields = f.clone();
|
||||||
Some(result) => self.inc(result.map(|x| x.clone()))?,
|
|
||||||
None => {
|
let replace_for = value.item.get_data_by_column_path(
|
||||||
return Err(ShellError::labeled_error(
|
value.tag(),
|
||||||
"inc could not find field to replace",
|
&f,
|
||||||
"column name",
|
Box::new(move |(obj_source, column_path_tried)| {
|
||||||
value.tag(),
|
match did_you_mean(&obj_source, &column_path_tried) {
|
||||||
))
|
Some(suggestions) => {
|
||||||
}
|
return ShellError::labeled_error(
|
||||||
|
"Unknown column",
|
||||||
|
format!("did you mean '{}'?", suggestions[0].1),
|
||||||
|
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return ShellError::labeled_error(
|
||||||
|
"Unknown column",
|
||||||
|
"row does not contain this column",
|
||||||
|
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let replacement = match replace_for {
|
||||||
|
Ok(got) => match got {
|
||||||
|
Some(result) => self.inc(result.map(|x| x.clone()))?,
|
||||||
|
None => {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"inc could not find field to replace",
|
||||||
|
"column name",
|
||||||
|
value.tag(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(reason) => return Err(reason),
|
||||||
};
|
};
|
||||||
|
|
||||||
match value.item.replace_data_at_column_path(
|
match value.item.replace_data_at_column_path(
|
||||||
value.tag(),
|
value.tag(),
|
||||||
f,
|
&f,
|
||||||
replacement.item.clone(),
|
replacement.item.clone(),
|
||||||
) {
|
) {
|
||||||
Some(v) => return Ok(v),
|
Some(v) => return Ok(v),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu::{
|
use nu::{
|
||||||
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
|
did_you_mean, serve_plugin, tag_for_tagged_list, CallInfo, Plugin, Primitive, ReturnSuccess,
|
||||||
SyntaxShape, Tagged, TaggedItem, Value,
|
ReturnValue, ShellError, Signature, SyntaxShape, Tagged, TaggedItem, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
@ -92,13 +92,50 @@ impl Str {
|
|||||||
Value::Primitive(Primitive::String(ref s)) => Ok(self.apply(&s)?.tagged(value.tag())),
|
Value::Primitive(Primitive::String(ref s)) => Ok(self.apply(&s)?.tagged(value.tag())),
|
||||||
Value::Row(_) => match self.field {
|
Value::Row(_) => match self.field {
|
||||||
Some(ref f) => {
|
Some(ref f) => {
|
||||||
let replacement = match value.item.get_data_by_column_path(value.tag(), f) {
|
let fields = f.clone();
|
||||||
Some(result) => self.strutils(result.map(|x| x.clone()))?,
|
|
||||||
None => return Ok(Value::nothing().tagged(value.tag)),
|
let replace_for = value.item.get_data_by_column_path(
|
||||||
|
value.tag(),
|
||||||
|
&f,
|
||||||
|
Box::new(move |(obj_source, column_path_tried)| {
|
||||||
|
//let fields = f.clone();
|
||||||
|
|
||||||
|
match did_you_mean(&obj_source, &column_path_tried) {
|
||||||
|
Some(suggestions) => {
|
||||||
|
return ShellError::labeled_error(
|
||||||
|
"Unknown column",
|
||||||
|
format!("did you mean '{}'?", suggestions[0].1),
|
||||||
|
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return ShellError::labeled_error(
|
||||||
|
"Unknown column",
|
||||||
|
"row does not contain this column",
|
||||||
|
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let replacement = match replace_for {
|
||||||
|
Ok(got) => match got {
|
||||||
|
Some(result) => self.strutils(result.map(|x| x.clone()))?,
|
||||||
|
None => {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"inc could not find field to replace",
|
||||||
|
"column name",
|
||||||
|
value.tag(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(reason) => return Err(reason),
|
||||||
};
|
};
|
||||||
|
|
||||||
match value.item.replace_data_at_column_path(
|
match value.item.replace_data_at_column_path(
|
||||||
value.tag(),
|
value.tag(),
|
||||||
f,
|
&f,
|
||||||
replacement.item.clone(),
|
replacement.item.clone(),
|
||||||
) {
|
) {
|
||||||
Some(v) => return Ok(v),
|
Some(v) => return Ok(v),
|
||||||
|
@ -66,7 +66,9 @@ pub(crate) use crate::commands::RawCommandArgs;
|
|||||||
pub(crate) use crate::context::CommandRegistry;
|
pub(crate) use crate::context::CommandRegistry;
|
||||||
pub(crate) use crate::context::{AnchorLocation, Context};
|
pub(crate) use crate::context::{AnchorLocation, Context};
|
||||||
pub(crate) use crate::data::base as value;
|
pub(crate) use crate::data::base as value;
|
||||||
pub(crate) use crate::data::meta::{Span, Spanned, SpannedItem, Tag, Tagged, TaggedItem};
|
pub(crate) use crate::data::meta::{
|
||||||
|
tag_for_tagged_list, Span, Spanned, SpannedItem, Tag, Tagged, TaggedItem,
|
||||||
|
};
|
||||||
pub(crate) use crate::data::types::ExtractType;
|
pub(crate) use crate::data::types::ExtractType;
|
||||||
pub(crate) use crate::data::{Primitive, Value};
|
pub(crate) use crate::data::{Primitive, Value};
|
||||||
pub(crate) use crate::env::host::handle_unexpected;
|
pub(crate) use crate::env::host::handle_unexpected;
|
||||||
|
24
src/utils.rs
24
src/utils.rs
@ -5,6 +5,30 @@ use std::fmt;
|
|||||||
use std::ops::Div;
|
use std::ops::Div;
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
|
||||||
|
pub fn did_you_mean(
|
||||||
|
obj_source: &Value,
|
||||||
|
field_tried: &Tagged<String>,
|
||||||
|
) -> Option<Vec<(usize, String)>> {
|
||||||
|
let possibilities = obj_source.data_descriptors();
|
||||||
|
|
||||||
|
let mut possible_matches: Vec<_> = possibilities
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| {
|
||||||
|
let word = x.clone();
|
||||||
|
let distance = natural::distance::levenshtein_distance(&word, &field_tried);
|
||||||
|
|
||||||
|
(distance, word)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if possible_matches.len() > 0 {
|
||||||
|
possible_matches.sort();
|
||||||
|
return Some(possible_matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AbsoluteFile {
|
pub struct AbsoluteFile {
|
||||||
inner: PathBuf,
|
inner: PathBuf,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user