mirror of
https://github.com/nushell/nushell.git
synced 2025-06-30 22:50:14 +02:00
Create Record
type (#10103)
# Description This PR creates a new `Record` type to reduce duplicate code and possibly bugs as well. (This is an edited version of #9648.) - `Record` implements `FromIterator` and `IntoIterator` and so can be iterated over or collected into. For example, this helps with conversions to and from (hash)maps. (Also, no more `cols.iter().zip(vals)`!) - `Record` has a `push(col, val)` function to help insure that the number of columns is equal to the number of values. I caught a few potential bugs thanks to this (e.g. in the `ls` command). - Finally, this PR also adds a `record!` macro that helps simplify record creation. It is used like so: ```rust record! { "key1" => some_value, "key2" => Value::string("text", span), "key3" => Value::int(optional_int.unwrap_or(0), span), "key4" => Value::bool(config.setting, span), } ``` Since macros hinder formatting, etc., the right hand side values should be relatively short and sweet like the examples above. Where possible, prefer `record!` or `.collect()` on an iterator instead of multiple `Record::push`s, since the first two automatically set the record capacity and do less work overall. # User-Facing Changes Besides the changes in `nu-protocol` the only other breaking changes are to `nu-table::{ExpandedTable::build_map, JustTable::kv_table}`.
This commit is contained in:
@ -2,6 +2,7 @@ use indexmap::indexmap;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::record;
|
||||
use nu_protocol::{
|
||||
ast::Call, engine::Command, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData,
|
||||
PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
@ -232,9 +233,6 @@ impl Command for Char {
|
||||
return Ok(CHAR_MAP
|
||||
.iter()
|
||||
.map(move |(name, s)| {
|
||||
let cols = vec!["name".into(), "character".into(), "unicode".into()];
|
||||
let name: Value = Value::string(String::from(*name), call_span);
|
||||
let character = Value::string(s, call_span);
|
||||
let unicode = Value::string(
|
||||
s.chars()
|
||||
.map(|c| format!("{:x}", c as u32))
|
||||
@ -242,12 +240,13 @@ impl Command for Char {
|
||||
.join(" "),
|
||||
call_span,
|
||||
);
|
||||
let vals = vec![name, character, unicode];
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call_span,
|
||||
}
|
||||
let record = record! {
|
||||
"name" => Value::string(*name, call_span),
|
||||
"character" => Value::string(s, call_span),
|
||||
"unicode" => unicode,
|
||||
};
|
||||
|
||||
Value::record(record, call_span)
|
||||
})
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()));
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Range, ShellError, Signature,
|
||||
Span, Spanned, SyntaxShape, Type, Value,
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, Range, Record, ShellError,
|
||||
Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
type Input<'t> = Peekable<CharIndices<'t>>;
|
||||
@ -64,7 +64,7 @@ impl Command for DetectColumns {
|
||||
description: "Splits string across multiple columns",
|
||||
example: "'a b c' | detect columns -n",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column0".to_string(),
|
||||
"column1".to_string(),
|
||||
@ -75,8 +75,7 @@ impl Command for DetectColumns {
|
||||
Value::test_string("b"),
|
||||
Value::test_string("c"),
|
||||
],
|
||||
span,
|
||||
}],
|
||||
})],
|
||||
span,
|
||||
}),
|
||||
},
|
||||
@ -211,11 +210,7 @@ fn detect_columns(
|
||||
};
|
||||
|
||||
if !(l_idx <= r_idx && (r_idx >= 0 || l_idx < (cols.len() as isize))) {
|
||||
return Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: name_span,
|
||||
};
|
||||
return Value::record(Record { cols, vals }, name_span);
|
||||
}
|
||||
|
||||
(l_idx.max(0) as usize, (r_idx as usize + 1).min(cols.len()))
|
||||
@ -228,11 +223,7 @@ fn detect_columns(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: name_span,
|
||||
};
|
||||
return Value::record(Record { cols, vals }, name_span);
|
||||
};
|
||||
|
||||
// Merge Columns
|
||||
@ -254,11 +245,7 @@ fn detect_columns(
|
||||
vals.push(binding);
|
||||
last_seg.into_iter().for_each(|v| vals.push(v));
|
||||
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: name_span,
|
||||
}
|
||||
Value::record(Record { cols, vals }, name_span)
|
||||
})
|
||||
.into_pipeline_data(ctrlc))
|
||||
} else {
|
||||
|
@ -6,8 +6,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, ListStream, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||
Type, Value,
|
||||
Category, Example, ListStream, PipelineData, Record, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -44,11 +44,10 @@ impl Command for Parse {
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let result = Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["foo".to_string(), "bar".to_string()],
|
||||
vals: vec![Value::test_string("hi"), Value::test_string("there")],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
};
|
||||
|
||||
@ -67,11 +66,10 @@ impl Command for Parse {
|
||||
description: "Parse a string using fancy-regex named capture group pattern",
|
||||
example: "\"foo bar.\" | parse -r '\\s*(?<name>\\w+)(?=\\.)'",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["name".to_string()],
|
||||
vals: vec![Value::test_string("bar")],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -80,16 +78,14 @@ impl Command for Parse {
|
||||
example: "\"foo! bar.\" | parse -r '(\\w+)(?=\\.)|(\\w+)(?=!)'",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["capture0".to_string(), "capture1".to_string()],
|
||||
vals: vec![Value::test_string(""), Value::test_string("foo")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["capture0".to_string(), "capture1".to_string()],
|
||||
vals: vec![Value::test_string("bar"), Value::test_string("")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -99,14 +95,13 @@ impl Command for Parse {
|
||||
example:
|
||||
"\" @another(foo bar) \" | parse -r '\\s*(?<=[() ])(@\\w+)(\\([^)]*\\))?\\s*'",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["capture0".to_string(), "capture1".to_string()],
|
||||
vals: vec![
|
||||
Value::test_string("@another"),
|
||||
Value::test_string("(foo bar)"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -114,11 +109,10 @@ impl Command for Parse {
|
||||
description: "Parse a string using fancy-regex look ahead atomic group pattern",
|
||||
example: "\"abcd\" | parse -r '^a(bc(?=d)|b)cd$'",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["capture0".to_string()],
|
||||
vals: vec![Value::test_string("b")],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -179,7 +173,6 @@ fn operate(
|
||||
let results = regex_pattern.captures_iter(&s);
|
||||
|
||||
for c in results {
|
||||
let mut cols = Vec::with_capacity(columns.len());
|
||||
let captures = match c {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
@ -192,22 +185,18 @@ fn operate(
|
||||
))
|
||||
}
|
||||
};
|
||||
let mut vals = Vec::with_capacity(captures.len());
|
||||
|
||||
for (column_name, cap) in columns.iter().zip(captures.iter().skip(1)) {
|
||||
let cap_string = cap.map(|v| v.as_str()).unwrap_or("").to_string();
|
||||
cols.push(column_name.clone());
|
||||
vals.push(Value::String {
|
||||
val: cap_string,
|
||||
span: v.span()?,
|
||||
});
|
||||
}
|
||||
let v_span = v.span()?;
|
||||
let record = columns
|
||||
.iter()
|
||||
.zip(captures.iter().skip(1))
|
||||
.map(|(column_name, cap)| {
|
||||
let cap_string = cap.map(|v| v.as_str()).unwrap_or("");
|
||||
(column_name.clone(), Value::string(cap_string, v_span))
|
||||
})
|
||||
.collect();
|
||||
|
||||
parsed.push(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
});
|
||||
parsed.push(Value::record(record, head));
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
@ -449,7 +438,6 @@ fn stream_helper(
|
||||
let results = regex.captures_iter(&s);
|
||||
|
||||
for c in results {
|
||||
let mut cols = Vec::with_capacity(columns.len());
|
||||
let captures = match c {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
@ -464,18 +452,17 @@ fn stream_helper(
|
||||
})
|
||||
}
|
||||
};
|
||||
let mut vals = Vec::with_capacity(captures.len());
|
||||
|
||||
for (column_name, cap) in columns.iter().zip(captures.iter().skip(1)) {
|
||||
let cap_string = cap.map(|v| v.as_str()).unwrap_or("").to_string();
|
||||
cols.push(column_name.clone());
|
||||
vals.push(Value::String {
|
||||
val: cap_string,
|
||||
span,
|
||||
});
|
||||
}
|
||||
let record = columns
|
||||
.iter()
|
||||
.zip(captures.iter().skip(1))
|
||||
.map(|(column_name, cap)| {
|
||||
let cap_string = cap.map(|v| v.as_str()).unwrap_or("");
|
||||
(column_name.clone(), Value::string(cap_string, span))
|
||||
})
|
||||
.collect();
|
||||
|
||||
excess.push(Value::Record { cols, vals, span });
|
||||
excess.push(Value::record(record, span));
|
||||
}
|
||||
|
||||
if !excess.is_empty() {
|
||||
|
@ -1,7 +1,9 @@
|
||||
use fancy_regex::Regex;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value};
|
||||
use nu_protocol::{
|
||||
record, Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
use std::{fmt, str};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
@ -46,7 +48,7 @@ impl Command for Size {
|
||||
Example {
|
||||
description: "Count the number of words in a string",
|
||||
example: r#""There are seven words in this sentence" | size"#,
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"lines".into(),
|
||||
"words".into(),
|
||||
@ -61,13 +63,12 @@ impl Command for Size {
|
||||
Value::test_int(38),
|
||||
Value::test_int(38),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Counts unicode characters",
|
||||
example: r#"'今天天气真好' | size "#,
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"lines".into(),
|
||||
"words".into(),
|
||||
@ -82,13 +83,12 @@ impl Command for Size {
|
||||
Value::test_int(6),
|
||||
Value::test_int(6),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Counts Unicode characters correctly in a string",
|
||||
example: r#""Amélie Amelie" | size"#,
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"lines".into(),
|
||||
"words".into(),
|
||||
@ -103,8 +103,7 @@ impl Command for Size {
|
||||
Value::test_int(14),
|
||||
Value::test_int(13),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -145,55 +144,20 @@ fn size(
|
||||
|
||||
fn counter(contents: &str, span: Span) -> Value {
|
||||
let counts = uwc_count(&ALL_COUNTERS[..], contents);
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
cols.push("lines".into());
|
||||
vals.push(Value::Int {
|
||||
val: match counts.get(&Counter::Lines) {
|
||||
Some(c) => *c as i64,
|
||||
None => 0,
|
||||
},
|
||||
span,
|
||||
});
|
||||
fn get_count(counts: &BTreeMap<Counter, usize>, counter: Counter, span: Span) -> Value {
|
||||
Value::int(counts.get(&counter).copied().unwrap_or(0) as i64, span)
|
||||
}
|
||||
|
||||
cols.push("words".into());
|
||||
vals.push(Value::Int {
|
||||
val: match counts.get(&Counter::Words) {
|
||||
Some(c) => *c as i64,
|
||||
None => 0,
|
||||
},
|
||||
span,
|
||||
});
|
||||
let record = record! {
|
||||
"lines" => get_count(&counts, Counter::Lines, span),
|
||||
"words" => get_count(&counts, Counter::Words, span),
|
||||
"bytes" => get_count(&counts, Counter::Bytes, span),
|
||||
"chars" => get_count(&counts, Counter::CodePoints, span),
|
||||
"graphemes" => get_count(&counts, Counter::GraphemeClusters, span),
|
||||
};
|
||||
|
||||
cols.push("bytes".into());
|
||||
vals.push(Value::Int {
|
||||
val: match counts.get(&Counter::Bytes) {
|
||||
Some(c) => *c as i64,
|
||||
None => 0,
|
||||
},
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("chars".into());
|
||||
vals.push(Value::Int {
|
||||
val: match counts.get(&Counter::CodePoints) {
|
||||
Some(c) => *c as i64,
|
||||
None => 0,
|
||||
},
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("graphemes".into());
|
||||
vals.push(Value::Int {
|
||||
val: match counts.get(&Counter::GraphemeClusters) {
|
||||
Some(c) => *c as i64,
|
||||
None => 0,
|
||||
},
|
||||
span,
|
||||
});
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
Value::record(record, span)
|
||||
}
|
||||
|
||||
/// Take all the counts in `other_counts` and sum them into `accum`.
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||
Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
use regex::Regex;
|
||||
|
||||
@ -64,7 +64,7 @@ impl Command for SubCommand {
|
||||
description: "Split a string into columns by the specified separator",
|
||||
example: "'a--b--c' | split column '--'",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column1".to_string(),
|
||||
"column2".to_string(),
|
||||
@ -75,8 +75,7 @@ impl Command for SubCommand {
|
||||
Value::test_string("b"),
|
||||
Value::test_string("c"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -84,7 +83,7 @@ impl Command for SubCommand {
|
||||
description: "Split a string into columns of char and remove the empty columns",
|
||||
example: "'abc' | split column -c ''",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column1".to_string(),
|
||||
"column2".to_string(),
|
||||
@ -95,8 +94,7 @@ impl Command for SubCommand {
|
||||
Value::test_string("b"),
|
||||
Value::test_string("c"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -105,16 +103,14 @@ impl Command for SubCommand {
|
||||
example: "['a-b' 'c-d'] | split column -",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column1".to_string(), "column2".to_string()],
|
||||
vals: vec![Value::test_string("a"), Value::test_string("b")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column1".to_string(), "column2".to_string()],
|
||||
vals: vec![Value::test_string("c"), Value::test_string("d")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -124,16 +120,14 @@ impl Command for SubCommand {
|
||||
example: r"['a - b' 'c - d'] | split column -r '\s*-\s*'",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column1".to_string(), "column2".to_string()],
|
||||
vals: vec![Value::test_string("a"), Value::test_string("b")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column1".to_string(), "column2".to_string()],
|
||||
vals: vec![Value::test_string("c"), Value::test_string("d")],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -190,8 +184,7 @@ fn split_column_helper(
|
||||
let positional: Vec<_> = rest.iter().map(|f| f.item.clone()).collect();
|
||||
|
||||
// If they didn't provide column names, make up our own
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
let mut record = Record::new();
|
||||
if positional.is_empty() {
|
||||
let mut gen_columns = vec![];
|
||||
for i in 0..split_result.len() {
|
||||
@ -199,20 +192,14 @@ fn split_column_helper(
|
||||
}
|
||||
|
||||
for (&k, v) in split_result.iter().zip(&gen_columns) {
|
||||
cols.push(v.to_string());
|
||||
vals.push(Value::string(k, head));
|
||||
record.push(v, Value::string(k, head));
|
||||
}
|
||||
} else {
|
||||
for (&k, v) in split_result.iter().zip(&positional) {
|
||||
cols.push(v.into());
|
||||
vals.push(Value::string(k, head));
|
||||
record.push(v, Value::string(k, head));
|
||||
}
|
||||
}
|
||||
vec![Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: head,
|
||||
}]
|
||||
vec![Value::record(record, head)]
|
||||
} else {
|
||||
match v.span() {
|
||||
Ok(span) => vec![Value::Error {
|
||||
|
@ -3,7 +3,9 @@ use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::CellPath;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::Category;
|
||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
||||
use nu_protocol::{
|
||||
Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -67,11 +69,10 @@ impl Command for SubCommand {
|
||||
description: "Capitalize a column in a table",
|
||||
example: "[[lang, gems]; [nu_test, 100]] | str capitalize lang",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
span: Span::test_data(),
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("Nu_test"), Value::test_int(100)],
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -3,7 +3,9 @@ use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::CellPath;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::Category;
|
||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
||||
use nu_protocol::{
|
||||
Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -67,11 +69,10 @@ impl Command for SubCommand {
|
||||
description: "Downcase contents",
|
||||
example: "[[ColA ColB]; [Test ABC]] | str downcase ColA",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string()],
|
||||
vals: vec![Value::test_string("test"), Value::test_string("ABC")],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -79,11 +80,10 @@ impl Command for SubCommand {
|
||||
description: "Downcase contents",
|
||||
example: "[[ColA ColB]; [Test ABC]] | str downcase ColA ColB",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string()],
|
||||
vals: vec![Value::test_string("test"), Value::test_string("abc")],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -3,8 +3,9 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::CellPath;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::Category;
|
||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -88,21 +89,19 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Check if input contains string in a record",
|
||||
example: "{ ColA: test, ColB: 100 } | str contains 'e' ColA",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string()],
|
||||
vals: vec![Value::test_bool(true), Value::test_int(100)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Check if input contains string in a table",
|
||||
example: " [[ColA ColB]; [test 100]] | str contains -i 'E' ColA",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string()],
|
||||
vals: vec![Value::test_bool(true), Value::test_int(100)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -110,11 +109,10 @@ impl Command for SubCommand {
|
||||
description: "Check if input contains string in a table",
|
||||
example: " [[ColA ColB]; [test hello]] | str contains 'e' ColA ColB",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string()],
|
||||
vals: vec![Value::test_bool(true), Value::test_bool(true)],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
levenshtein_distance, Category, Example, PipelineData, ShellError, Signature, Span,
|
||||
levenshtein_distance, Category, Example, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
@ -82,11 +82,10 @@ impl Command for SubCommand {
|
||||
example: "[{a: 'nutshell' b: 'numetal'}] | str distance 'nushell' 'a' 'b'",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(4)],
|
||||
span: Span::test_data(),
|
||||
}
|
||||
})
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
@ -95,11 +94,10 @@ impl Command for SubCommand {
|
||||
description: "Compute edit distance between strings in record and another string, using cell paths",
|
||||
example: "{a: 'nutshell' b: 'numetal'} | str distance 'nushell' a b",
|
||||
result: Some(
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_int(4)],
|
||||
span: Span::test_data(),
|
||||
}
|
||||
})
|
||||
),
|
||||
}]
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
report_error_new, Category, Example, PipelineData, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
report_error_new, Category, Example, PipelineData, Record, ShellError, Signature, Span,
|
||||
Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
@ -150,15 +150,14 @@ impl Command for SubCommand {
|
||||
example:
|
||||
"[[ColA ColB ColC]; [abc abc ads]] | str replace -ar 'b' 'z' ColA ColC",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::test_string("azc"),
|
||||
Value::test_string("abc"),
|
||||
Value::test_string("ads"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
@ -166,15 +165,14 @@ impl Command for SubCommand {
|
||||
description: "Find and replace all occurrences of find string in record using regular expression",
|
||||
example:
|
||||
"{ KeyA: abc, KeyB: abc, KeyC: ads } | str replace -ar 'b' 'z' KeyA KeyC",
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["KeyA".to_string(), "KeyB".to_string(), "KeyC".to_string()],
|
||||
vals: vec![
|
||||
Value::test_string("azc"),
|
||||
Value::test_string("abc"),
|
||||
Value::test_string("ads"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Find and replace contents without using the replace parameter as a regular expression",
|
||||
|
@ -1,10 +1,10 @@
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::Category;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -180,14 +180,16 @@ fn action(input: &Value, arg: &Arguments, head: Span) -> Value {
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => match mode {
|
||||
ActionMode::Global => match other {
|
||||
Value::Record { cols, vals, span } => {
|
||||
let new_vals = vals.iter().map(|v| action(v, arg, head)).collect();
|
||||
Value::Record { val: record, span } => {
|
||||
let new_vals = record.vals.iter().map(|v| action(v, arg, head)).collect();
|
||||
|
||||
Value::Record {
|
||||
cols: cols.to_vec(),
|
||||
vals: new_vals,
|
||||
span: *span,
|
||||
}
|
||||
Value::record(
|
||||
Record {
|
||||
cols: record.cols.to_vec(),
|
||||
vals: new_vals,
|
||||
},
|
||||
*span,
|
||||
)
|
||||
}
|
||||
Value::List { vals, span } => {
|
||||
let new_vals = vals.iter().map(|v| action(v, arg, head)).collect();
|
||||
@ -249,14 +251,12 @@ mod tests {
|
||||
}
|
||||
|
||||
fn make_record(cols: Vec<&str>, vals: Vec<&str>) -> Value {
|
||||
Value::Record {
|
||||
cols: cols.iter().map(|x| x.to_string()).collect(),
|
||||
vals: vals
|
||||
.iter()
|
||||
.map(|x| Value::test_string(x.to_string()))
|
||||
Value::test_record(
|
||||
cols.into_iter()
|
||||
.zip(vals)
|
||||
.map(|(col, val)| (col.to_owned(), Value::test_string(val)))
|
||||
.collect(),
|
||||
span: Span::test_data(),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn make_list(vals: Vec<&str>) -> Value {
|
||||
|
Reference in New Issue
Block a user