Add new line primitive, bump version, allow bare filepaths

This commit is contained in:
Jonathan Turner 2019-12-03 19:44:59 +13:00
parent 3fa03eb7a4
commit efc879b955
37 changed files with 170 additions and 129 deletions

2
Cargo.lock generated
View File

@ -1878,7 +1878,7 @@ dependencies = [
[[package]] [[package]]
name = "nu" name = "nu"
version = "0.6.1" version = "0.6.2"
dependencies = [ dependencies = [
"ansi_term 0.12.1", "ansi_term 0.12.1",
"app_dirs", "app_dirs",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "nu" name = "nu"
version = "0.6.1" version = "0.6.2"
authors = ["Yehuda Katz <wycats@gmail.com>", "Jonathan Turner <jonathan.d.turner@gmail.com>", "Andrés N. Robalino <andres@androbtech.com>"] authors = ["Yehuda Katz <wycats@gmail.com>", "Jonathan Turner <jonathan.d.turner@gmail.com>", "Andrés N. Robalino <andres@androbtech.com>"]
description = "A shell for the GitHub era" description = "A shell for the GitHub era"
license = "MIT" license = "MIT"

View File

@ -102,10 +102,17 @@ impl ExpandExpression for FilePathShape {
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ParseError> { ) -> Result<hir::Expression, ParseError> {
let atom = expand_atom(token_nodes, "file path", context, ExpansionRule::new())?; let atom = expand_atom(
token_nodes,
"file path",
context,
ExpansionRule::new().allow_external_word(),
)?;
match atom.unspanned { match atom.unspanned {
UnspannedAtomicToken::Word { text: body } | UnspannedAtomicToken::String { body } => { UnspannedAtomicToken::Word { text: body }
| UnspannedAtomicToken::ExternalWord { text: body }
| UnspannedAtomicToken::String { body } => {
let path = expand_file_path(body.slice(context.source), context); let path = expand_file_path(body.slice(context.source), context);
return Ok(hir::Expression::file_path(path, atom.span)); return Ok(hir::Expression::file_path(path, atom.span));
} }

View File

@ -84,11 +84,17 @@ impl ExpandExpression for PatternShape {
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ParseError> { ) -> Result<hir::Expression, ParseError> {
let atom = expand_atom(token_nodes, "pattern", context, ExpansionRule::new())?; let atom = expand_atom(
token_nodes,
"pattern",
context,
ExpansionRule::new().allow_external_word(),
)?;
match atom.unspanned { match atom.unspanned {
UnspannedAtomicToken::Word { text: body } UnspannedAtomicToken::Word { text: body }
| UnspannedAtomicToken::String { body } | UnspannedAtomicToken::String { body }
| UnspannedAtomicToken::ExternalWord { text: body }
| UnspannedAtomicToken::GlobPattern { pattern: body } => { | UnspannedAtomicToken::GlobPattern { pattern: body } => {
let path = expand_file_path(body.slice(context.source), context); let path = expand_file_path(body.slice(context.source), context);
return Ok(hir::Expression::pattern(path.to_string_lossy(), atom.span)); return Ok(hir::Expression::pattern(path.to_string_lossy(), atom.span));

View File

@ -132,9 +132,10 @@ impl Value {
self.tag.clone() self.tag.clone()
} }
pub fn as_string(&self) -> Result<&str, ShellError> { pub fn as_string(&self) -> Result<String, ShellError> {
match &self.value { match &self.value {
UntaggedValue::Primitive(Primitive::String(string)) => Ok(&string[..]), UntaggedValue::Primitive(Primitive::String(string)) => Ok(string.clone()),
UntaggedValue::Primitive(Primitive::Line(line)) => Ok(line.clone() + "\n"),
_ => Err(ShellError::type_error("string", self.spanned_type_name())), _ => Err(ShellError::type_error("string", self.spanned_type_name())),
} }
} }

View File

@ -31,6 +31,7 @@ impl PrettyType for Primitive {
Primitive::Decimal(_) => ty("decimal"), Primitive::Decimal(_) => ty("decimal"),
Primitive::Bytes(_) => ty("bytesize"), Primitive::Bytes(_) => ty("bytesize"),
Primitive::String(_) => ty("string"), Primitive::String(_) => ty("string"),
Primitive::Line(_) => ty("line"),
Primitive::ColumnPath(_) => ty("column-path"), Primitive::ColumnPath(_) => ty("column-path"),
Primitive::Pattern(_) => ty("pattern"), Primitive::Pattern(_) => ty("pattern"),
Primitive::Boolean(_) => ty("boolean"), Primitive::Boolean(_) => ty("boolean"),
@ -52,6 +53,7 @@ impl PrettyDebug for Primitive {
Primitive::Decimal(decimal) => prim(format_args!("{}", decimal)), Primitive::Decimal(decimal) => prim(format_args!("{}", decimal)),
Primitive::Bytes(bytes) => primitive_doc(bytes, "bytesize"), Primitive::Bytes(bytes) => primitive_doc(bytes, "bytesize"),
Primitive::String(string) => prim(string), Primitive::String(string) => prim(string),
Primitive::Line(string) => prim(string),
Primitive::ColumnPath(path) => path.pretty(), Primitive::ColumnPath(path) => path.pretty(),
Primitive::Pattern(pattern) => primitive_doc(pattern, "pattern"), Primitive::Pattern(pattern) => primitive_doc(pattern, "pattern"),
Primitive::Boolean(boolean) => match boolean { Primitive::Boolean(boolean) => match boolean {

View File

@ -17,6 +17,7 @@ pub enum Primitive {
Decimal(BigDecimal), Decimal(BigDecimal),
Bytes(u64), Bytes(u64),
String(String), String(String),
Line(String),
ColumnPath(ColumnPath), ColumnPath(ColumnPath),
Pattern(String), Pattern(String),
Boolean(bool), Boolean(bool),
@ -51,6 +52,7 @@ impl ShellTypeName for Primitive {
Primitive::Decimal(_) => "decimal", Primitive::Decimal(_) => "decimal",
Primitive::Bytes(_) => "bytes", Primitive::Bytes(_) => "bytes",
Primitive::String(_) => "string", Primitive::String(_) => "string",
Primitive::Line(_) => "line",
Primitive::ColumnPath(_) => "column path", Primitive::ColumnPath(_) => "column path",
Primitive::Pattern(_) => "pattern", Primitive::Pattern(_) => "pattern",
Primitive::Boolean(_) => "boolean", Primitive::Boolean(_) => "boolean",

View File

@ -152,6 +152,25 @@ pub fn autoview(
} => { } => {
outln!("{}", s); outln!("{}", s);
} }
Value {
value: UntaggedValue::Primitive(Primitive::Line(ref s)),
tag: Tag { anchor, span },
} if anchor.is_some() => {
if let Some(text) = text {
let mut stream = VecDeque::new();
stream.push_back(value::string(s).into_value(Tag { anchor, span }));
let result = text.run(raw.with_input(stream.into()), &context.commands);
result.collect::<Vec<_>>().await;
} else {
outln!("{}\n", s);
}
}
Value {
value: UntaggedValue::Primitive(Primitive::Line(s)),
..
} => {
outln!("{}\n", s);
}
Value { Value {
value: UntaggedValue::Primitive(Primitive::Path(s)), value: UntaggedValue::Primitive(Primitive::Path(s)),
.. ..

View File

@ -25,7 +25,7 @@ impl Encoder for LinesCodec {
} }
impl Decoder for LinesCodec { impl Decoder for LinesCodec {
type Item = String; type Item = nu_protocol::UntaggedValue;
type Error = Error; type Error = Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> { fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
@ -33,12 +33,14 @@ impl Decoder for LinesCodec {
Some(pos) if !src.is_empty() => { Some(pos) if !src.is_empty() => {
let buf = src.split_to(pos + 1); let buf = src.split_to(pos + 1);
String::from_utf8(buf.to_vec()) String::from_utf8(buf.to_vec())
.map(value::line)
.map(Some) .map(Some)
.map_err(|e| Error::new(ErrorKind::InvalidData, e)) .map_err(|e| Error::new(ErrorKind::InvalidData, e))
} }
_ if !src.is_empty() => { _ if !src.is_empty() => {
let drained = src.take(); let drained = src.take();
String::from_utf8(drained.to_vec()) String::from_utf8(drained.to_vec())
.map(value::string)
.map(Some) .map(Some)
.map_err(|e| Error::new(ErrorKind::InvalidData, e)) .map_err(|e| Error::new(ErrorKind::InvalidData, e))
} }
@ -192,8 +194,7 @@ pub(crate) async fn run_external_command(
let stdout = popen.stdout.take().unwrap(); let stdout = popen.stdout.take().unwrap();
let file = futures::io::AllowStdIo::new(stdout); let file = futures::io::AllowStdIo::new(stdout);
let stream = Framed::new(file, LinesCodec {}); let stream = Framed::new(file, LinesCodec {});
let stream = let stream = stream.map(move |line| line.unwrap().into_value(&name_tag));
stream.map(move |line| value::string(line.unwrap()).into_value(&name_tag));
Ok(ClassifiedInputStream::from_input_stream( Ok(ClassifiedInputStream::from_input_stream(
stream.boxed() as BoxStream<'static, Value> stream.boxed() as BoxStream<'static, Value>
)) ))

View File

@ -56,19 +56,16 @@ pub fn from_delimited_data(
for value in values { for value in values {
let value_tag = &value.tag; let value_tag = &value.tag;
latest_tag = Some(value_tag.clone()); latest_tag = Some(value_tag.clone());
match &value.value { if let Ok(s) = value.as_string() {
UntaggedValue::Primitive(Primitive::String(s)) => { concat_string.push_str(&s);
concat_string.push_str(&s); } else {
concat_string.push_str("\n"); yield Err(ShellError::labeled_error_with_secondary(
}
_ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name_tag.clone(), name_tag.clone(),
"value originates from here", "value originates from here",
value_tag.clone(), value_tag.clone(),
)), ))
} }
} }

View File

@ -79,19 +79,16 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
for value in values { for value in values {
latest_tag = Some(value.tag.clone()); latest_tag = Some(value.tag.clone());
let value_span = value.tag.span; let value_span = value.tag.span;
match &value.value { if let Ok(s) = value.as_string() {
UntaggedValue::Primitive(Primitive::String(s)) => { concat_string.push_str(&s);
concat_string.push_str(&s); } else {
concat_string.push_str("\n"); yield Err(ShellError::labeled_error_with_secondary(
}
_ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
span, span,
"value originates from here", "value originates from here",
value_span, value_span,
)), ))
} }
} }

View File

@ -83,19 +83,17 @@ fn from_json(
for value in values { for value in values {
latest_tag = Some(value.tag.clone()); latest_tag = Some(value.tag.clone());
let value_span = value.tag.span; let value_span = value.tag.span;
match &value.value {
UntaggedValue::Primitive(Primitive::String(s)) => { if let Ok(s) = value.as_string() {
concat_string.push_str(&s); concat_string.push_str(&s);
concat_string.push_str("\n"); } else {
} yield Err(ShellError::labeled_error_with_secondary(
_ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name_span, name_span,
"value originates from here", "value originates from here",
value_span, value_span,
)), ))
} }
} }

View File

@ -266,17 +266,17 @@ fn from_ssv(
for value in values { for value in values {
let value_tag = value.tag.clone(); let value_tag = value.tag.clone();
latest_tag = Some(value_tag.clone()); latest_tag = Some(value_tag.clone());
match &value.value { if let Ok(s) = value.as_string() {
UntaggedValue::Primitive(Primitive::String(s)) => { concat_string.push_str(&s);
concat_string.push_str(&s); }
} else {
_ => yield Err(ShellError::labeled_error_with_secondary ( yield Err(ShellError::labeled_error_with_secondary (
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
&name, &name,
"value originates from here", "value originates from here",
&value_tag &value_tag
)), ))
} }
} }

View File

@ -82,19 +82,17 @@ pub fn from_toml(
for value in values { for value in values {
latest_tag = Some(value.tag.clone()); latest_tag = Some(value.tag.clone());
let value_span = value.tag.span; let value_span = value.tag.span;
match value.value { if let Ok(s) = value.as_string() {
UntaggedValue::Primitive(Primitive::String(s)) => { concat_string.push_str(&s);
concat_string.push_str(&s); }
concat_string.push_str("\n"); else {
} yield Err(ShellError::labeled_error_with_secondary(
_ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name_span, name_span,
"value originates from here", "value originates from here",
value_span, value_span,
)), ))
} }
} }

View File

@ -3,7 +3,7 @@ use crate::data::value;
use crate::data::TaggedDictBuilder; use crate::data::TaggedDictBuilder;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; use nu_protocol::{ReturnSuccess, Signature, Value};
pub struct FromURL; pub struct FromURL;
@ -44,18 +44,16 @@ fn from_url(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
for value in values { for value in values {
latest_tag = Some(value.tag.clone()); latest_tag = Some(value.tag.clone());
let value_span = value.tag.span; let value_span = value.tag.span;
match value.value { if let Ok(s) = value.as_string() {
UntaggedValue::Primitive(Primitive::String(s)) => { concat_string.push_str(&s);
concat_string.push_str(&s); } else {
} yield Err(ShellError::labeled_error_with_secondary(
_ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name_span, name_span,
"value originates from here", "value originates from here",
value_span, value_span,
)), ))
} }
} }

View File

@ -96,19 +96,17 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
latest_tag = Some(value.tag.clone()); latest_tag = Some(value.tag.clone());
let value_span = value.tag.span; let value_span = value.tag.span;
match value.value { if let Ok(s) = value.as_string() {
UntaggedValue::Primitive(Primitive::String(s)) => { concat_string.push_str(&s);
concat_string.push_str(&s); }
concat_string.push_str("\n"); else {
} yield Err(ShellError::labeled_error_with_secondary(
_ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name_span, name_span,
"value originates from here", "value originates from here",
value_span, value_span,
)), ))
} }
} }

View File

@ -110,19 +110,17 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
latest_tag = Some(value.tag.clone()); latest_tag = Some(value.tag.clone());
let value_span = value.tag.span; let value_span = value.tag.span;
match &value.value { if let Ok(s) = value.as_string() {
UntaggedValue::Primitive(Primitive::String(s)) => { concat_string.push_str(&s);
concat_string.push_str(&s); }
concat_string.push_str("\n"); else {
} yield Err(ShellError::labeled_error_with_secondary(
_ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name_span, name_span,
"value originates from here", "value originates from here",
value_span, value_span,
)), ))
} }
} }

View File

@ -38,8 +38,8 @@ fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
let stream = input let stream = input
.values .values
.map(move |v| match v.value { .map(move |v| {
UntaggedValue::Primitive(Primitive::String(s)) => { if let Ok(s) = v.as_string() {
let split_result: Vec<_> = s.lines().filter(|s| s.trim() != "").collect(); let split_result: Vec<_> = s.lines().filter(|s| s.trim() != "").collect();
trace!("split result = {:?}", split_result); trace!("split result = {:?}", split_result);
@ -47,12 +47,11 @@ fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
let mut result = VecDeque::new(); let mut result = VecDeque::new();
for s in split_result { for s in split_result {
result.push_back(ReturnSuccess::value( result.push_back(ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::String(s.into())).into_untagged_value(), UntaggedValue::Primitive(Primitive::Line(s.into())).into_untagged_value(),
)); ));
} }
result result
} } else {
_ => {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
let value_span = v.tag.span; let value_span = v.tag.span;

View File

@ -146,7 +146,7 @@ fn save(
} }
_ => { _ => {
yield Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"Save requires a filepath (1)", "Save requires a filepath",
"needs path", "needs path",
name_tag.clone(), name_tag.clone(),
)); ));
@ -154,7 +154,7 @@ fn save(
}, },
None => { None => {
yield Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"Save requires a filepath (2)", "Save requires a filepath",
"needs path", "needs path",
name_tag.clone(), name_tag.clone(),
)); ));
@ -162,7 +162,7 @@ fn save(
} }
} else { } else {
yield Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"Save requires a filepath (3)", "Save requires a filepath",
"needs path", "needs path",
name_tag.clone(), name_tag.clone(),
)); ));

View File

@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand;
use crate::data::{value, TaggedDictBuilder}; use crate::data::{value, TaggedDictBuilder};
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; use nu_protocol::{ReturnSuccess, Signature, Value};
pub struct Size; pub struct Size;
@ -35,17 +35,18 @@ fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream,
Ok(input Ok(input
.values .values
.map(move |v| match v.value { .map(move |v| {
UntaggedValue::Primitive(Primitive::String(ref s)) => { if let Ok(s) = v.as_string() {
ReturnSuccess::value(count(s, &v.tag)) ReturnSuccess::value(count(&s, &v.tag))
} else {
Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline",
"requires string input",
name_span,
"value originates from here",
v.tag.span,
))
} }
_ => Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline",
"requires string input",
name_span,
"value originates from here",
v.tag.span,
)),
}) })
.to_output_stream()) .to_output_stream())
} }

View File

@ -57,8 +57,8 @@ fn split_column(
Ok(input Ok(input
.values .values
.map(move |v| match v.value { .map(move |v| {
UntaggedValue::Primitive(Primitive::String(ref s)) => { if let Ok(s) = v.as_string() {
let splitter = separator.replace("\\n", "\n"); let splitter = separator.replace("\\n", "\n");
trace!("splitting with {:?}", splitter); trace!("splitting with {:?}", splitter);
@ -104,14 +104,15 @@ fn split_column(
} }
ReturnSuccess::value(dict.into_value()) ReturnSuccess::value(dict.into_value())
} }
} else {
Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline",
"requires string input",
name_span,
"value originates from here",
v.tag.span,
))
} }
_ => Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline",
"requires string input",
name_span,
"value originates from here",
v.tag.span,
)),
}) })
.to_output_stream()) .to_output_stream())
} }

View File

@ -44,8 +44,8 @@ fn split_row(
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let stream = input let stream = input
.values .values
.map(move |v| match v.value { .map(move |v| {
UntaggedValue::Primitive(Primitive::String(ref s)) => { if let Ok(s) = v.as_string() {
let splitter = separator.item.replace("\\n", "\n"); let splitter = separator.item.replace("\\n", "\n");
trace!("splitting with {:?}", splitter); trace!("splitting with {:?}", splitter);
let split_result: Vec<_> = s.split(&splitter).filter(|s| s.trim() != "").collect(); let split_result: Vec<_> = s.split(&splitter).filter(|s| s.trim() != "").collect();
@ -59,8 +59,7 @@ fn split_row(
)); ));
} }
result result
} } else {
_ => {
let mut result = VecDeque::new(); let mut result = VecDeque::new();
result.push_back(Err(ShellError::labeled_error_with_secondary( result.push_back(Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",

View File

@ -56,6 +56,7 @@ pub fn value_to_bson_value(v: &Value) -> Result<Bson, ShellError> {
} }
UntaggedValue::Primitive(Primitive::Nothing) => Bson::Null, UntaggedValue::Primitive(Primitive::Nothing) => Bson::Null,
UntaggedValue::Primitive(Primitive::String(s)) => Bson::String(s.clone()), UntaggedValue::Primitive(Primitive::String(s)) => Bson::String(s.clone()),
UntaggedValue::Primitive(Primitive::Line(s)) => Bson::String(s.clone()),
UntaggedValue::Primitive(Primitive::ColumnPath(path)) => Bson::Array( UntaggedValue::Primitive(Primitive::ColumnPath(path)) => Bson::Array(
path.iter() path.iter()
.map(|x| match &x.unspanned { .map(|x| match &x.unspanned {

View File

@ -137,6 +137,7 @@ fn to_string_tagged_value(v: &Value) -> Result<String, ShellError> {
UntaggedValue::Primitive(Primitive::Path(_)) => Ok(v.as_string()?.to_string()), UntaggedValue::Primitive(Primitive::Path(_)) => Ok(v.as_string()?.to_string()),
UntaggedValue::Table(_) => return Ok(String::from("[Table]")), UntaggedValue::Table(_) => return Ok(String::from("[Table]")),
UntaggedValue::Row(_) => return Ok(String::from("[Row]")), UntaggedValue::Row(_) => return Ok(String::from("[Row]")),
UntaggedValue::Primitive(Primitive::Line(s)) => return Ok(s.to_string()),
UntaggedValue::Primitive(Primitive::String(s)) => return Ok(s.to_string()), UntaggedValue::Primitive(Primitive::String(s)) => return Ok(s.to_string()),
_ => { _ => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(

View File

@ -54,6 +54,7 @@ pub fn value_to_json_value(v: &Value) -> Result<serde_json::Value, ShellError> {
UntaggedValue::Primitive(Primitive::Nothing) => serde_json::Value::Null, UntaggedValue::Primitive(Primitive::Nothing) => serde_json::Value::Null,
UntaggedValue::Primitive(Primitive::Pattern(s)) => serde_json::Value::String(s.clone()), UntaggedValue::Primitive(Primitive::Pattern(s)) => serde_json::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::String(s)) => serde_json::Value::String(s.clone()), UntaggedValue::Primitive(Primitive::String(s)) => serde_json::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::Line(s)) => serde_json::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::ColumnPath(path)) => serde_json::Value::Array( UntaggedValue::Primitive(Primitive::ColumnPath(path)) => serde_json::Value::Array(
path.iter() path.iter()
.map(|x| match &x.unspanned { .map(|x| match &x.unspanned {

View File

@ -94,6 +94,7 @@ fn nu_value_to_sqlite_string(v: Value) -> String {
Primitive::Bytes(u) => format!("{}", u), Primitive::Bytes(u) => format!("{}", u),
Primitive::Pattern(s) => format!("'{}'", s.replace("'", "''")), Primitive::Pattern(s) => format!("'{}'", s.replace("'", "''")),
Primitive::String(s) => format!("'{}'", s.replace("'", "''")), Primitive::String(s) => format!("'{}'", s.replace("'", "''")),
Primitive::Line(s) => format!("'{}'", s.replace("'", "''")),
Primitive::Boolean(true) => "1".into(), Primitive::Boolean(true) => "1".into(),
Primitive::Boolean(_) => "0".into(), Primitive::Boolean(_) => "0".into(),
Primitive::Date(d) => format!("'{}'", d), Primitive::Date(d) => format!("'{}'", d),

View File

@ -50,6 +50,7 @@ pub fn value_to_toml_value(v: &Value) -> Result<toml::Value, ShellError> {
} }
UntaggedValue::Primitive(Primitive::Pattern(s)) => toml::Value::String(s.clone()), UntaggedValue::Primitive(Primitive::Pattern(s)) => toml::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()), UntaggedValue::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::Line(s)) => toml::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::Path(s)) => { UntaggedValue::Primitive(Primitive::Path(s)) => {
toml::Value::String(s.display().to_string()) toml::Value::String(s.display().to_string())
} }

View File

@ -51,6 +51,7 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
UntaggedValue::Primitive(Primitive::Nothing) => serde_yaml::Value::Null, UntaggedValue::Primitive(Primitive::Nothing) => serde_yaml::Value::Null,
UntaggedValue::Primitive(Primitive::Pattern(s)) => serde_yaml::Value::String(s.clone()), UntaggedValue::Primitive(Primitive::Pattern(s)) => serde_yaml::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::String(s)) => serde_yaml::Value::String(s.clone()), UntaggedValue::Primitive(Primitive::String(s)) => serde_yaml::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::Line(s)) => serde_yaml::Value::String(s.clone()),
UntaggedValue::Primitive(Primitive::ColumnPath(path)) => { UntaggedValue::Primitive(Primitive::ColumnPath(path)) => {
let mut out = vec![]; let mut out = vec![];

View File

@ -186,6 +186,9 @@ fn coerce_compare_primitive(
CompareValues::Decimals(BigDecimal::from(*left), right.clone()) CompareValues::Decimals(BigDecimal::from(*left), right.clone())
} }
(String(left), String(right)) => CompareValues::String(left.clone(), right.clone()), (String(left), String(right)) => CompareValues::String(left.clone(), right.clone()),
(Line(left), String(right)) => CompareValues::String(left.clone(), right.clone()),
(String(left), Line(right)) => CompareValues::String(left.clone(), right.clone()),
(Line(left), Line(right)) => CompareValues::String(left.clone(), right.clone()),
(Date(left), Date(right)) => CompareValues::Date(left.clone(), right.clone()), (Date(left), Date(right)) => CompareValues::Date(left.clone(), right.clone()),
(Date(left), Duration(right)) => CompareValues::DateDuration(left.clone(), right.clone()), (Date(left), Duration(right)) => CompareValues::DateDuration(left.clone(), right.clone()),
_ => return Err((left.type_name(), right.type_name())), _ => return Err((left.type_name(), right.type_name())),

View File

@ -33,6 +33,7 @@ pub enum TypeShape {
Decimal, Decimal,
Bytesize, Bytesize,
String, String,
Line,
ColumnPath, ColumnPath,
Pattern, Pattern,
Boolean, Boolean,
@ -62,6 +63,7 @@ impl TypeShape {
Primitive::Decimal(_) => TypeShape::Decimal, Primitive::Decimal(_) => TypeShape::Decimal,
Primitive::Bytes(_) => TypeShape::Bytesize, Primitive::Bytes(_) => TypeShape::Bytesize,
Primitive::String(_) => TypeShape::String, Primitive::String(_) => TypeShape::String,
Primitive::Line(_) => TypeShape::Line,
Primitive::ColumnPath(_) => TypeShape::ColumnPath, Primitive::ColumnPath(_) => TypeShape::ColumnPath,
Primitive::Pattern(_) => TypeShape::Pattern, Primitive::Pattern(_) => TypeShape::Pattern,
Primitive::Boolean(_) => TypeShape::Boolean, Primitive::Boolean(_) => TypeShape::Boolean,
@ -114,6 +116,7 @@ impl PrettyDebug for TypeShape {
TypeShape::Decimal => ty("decimal"), TypeShape::Decimal => ty("decimal"),
TypeShape::Bytesize => ty("bytesize"), TypeShape::Bytesize => ty("bytesize"),
TypeShape::String => ty("string"), TypeShape::String => ty("string"),
TypeShape::Line => ty("line"),
TypeShape::ColumnPath => ty("column-path"), TypeShape::ColumnPath => ty("column-path"),
TypeShape::Pattern => ty("pattern"), TypeShape::Pattern => ty("pattern"),
TypeShape::Boolean => ty("boolean"), TypeShape::Boolean => ty("boolean"),
@ -208,6 +211,7 @@ pub enum InlineShape {
Decimal(BigDecimal), Decimal(BigDecimal),
Bytesize(u64), Bytesize(u64),
String(String), String(String),
Line(String),
ColumnPath(ColumnPath), ColumnPath(ColumnPath),
Pattern(String), Pattern(String),
Boolean(bool), Boolean(bool),
@ -242,6 +246,7 @@ impl InlineShape {
Primitive::Decimal(decimal) => InlineShape::Decimal(decimal.clone()), Primitive::Decimal(decimal) => InlineShape::Decimal(decimal.clone()),
Primitive::Bytes(bytesize) => InlineShape::Bytesize(*bytesize), Primitive::Bytes(bytesize) => InlineShape::Bytesize(*bytesize),
Primitive::String(string) => InlineShape::String(string.clone()), Primitive::String(string) => InlineShape::String(string.clone()),
Primitive::Line(string) => InlineShape::Line(string.clone()),
Primitive::ColumnPath(path) => InlineShape::ColumnPath(path.clone()), Primitive::ColumnPath(path) => InlineShape::ColumnPath(path.clone()),
Primitive::Pattern(pattern) => InlineShape::Pattern(pattern.clone()), Primitive::Pattern(pattern) => InlineShape::Pattern(pattern.clone()),
Primitive::Boolean(boolean) => InlineShape::Boolean(*boolean), Primitive::Boolean(boolean) => InlineShape::Boolean(*boolean),
@ -327,6 +332,7 @@ impl PrettyDebug for FormatInlineShape {
} }
} }
InlineShape::String(string) => b::primitive(format!("{}", string)), InlineShape::String(string) => b::primitive(format!("{}", string)),
InlineShape::Line(string) => b::primitive(format!("{}", string)),
InlineShape::ColumnPath(path) => { InlineShape::ColumnPath(path) => {
b::intersperse(path.iter().map(|member| member.pretty()), b::keyword(".")) b::intersperse(path.iter().map(|member| member.pretty()), b::keyword("."))
} }

View File

@ -37,6 +37,7 @@ pub fn format_primitive(primitive: &Primitive, field_name: Option<&String>) -> S
Primitive::Decimal(decimal) => format!("{}", decimal), Primitive::Decimal(decimal) => format!("{}", decimal),
Primitive::Pattern(s) => format!("{}", s), Primitive::Pattern(s) => format!("{}", s),
Primitive::String(s) => format!("{}", s), Primitive::String(s) => format!("{}", s),
Primitive::Line(s) => format!("{}", s),
Primitive::ColumnPath(p) => { Primitive::ColumnPath(p) => {
let mut members = p.iter(); let mut members = p.iter();
let mut f = String::new(); let mut f = String::new();

View File

@ -86,6 +86,10 @@ impl ExtractType for String {
value: UntaggedValue::Primitive(Primitive::String(string)), value: UntaggedValue::Primitive(Primitive::String(string)),
.. ..
} => Ok(string.clone()), } => Ok(string.clone()),
Value {
value: UntaggedValue::Primitive(Primitive::Line(string)),
..
} => Ok(string.clone()),
other => Err(ShellError::type_error("String", other.spanned_type_name())), other => Err(ShellError::type_error("String", other.spanned_type_name())),
} }
} }

View File

@ -27,6 +27,10 @@ pub fn string(s: impl Into<String>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::String(s.into())) UntaggedValue::Primitive(Primitive::String(s.into()))
} }
pub fn line(s: impl Into<String>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::Line(s.into()))
}
pub fn column_path(s: Vec<impl Into<PathMember>>) -> UntaggedValue { pub fn column_path(s: Vec<impl Into<PathMember>>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::ColumnPath(ColumnPath::new( UntaggedValue::Primitive(Primitive::ColumnPath(ColumnPath::new(
s.into_iter().map(|p| p.into()).collect(), s.into_iter().map(|p| p.into()).collect(),

View File

@ -72,16 +72,14 @@ impl Plugin for Match {
tag, tag,
} => { } => {
if let Some(val) = dict.entries.get(&self.column) { if let Some(val) = dict.entries.get(&self.column) {
match val { if let Ok(s) = val.as_string() {
Value { flag = self.regex.is_match(&s);
value: UntaggedValue::Primitive(Primitive::String(s)), } else {
.. return Err(ShellError::labeled_error(
} => { "expected string",
flag = self.regex.is_match(s); "value",
} val.tag(),
Value { tag, .. } => { ));
return Err(ShellError::labeled_error("expected string", "value", tag));
}
} }
} else { } else {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(

View File

@ -129,24 +129,16 @@ impl Plugin for Parse {
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, ShellError> { fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, ShellError> {
let mut results = vec![]; let mut results = vec![];
match &input { if let Ok(s) = input.as_string() {
Value { for cap in self.regex.captures_iter(&s) {
tag, let mut dict = TaggedDictBuilder::new(input.tag());
value: UntaggedValue::Primitive(Primitive::String(s)),
} => {
//self.full_input.push_str(&s);
for cap in self.regex.captures_iter(&s) { for (idx, column_name) in self.column_names.iter().enumerate() {
let mut dict = TaggedDictBuilder::new(tag); dict.insert_untagged(column_name, value::string(&cap[idx + 1].to_string()));
for (idx, column_name) in self.column_names.iter().enumerate() {
dict.insert_untagged(column_name, value::string(&cap[idx + 1].to_string()));
}
results.push(ReturnSuccess::value(dict.into_value()));
} }
results.push(ReturnSuccess::value(dict.into_value()));
} }
_ => {}
} }
Ok(results) Ok(results)
} }

View File

@ -155,6 +155,9 @@ impl Str {
UntaggedValue::Primitive(Primitive::String(ref s)) => { UntaggedValue::Primitive(Primitive::String(ref s)) => {
Ok(self.apply(&s)?.into_value(value.tag())) Ok(self.apply(&s)?.into_value(value.tag()))
} }
UntaggedValue::Primitive(Primitive::Line(ref s)) => {
Ok(self.apply(&s)?.into_value(value.tag()))
}
UntaggedValue::Row(_) => match self.field { UntaggedValue::Row(_) => match self.field {
Some(ref f) => { Some(ref f) => {
let fields = f.clone(); let fields = f.clone();

View File

@ -30,6 +30,7 @@ fn converts_structured_table_to_csv_text() {
r#" r#"
open csv_text_sample.txt open csv_text_sample.txt
| lines | lines
| trim
| split-column "," a b c d origin | split-column "," a b c d origin
| last 1 | last 1
| to-csv | to-csv
@ -60,6 +61,7 @@ fn converts_structured_table_to_csv_text_skipping_headers_after_conversion() {
r#" r#"
open csv_text_sample.txt open csv_text_sample.txt
| lines | lines
| trim
| split-column "," a b c d origin | split-column "," a b c d origin
| last 1 | last 1
| to-csv --headerless | to-csv --headerless