mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 07:16:05 +02:00
Use Record::get
instead of Value
functions (#10925)
# Description Where appropriate, this PR replaces instances of `Value::get_data_by_key` and `Value::follow_cell_path` with `Record::get`. This avoids some unnecessary clones and simplifies the code in some places.
This commit is contained in:
@ -194,7 +194,7 @@ fn push_empty_column(data: &mut Vec<Vec<String>>) {
|
||||
mod util {
|
||||
use crate::debug::explain::debug_string_without_formatting;
|
||||
use nu_engine::get_columns;
|
||||
use nu_protocol::{ast::PathMember, Span, Value};
|
||||
use nu_protocol::Value;
|
||||
|
||||
/// Try to build column names and a table grid.
|
||||
pub fn collect_input(value: Value) -> (Vec<String>, Vec<Vec<String>>) {
|
||||
@ -265,30 +265,19 @@ mod util {
|
||||
}
|
||||
|
||||
fn record_create_row(headers: &[String], item: &Value) -> Vec<String> {
|
||||
let mut rows = vec![String::default(); headers.len()];
|
||||
|
||||
for (i, header) in headers.iter().enumerate() {
|
||||
let value = record_lookup_value(item, header);
|
||||
rows[i] = debug_string_without_formatting(&value);
|
||||
}
|
||||
|
||||
rows
|
||||
}
|
||||
|
||||
fn record_lookup_value(item: &Value, header: &str) -> Value {
|
||||
match item {
|
||||
Value::Record { .. } => {
|
||||
let path = PathMember::String {
|
||||
val: header.to_owned(),
|
||||
span: Span::unknown(),
|
||||
optional: false,
|
||||
};
|
||||
|
||||
item.clone()
|
||||
.follow_cell_path(&[path], false)
|
||||
.unwrap_or_else(|_| item.clone())
|
||||
}
|
||||
item => item.clone(),
|
||||
if let Value::Record { val, .. } = item {
|
||||
headers
|
||||
.iter()
|
||||
.map(|col| {
|
||||
val.get(col)
|
||||
.map(debug_string_without_formatting)
|
||||
.unwrap_or_else(String::new)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
// should never reach here due to `get_columns` above which will return
|
||||
// empty columns if any value in the list is not a record
|
||||
vec![String::new(); headers.len()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ pub fn compact(
|
||||
Value::Nothing { .. } => false,
|
||||
Value::Record { val, .. } => {
|
||||
for column in columns.iter() {
|
||||
match item.get_data_by_key(column) {
|
||||
match val.get(column) {
|
||||
None => return false,
|
||||
Some(x) => {
|
||||
if let Value::Nothing { .. } = x {
|
||||
|
@ -258,7 +258,7 @@ fn join_rows(
|
||||
val: this_record, ..
|
||||
} = this_row
|
||||
{
|
||||
if let Some(this_valkey) = this_row.get_data_by_key(this_join_key) {
|
||||
if let Some(this_valkey) = this_record.get(this_join_key) {
|
||||
if let Some(other_rows) = other.get(&this_valkey.into_string(sep, config)) {
|
||||
if matches!(include_inner, IncludeInner::Yes) {
|
||||
for other_record in other_rows {
|
||||
@ -287,8 +287,9 @@ fn join_rows(
|
||||
.iter()
|
||||
.map(|key| {
|
||||
let val = if Some(key.as_ref()) == shared_join_key {
|
||||
this_row
|
||||
.get_data_by_key(key)
|
||||
this_record
|
||||
.get(key)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Value::nothing(span))
|
||||
} else {
|
||||
Value::nothing(span)
|
||||
@ -339,7 +340,7 @@ fn lookup_table<'a>(
|
||||
let mut map = HashMap::<String, Vec<&'a Record>>::with_capacity(cap);
|
||||
for row in rows {
|
||||
if let Value::Record { val: record, .. } = row {
|
||||
if let Some(val) = &row.get_data_by_key(on) {
|
||||
if let Some(val) = record.get(on) {
|
||||
let valkey = val.into_string(sep, config);
|
||||
map.entry(valkey).or_default().push(record);
|
||||
}
|
||||
|
@ -125,13 +125,21 @@ pub fn split(
|
||||
|
||||
match grouper {
|
||||
Grouper::ByColumn(Some(column_name)) => {
|
||||
let block = move |_, row: &Value| match row.get_data_by_key(&column_name.item) {
|
||||
Some(group_key) => Ok(group_key.as_string()?),
|
||||
None => Err(ShellError::CantFindColumn {
|
||||
col_name: column_name.item.to_string(),
|
||||
span: column_name.span,
|
||||
src_span: row.span(),
|
||||
}),
|
||||
let block = move |_, row: &Value| {
|
||||
let group_key = if let Value::Record { val: row, .. } = row {
|
||||
row.get(&column_name.item)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match group_key {
|
||||
Some(group_key) => Ok(group_key.as_string()?),
|
||||
None => Err(ShellError::CantFindColumn {
|
||||
col_name: column_name.item.to_string(),
|
||||
span: column_name.span,
|
||||
src_span: row.span(),
|
||||
}),
|
||||
}
|
||||
};
|
||||
|
||||
data_split(values, Some(&block), span)
|
||||
|
@ -75,14 +75,17 @@ fn table_to_delimited(
|
||||
.expect("can not write.");
|
||||
|
||||
for l in vals {
|
||||
let mut row = vec![];
|
||||
for desc in &merged_descriptors {
|
||||
row.push(match l.to_owned().get_data_by_key(desc) {
|
||||
Some(s) => to_string_tagged_value(&s, config, head, span)?,
|
||||
None => String::new(),
|
||||
});
|
||||
// should always be true because of `find_non_record` above
|
||||
if let Value::Record { val: l, .. } = l {
|
||||
let mut row = vec![];
|
||||
for desc in &merged_descriptors {
|
||||
row.push(match l.get(desc) {
|
||||
Some(s) => to_string_tagged_value(s, config, head, span)?,
|
||||
None => String::new(),
|
||||
});
|
||||
}
|
||||
wtr.write_record(&row).expect("can not write");
|
||||
}
|
||||
wtr.write_record(&row).expect("can not write");
|
||||
}
|
||||
}
|
||||
writer_to_string(wtr).map_err(|_| make_conversion_error("table", span))
|
||||
|
@ -105,27 +105,24 @@ fn to_md(
|
||||
}
|
||||
|
||||
fn fragment(input: Value, pretty: bool, config: &Config) -> String {
|
||||
let headers = input.columns();
|
||||
let mut out = String::new();
|
||||
|
||||
if headers.len() == 1 {
|
||||
let markup = match headers[0].to_ascii_lowercase().as_ref() {
|
||||
"h1" => "# ".to_string(),
|
||||
"h2" => "## ".to_string(),
|
||||
"h3" => "### ".to_string(),
|
||||
"blockquote" => "> ".to_string(),
|
||||
if let Value::Record { val, .. } = &input {
|
||||
match val.get_index(0) {
|
||||
Some((header, data)) if val.len() == 1 => {
|
||||
let markup = match header.to_ascii_lowercase().as_ref() {
|
||||
"h1" => "# ".to_string(),
|
||||
"h2" => "## ".to_string(),
|
||||
"h3" => "### ".to_string(),
|
||||
"blockquote" => "> ".to_string(),
|
||||
_ => return table(input.into_pipeline_data(), pretty, config),
|
||||
};
|
||||
|
||||
_ => return table(input.into_pipeline_data(), pretty, config),
|
||||
};
|
||||
|
||||
out.push_str(&markup);
|
||||
let data = match input.get_data_by_key(&headers[0]) {
|
||||
Some(v) => v,
|
||||
None => input,
|
||||
};
|
||||
out.push_str(&data.into_string("|", config));
|
||||
} else if let Value::Record { .. } = input {
|
||||
out = table(input.into_pipeline_data(), pretty, config)
|
||||
out.push_str(&markup);
|
||||
out.push_str(&data.into_string("|", config));
|
||||
}
|
||||
_ => out = table(input.into_pipeline_data(), pretty, config),
|
||||
}
|
||||
} else {
|
||||
out = input.into_string("|", config)
|
||||
}
|
||||
@ -164,10 +161,11 @@ fn table(input: PipelineData, pretty: bool, config: &Config) -> String {
|
||||
let span = row.span();
|
||||
|
||||
match row.to_owned() {
|
||||
Value::Record { .. } => {
|
||||
Value::Record { val: row, .. } => {
|
||||
for i in 0..headers.len() {
|
||||
let data = row.get_data_by_key(&headers[i]);
|
||||
let value_string = data
|
||||
let value_string = row
|
||||
.get(&headers[i])
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Value::nothing(span))
|
||||
.into_string(", ", config);
|
||||
let new_column_width = value_string.len();
|
||||
|
@ -109,47 +109,50 @@ fn to_xml_entry<W: Write>(
|
||||
return to_xml_text(val.as_str(), span, writer);
|
||||
}
|
||||
|
||||
if !matches!(entry, Value::Record { .. }) {
|
||||
return Err(ShellError::CantConvert {
|
||||
if let Value::Record { val: record, .. } = &entry {
|
||||
// If key is not found it is assumed to be nothing. This way
|
||||
// user can write a tag like {tag: a content: [...]} instead
|
||||
// of longer {tag: a attributes: {} content: [...]}
|
||||
let tag = record
|
||||
.get(COLUMN_TAG_NAME)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Value::nothing(Span::unknown()));
|
||||
let attrs = record
|
||||
.get(COLUMN_ATTRS_NAME)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Value::nothing(Span::unknown()));
|
||||
let content = record
|
||||
.get(COLUMN_CONTENT_NAME)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Value::nothing(Span::unknown()));
|
||||
|
||||
let content_span = content.span();
|
||||
let tag_span = tag.span();
|
||||
match (tag, attrs, content) {
|
||||
(Value::Nothing { .. }, Value::Nothing { .. }, Value::String { val, .. }) => {
|
||||
// Strings can not appear on top level of document
|
||||
if top_level {
|
||||
return Err(ShellError::CantConvert {
|
||||
to_type: "XML".into(),
|
||||
from_type: entry.get_type().to_string(),
|
||||
span: entry_span,
|
||||
help: Some("Strings can not be a root element of document".into()),
|
||||
});
|
||||
}
|
||||
to_xml_text(val.as_str(), content_span, writer)
|
||||
}
|
||||
(Value::String { val: tag_name, .. }, attrs, children) => to_tag_like(
|
||||
entry_span, tag_name, tag_span, attrs, children, top_level, writer,
|
||||
),
|
||||
_ => Ok(()),
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::CantConvert {
|
||||
to_type: "XML".into(),
|
||||
from_type: entry.get_type().to_string(),
|
||||
span: entry_span,
|
||||
help: Some("Xml entry expected to be a record".into()),
|
||||
});
|
||||
};
|
||||
|
||||
// If key is not found it is assumed to be nothing. This way
|
||||
// user can write a tag like {tag: a content: [...]} instead
|
||||
// of longer {tag: a attributes: {} content: [...]}
|
||||
let tag = entry
|
||||
.get_data_by_key(COLUMN_TAG_NAME)
|
||||
.unwrap_or_else(|| Value::nothing(Span::unknown()));
|
||||
let attrs = entry
|
||||
.get_data_by_key(COLUMN_ATTRS_NAME)
|
||||
.unwrap_or_else(|| Value::nothing(Span::unknown()));
|
||||
let content = entry
|
||||
.get_data_by_key(COLUMN_CONTENT_NAME)
|
||||
.unwrap_or_else(|| Value::nothing(Span::unknown()));
|
||||
|
||||
let content_span = content.span();
|
||||
let tag_span = tag.span();
|
||||
match (tag, attrs, content) {
|
||||
(Value::Nothing { .. }, Value::Nothing { .. }, Value::String { val, .. }) => {
|
||||
// Strings can not appear on top level of document
|
||||
if top_level {
|
||||
return Err(ShellError::CantConvert {
|
||||
to_type: "XML".into(),
|
||||
from_type: entry.get_type().to_string(),
|
||||
span: entry_span,
|
||||
help: Some("Strings can not be a root element of document".into()),
|
||||
});
|
||||
}
|
||||
to_xml_text(val.as_str(), content_span, writer)
|
||||
}
|
||||
(Value::String { val: tag_name, .. }, attrs, children) => to_tag_like(
|
||||
entry_span, tag_name, tag_span, attrs, children, top_level, writer,
|
||||
),
|
||||
_ => Ok(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,10 @@ use lscolors::Style;
|
||||
use nu_engine::env_to_string;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, PathMember},
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
use nu_term_grid::grid::{Alignment, Cell, Direction, Filling, Grid, GridOptions};
|
||||
use nu_utils::get_ls_colors;
|
||||
@ -77,7 +77,7 @@ prints out the list properly."#
|
||||
match input {
|
||||
PipelineData::Value(Value::List { vals, .. }, ..) => {
|
||||
// dbg!("value::list");
|
||||
let data = convert_to_list(vals, config, call.head)?;
|
||||
let data = convert_to_list(vals, config)?;
|
||||
if let Some(items) = data {
|
||||
Ok(create_grid_output(
|
||||
items,
|
||||
@ -94,7 +94,7 @@ prints out the list properly."#
|
||||
}
|
||||
PipelineData::ListStream(stream, ..) => {
|
||||
// dbg!("value::stream");
|
||||
let data = convert_to_list(stream, config, call.head)?;
|
||||
let data = convert_to_list(stream, config)?;
|
||||
if let Some(items) = data {
|
||||
Ok(create_grid_output(
|
||||
items,
|
||||
@ -253,7 +253,6 @@ fn create_grid_output(
|
||||
fn convert_to_list(
|
||||
iter: impl IntoIterator<Item = Value>,
|
||||
config: &Config,
|
||||
head: Span,
|
||||
) -> Result<Option<Vec<(usize, String, String)>>, ShellError> {
|
||||
let mut iter = iter.into_iter().peekable();
|
||||
|
||||
@ -273,21 +272,14 @@ fn convert_to_list(
|
||||
row.push(item.nonerror_into_string(", ", config)?)
|
||||
} else {
|
||||
for header in headers.iter().skip(1) {
|
||||
let result = match item {
|
||||
Value::Record { .. } => item.clone().follow_cell_path(
|
||||
&[PathMember::String {
|
||||
val: header.into(),
|
||||
span: head,
|
||||
optional: false,
|
||||
}],
|
||||
false,
|
||||
),
|
||||
_ => Ok(item.clone()),
|
||||
let result = match &item {
|
||||
Value::Record { val, .. } => val.get(header),
|
||||
item => Some(item),
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(value) => row.push(value.nonerror_into_string(", ", config)?),
|
||||
Err(_) => row.push(String::new()),
|
||||
Some(value) => row.push(value.nonerror_into_string(", ", config)?),
|
||||
None => row.push(String::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user