mirror of
https://github.com/nushell/nushell.git
synced 2025-03-29 17:16:49 +01:00
Format implementation, fix on Echo
Now, Echo converts multiple values in a ValueStream, but it simply forwards a single Value; if no PipelineData is detected as an input, an empty string is returned as a single Value.
This commit is contained in:
parent
af5799c702
commit
806cd4851f
@ -29,10 +29,23 @@ impl Command for Echo {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
call.rest(engine_state, stack, 0).map(|to_be_echoed| {
|
call.rest(engine_state, stack, 0).map(|to_be_echoed| {
|
||||||
PipelineData::Stream(ValueStream::from_stream(
|
let n = to_be_echoed.len();
|
||||||
to_be_echoed.into_iter(),
|
match n.cmp(&1usize) {
|
||||||
engine_state.ctrlc.clone(),
|
// More than one value is converted in a stream of values
|
||||||
))
|
std::cmp::Ordering::Greater => PipelineData::Stream(ValueStream::from_stream(
|
||||||
|
to_be_echoed.into_iter(),
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)),
|
||||||
|
|
||||||
|
// But a single value can be forwarded as it is
|
||||||
|
std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone()),
|
||||||
|
|
||||||
|
// When there are no elements, we echo the empty string
|
||||||
|
std::cmp::Ordering::Less => PipelineData::Value(Value::String {
|
||||||
|
val: "".to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,10 +54,7 @@ impl Command for Echo {
|
|||||||
Example {
|
Example {
|
||||||
description: "Put a hello message in the pipeline",
|
description: "Put a hello message in the pipeline",
|
||||||
example: "echo 'hello'",
|
example: "echo 'hello'",
|
||||||
result: Some(Value::List {
|
result: Some(Value::test_string("hello")),
|
||||||
vals: vec![Value::test_string("hello")],
|
|
||||||
span: Span::new(0, 0),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Print the value of the special '$nu' variable",
|
description: "Print the value of the special '$nu' variable",
|
||||||
|
@ -24,6 +24,8 @@ pub fn test_examples(cmd: impl Command + 'static) {
|
|||||||
working_set.add_decl(Box::new(Math));
|
working_set.add_decl(Box::new(Math));
|
||||||
working_set.add_decl(Box::new(Date));
|
working_set.add_decl(Box::new(Date));
|
||||||
|
|
||||||
|
use super::Echo;
|
||||||
|
working_set.add_decl(Box::new(Echo));
|
||||||
// Adding the command that is being tested to the working set
|
// Adding the command that is being tested to the working set
|
||||||
working_set.add_decl(Box::new(cmd));
|
working_set.add_decl(Box::new(cmd));
|
||||||
|
|
||||||
|
193
crates/nu-command/src/strings/format/command.rs
Normal file
193
crates/nu-command/src/strings/format/command.rs
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::ast::{Call, PathMember};
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{
|
||||||
|
Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, ValueStream,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Format;
|
||||||
|
|
||||||
|
impl Command for Format {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"format"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("format").required(
|
||||||
|
"pattern",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"the pattern to output. e.g.) \"{foo}: {bar}\"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Format columns into a string using a simple pattern."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let specified_pattern: Result<Value, ShellError> = call.req(engine_state, stack, 0);
|
||||||
|
match specified_pattern {
|
||||||
|
Err(e) => Err(e),
|
||||||
|
Ok(pattern) => {
|
||||||
|
// Handle the pattern
|
||||||
|
let string_pattern = pattern.as_string().unwrap();
|
||||||
|
let ops = extract_formatting_operations(string_pattern);
|
||||||
|
format(input, &ops)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Print filenames with their sizes",
|
||||||
|
example: "ls | format '{name}: {size}'",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Print elements from some columns of a table",
|
||||||
|
example: "echo [[col1, col2]; [v1, v2] [v3, v4]] | format '{col2}'",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![Value::test_string("v2"), Value::test_string("v4")],
|
||||||
|
span: Span::new(0, 0),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum FormatOperation {
|
||||||
|
FixedText(String),
|
||||||
|
ValueFromColumn(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_formatting_operations(input: String) -> Vec<FormatOperation> {
|
||||||
|
let mut output = vec![];
|
||||||
|
|
||||||
|
let mut characters = input.chars();
|
||||||
|
'outer: loop {
|
||||||
|
let mut before_bracket = String::new();
|
||||||
|
|
||||||
|
for ch in &mut characters {
|
||||||
|
if ch == '{' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
before_bracket.push(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !before_bracket.is_empty() {
|
||||||
|
output.push(FormatOperation::FixedText(before_bracket.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut column_name = String::new();
|
||||||
|
|
||||||
|
for ch in &mut characters {
|
||||||
|
if ch == '}' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
column_name.push(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !column_name.is_empty() {
|
||||||
|
output.push(FormatOperation::ValueFromColumn(column_name.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if before_bracket.is_empty() && column_name.is_empty() {
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(
|
||||||
|
input_data: PipelineData,
|
||||||
|
format_operations: &[FormatOperation],
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let data_as_value = input_data.into_value();
|
||||||
|
eprintln!("{:#?}", data_as_value);
|
||||||
|
match data_as_value {
|
||||||
|
Value::Record { .. } => match format_record(format_operations, &data_as_value) {
|
||||||
|
Ok(value) => Ok(PipelineData::Value(Value::string(value, Span::unknown()))),
|
||||||
|
Err(value) => Err(value),
|
||||||
|
},
|
||||||
|
|
||||||
|
Value::List { vals, .. } => {
|
||||||
|
let mut list = vec![];
|
||||||
|
for val in vals.iter() {
|
||||||
|
match val {
|
||||||
|
Value::Record { .. } => match format_record(format_operations, val) {
|
||||||
|
Ok(value) => {
|
||||||
|
list.push(Value::string(value, Span::unknown()));
|
||||||
|
}
|
||||||
|
Err(value) => {
|
||||||
|
return Err(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
return Err(ShellError::UnsupportedInput(
|
||||||
|
"Input data is not supported by this command.".to_string(),
|
||||||
|
Span::unknown(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(PipelineData::Stream(ValueStream::from_stream(
|
||||||
|
list.into_iter(),
|
||||||
|
None,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
_ => Err(ShellError::UnsupportedInput(
|
||||||
|
"Input data is not supported by this command.".to_string(),
|
||||||
|
Span::unknown(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_record(
|
||||||
|
format_operations: &[FormatOperation],
|
||||||
|
data_as_value: &Value,
|
||||||
|
) -> Result<String, ShellError> {
|
||||||
|
let mut output = String::new();
|
||||||
|
for op in format_operations {
|
||||||
|
match op {
|
||||||
|
FormatOperation::FixedText(s) => output.push_str(s.as_str()),
|
||||||
|
|
||||||
|
// The referenced code suggest to use the correct Span's
|
||||||
|
// See: https://github.com/nushell/nushell/blob/c4af5df828135159633d4bc3070ce800518a42a2/crates/nu-command/src/commands/strings/format/command.rs#L61
|
||||||
|
FormatOperation::ValueFromColumn(col_name) => {
|
||||||
|
match data_as_value
|
||||||
|
.clone()
|
||||||
|
.follow_cell_path(&[PathMember::String {
|
||||||
|
val: col_name.clone(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}]) {
|
||||||
|
Ok(value_at_column) => {
|
||||||
|
output.push_str(value_at_column.as_string().unwrap().as_str())
|
||||||
|
}
|
||||||
|
Err(se) => return Err(se),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use super::Format;
|
||||||
|
use crate::test_examples;
|
||||||
|
test_examples(Format {})
|
||||||
|
}
|
||||||
|
}
|
@ -1,53 +0,0 @@
|
|||||||
//use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, SyntaxShape};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Format;
|
|
||||||
|
|
||||||
impl Command for Format {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"format"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("format").required(
|
|
||||||
"pattern",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"the pattern to output. e.g.) \"{foo}: {bar}\"",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Format columns into a string using a simple pattern."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
_engine_state: &EngineState,
|
|
||||||
_stack: &mut Stack,
|
|
||||||
_call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Print filenames with their sizes",
|
|
||||||
example: "ls | format '{name}: {size}'",
|
|
||||||
result: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use super::Format;
|
|
||||||
use crate::test_examples;
|
|
||||||
test_examples(Format {})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,3 @@
|
|||||||
pub mod format;
|
pub mod command;
|
||||||
|
|
||||||
pub use format::Format;
|
pub use command::Format;
|
||||||
|
Loading…
Reference in New Issue
Block a user