mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 16:05:01 +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,7 +2,7 @@ use eml_parser::eml::*;
|
||||
use eml_parser::EmlParser;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_plugin::{EvaluatedCall, LabeledError};
|
||||
use nu_protocol::{PluginExample, ShellError, Span, Spanned, Value};
|
||||
use nu_protocol::{record, PluginExample, Record, ShellError, Span, Value};
|
||||
|
||||
const DEFAULT_BODY_PREVIEW: usize = 50;
|
||||
pub const CMD_NAME: &str = "from eml";
|
||||
@ -24,7 +24,7 @@ Subject: Welcome
|
||||
To: someone@somewhere.com
|
||||
Test' | from eml"
|
||||
.into(),
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"Subject".to_string(),
|
||||
"From".to_string(),
|
||||
@ -33,26 +33,23 @@ Test' | from eml"
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_string("Welcome"),
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["Name".to_string(), "Address".to_string()],
|
||||
vals: vec![
|
||||
Value::nothing(Span::test_data()),
|
||||
Value::test_string("test@email.com"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["Name".to_string(), "Address".to_string()],
|
||||
vals: vec![
|
||||
Value::nothing(Span::test_data()),
|
||||
Value::test_string("someone@somewhere.com"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
Value::test_string("Test"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
PluginExample {
|
||||
description: "Convert eml structured data into record".into(),
|
||||
@ -61,7 +58,7 @@ Subject: Welcome
|
||||
To: someone@somewhere.com
|
||||
Test' | from eml -b 1"
|
||||
.into(),
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"Subject".to_string(),
|
||||
"From".to_string(),
|
||||
@ -70,26 +67,23 @@ Test' | from eml -b 1"
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_string("Welcome"),
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec!["Name".to_string(), "Address".to_string()],
|
||||
vals: vec![
|
||||
Value::nothing(Span::test_data()),
|
||||
Value::test_string("test@email.com"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["Name".to_string(), "Address".to_string()],
|
||||
vals: vec![
|
||||
Value::nothing(Span::test_data()),
|
||||
Value::test_string("someone@somewhere.com"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
Value::test_string("T"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -115,11 +109,13 @@ fn emailaddress_to_value(span: Span, email_address: &EmailAddress) -> Value {
|
||||
),
|
||||
};
|
||||
|
||||
Value::Record {
|
||||
cols: vec!["Name".to_string(), "Address".to_string()],
|
||||
vals: vec![n, a],
|
||||
Value::record(
|
||||
record! {
|
||||
"Name" => n,
|
||||
"Address" => a,
|
||||
},
|
||||
span,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn headerfieldvalue_to_value(head: Span, value: &HeaderFieldValue) -> Value {
|
||||
@ -186,8 +182,5 @@ fn from_eml(input: &Value, body_preview: usize, head: Span) -> Result<Value, Lab
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Value::from(Spanned {
|
||||
item: collected,
|
||||
span: head,
|
||||
}))
|
||||
Ok(Value::record(collected.into_iter().collect(), head))
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use ical::parser::ical::component::*;
|
||||
use ical::property::Property;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_plugin::{EvaluatedCall, LabeledError};
|
||||
use nu_protocol::{PluginExample, ShellError, Span, Spanned, Value};
|
||||
use nu_protocol::{record, PluginExample, Record, ShellError, Span, Value};
|
||||
use std::io::BufReader;
|
||||
|
||||
pub const CMD_NAME: &str = "from ics";
|
||||
@ -50,7 +50,7 @@ pub fn examples() -> Vec<PluginExample> {
|
||||
.into(),
|
||||
description: "Converts ics formatted string to table".into(),
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"properties".to_string(),
|
||||
"events".to_string(),
|
||||
@ -90,37 +90,25 @@ pub fn examples() -> Vec<PluginExample> {
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
}]
|
||||
}
|
||||
|
||||
fn calendar_to_value(calendar: IcalCalendar, span: Span) -> Value {
|
||||
let mut row = IndexMap::new();
|
||||
|
||||
row.insert(
|
||||
"properties".to_string(),
|
||||
properties_to_value(calendar.properties, span),
|
||||
);
|
||||
row.insert("events".to_string(), events_to_value(calendar.events, span));
|
||||
row.insert("alarms".to_string(), alarms_to_value(calendar.alarms, span));
|
||||
row.insert("to-Dos".to_string(), todos_to_value(calendar.todos, span));
|
||||
row.insert(
|
||||
"journals".to_string(),
|
||||
journals_to_value(calendar.journals, span),
|
||||
);
|
||||
row.insert(
|
||||
"free-busys".to_string(),
|
||||
free_busys_to_value(calendar.free_busys, span),
|
||||
);
|
||||
row.insert(
|
||||
"timezones".to_string(),
|
||||
timezones_to_value(calendar.timezones, span),
|
||||
);
|
||||
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! {
|
||||
"properties" => properties_to_value(calendar.properties, span),
|
||||
"events" => events_to_value(calendar.events, span),
|
||||
"alarms" => alarms_to_value(calendar.alarms, span),
|
||||
"to-Dos" => todos_to_value(calendar.todos, span),
|
||||
"journals" => journals_to_value(calendar.journals, span),
|
||||
"free-busys" => free_busys_to_value(calendar.free_busys, span),
|
||||
"timezones" => timezones_to_value(calendar.timezones, span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
fn events_to_value(events: Vec<IcalEvent>, span: Span) -> Value {
|
||||
@ -128,13 +116,13 @@ fn events_to_value(events: Vec<IcalEvent>, span: Span) -> Value {
|
||||
vals: events
|
||||
.into_iter()
|
||||
.map(|event| {
|
||||
let mut row = IndexMap::new();
|
||||
row.insert(
|
||||
"properties".to_string(),
|
||||
properties_to_value(event.properties, span),
|
||||
);
|
||||
row.insert("alarms".to_string(), alarms_to_value(event.alarms, span));
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! {
|
||||
"properties" => properties_to_value(event.properties, span),
|
||||
"alarms" => alarms_to_value(event.alarms, span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
@ -146,12 +134,10 @@ fn alarms_to_value(alarms: Vec<IcalAlarm>, span: Span) -> Value {
|
||||
vals: alarms
|
||||
.into_iter()
|
||||
.map(|alarm| {
|
||||
let mut row = IndexMap::new();
|
||||
row.insert(
|
||||
"properties".to_string(),
|
||||
properties_to_value(alarm.properties, span),
|
||||
);
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! { "properties" => properties_to_value(alarm.properties, span), },
|
||||
span,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
@ -163,13 +149,13 @@ fn todos_to_value(todos: Vec<IcalTodo>, span: Span) -> Value {
|
||||
vals: todos
|
||||
.into_iter()
|
||||
.map(|todo| {
|
||||
let mut row = IndexMap::new();
|
||||
row.insert(
|
||||
"properties".to_string(),
|
||||
properties_to_value(todo.properties, span),
|
||||
);
|
||||
row.insert("alarms".to_string(), alarms_to_value(todo.alarms, span));
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! {
|
||||
"properties" => properties_to_value(todo.properties, span),
|
||||
"alarms" => alarms_to_value(todo.alarms, span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
@ -181,12 +167,10 @@ fn journals_to_value(journals: Vec<IcalJournal>, span: Span) -> Value {
|
||||
vals: journals
|
||||
.into_iter()
|
||||
.map(|journal| {
|
||||
let mut row = IndexMap::new();
|
||||
row.insert(
|
||||
"properties".to_string(),
|
||||
properties_to_value(journal.properties, span),
|
||||
);
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! { "properties" => properties_to_value(journal.properties, span), },
|
||||
span,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
@ -198,12 +182,10 @@ fn free_busys_to_value(free_busys: Vec<IcalFreeBusy>, span: Span) -> Value {
|
||||
vals: free_busys
|
||||
.into_iter()
|
||||
.map(|free_busy| {
|
||||
let mut row = IndexMap::new();
|
||||
row.insert(
|
||||
"properties".to_string(),
|
||||
properties_to_value(free_busy.properties, span),
|
||||
);
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! { "properties" => properties_to_value(free_busy.properties, span) },
|
||||
span,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
@ -215,16 +197,13 @@ fn timezones_to_value(timezones: Vec<IcalTimeZone>, span: Span) -> Value {
|
||||
vals: timezones
|
||||
.into_iter()
|
||||
.map(|timezone| {
|
||||
let mut row = IndexMap::new();
|
||||
row.insert(
|
||||
"properties".to_string(),
|
||||
properties_to_value(timezone.properties, span),
|
||||
);
|
||||
row.insert(
|
||||
"transitions".to_string(),
|
||||
timezone_transitions_to_value(timezone.transitions, span),
|
||||
);
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! {
|
||||
"properties" => properties_to_value(timezone.properties, span),
|
||||
"transitions" => timezone_transitions_to_value(timezone.transitions, span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
@ -236,12 +215,10 @@ fn timezone_transitions_to_value(transitions: Vec<IcalTimeZoneTransition>, span:
|
||||
vals: transitions
|
||||
.into_iter()
|
||||
.map(|transition| {
|
||||
let mut row = IndexMap::new();
|
||||
row.insert(
|
||||
"properties".to_string(),
|
||||
properties_to_value(transition.properties, span),
|
||||
);
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! { "properties" => properties_to_value(transition.properties, span) },
|
||||
span,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
@ -253,8 +230,6 @@ fn properties_to_value(properties: Vec<Property>, span: Span) -> Value {
|
||||
vals: properties
|
||||
.into_iter()
|
||||
.map(|prop| {
|
||||
let mut row = IndexMap::new();
|
||||
|
||||
let name = Value::String {
|
||||
val: prop.name,
|
||||
span,
|
||||
@ -268,10 +243,14 @@ fn properties_to_value(properties: Vec<Property>, span: Span) -> Value {
|
||||
None => Value::nothing(span),
|
||||
};
|
||||
|
||||
row.insert("name".to_string(), name);
|
||||
row.insert("value".to_string(), value);
|
||||
row.insert("params".to_string(), params);
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! {
|
||||
"name" => name,
|
||||
"value" => value,
|
||||
"params" => params,
|
||||
},
|
||||
span,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
@ -290,5 +269,5 @@ fn params_to_value(params: Vec<(String, Vec<String>)>, span: Span) -> Value {
|
||||
row.insert(param_name, values);
|
||||
}
|
||||
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(row.into_iter().collect(), span)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use nu_plugin::{EvaluatedCall, LabeledError};
|
||||
use nu_protocol::{PluginExample, ShellError, Span, Value};
|
||||
use nu_protocol::{PluginExample, Record, ShellError, Value};
|
||||
|
||||
pub const CMD_NAME: &str = "from ini";
|
||||
|
||||
@ -11,52 +11,34 @@ pub fn from_ini_call(call: &EvaluatedCall, input: &Value) -> Result<Value, Label
|
||||
let ini_config: Result<ini::Ini, ini::ParseError> = ini::Ini::load_from_str(&input_string);
|
||||
match ini_config {
|
||||
Ok(config) => {
|
||||
let mut sections: Vec<String> = Vec::new();
|
||||
let mut sections_key_value_pairs: Vec<Value> = Vec::new();
|
||||
let mut sections = Record::new();
|
||||
|
||||
for (section, properties) in config.iter() {
|
||||
let mut keys_for_section: Vec<String> = Vec::new();
|
||||
let mut values_for_section: Vec<Value> = Vec::new();
|
||||
let mut section_record = Record::new();
|
||||
|
||||
// section's key value pairs
|
||||
for (key, value) in properties.iter() {
|
||||
section_record.push(key, Value::string(value, span));
|
||||
}
|
||||
|
||||
let section_record = Value::record(section_record, span);
|
||||
|
||||
// section
|
||||
match section {
|
||||
Some(section_name) => {
|
||||
sections.push(section_name.to_owned());
|
||||
sections.push(section_name, section_record);
|
||||
}
|
||||
None => {
|
||||
// Section (None) allows for key value pairs without a section
|
||||
if !properties.is_empty() {
|
||||
sections.push(String::new());
|
||||
sections.push(String::new(), section_record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// section's key value pairs
|
||||
for (key, value) in properties.iter() {
|
||||
keys_for_section.push(key.to_owned());
|
||||
values_for_section.push(Value::String {
|
||||
val: value.to_owned(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
|
||||
// section with its key value pairs
|
||||
// Only add section if contains key,value pair
|
||||
if !properties.is_empty() {
|
||||
sections_key_value_pairs.push(Value::Record {
|
||||
cols: keys_for_section,
|
||||
vals: values_for_section,
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// all sections with all its key value pairs
|
||||
Ok(Value::Record {
|
||||
cols: sections,
|
||||
vals: sections_key_value_pairs,
|
||||
span,
|
||||
})
|
||||
Ok(Value::record(sections, span))
|
||||
}
|
||||
Err(err) => Err(ShellError::UnsupportedInput(
|
||||
format!("Could not load ini: {err}"),
|
||||
@ -75,14 +57,12 @@ a=1
|
||||
b=2' | from ini"
|
||||
.into(),
|
||||
description: "Converts ini formatted string to record".into(),
|
||||
result: Some(Value::Record {
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["foo".to_string()],
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![Value::test_string("1"), Value::test_string("2")],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
})],
|
||||
})),
|
||||
}]
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use ical::parser::vcard::component::*;
|
||||
use ical::property::Property;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_plugin::{EvaluatedCall, LabeledError};
|
||||
use nu_protocol::{PluginExample, ShellError, Span, Spanned, Value};
|
||||
use nu_protocol::{record, PluginExample, Record, ShellError, Span, Value};
|
||||
|
||||
pub const CMD_NAME: &str = "from vcf";
|
||||
|
||||
@ -50,11 +50,11 @@ END:VCARD' | from vcf"
|
||||
.into(),
|
||||
description: "Converts ics formatted string to table".into(),
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
vals: vec![Value::test_record(Record {
|
||||
cols: vec!["properties".to_string()],
|
||||
vals: vec![Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"value".to_string(),
|
||||
@ -67,9 +67,8 @@ END:VCARD' | from vcf"
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"value".to_string(),
|
||||
@ -82,9 +81,8 @@ END:VCARD' | from vcf"
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"value".to_string(),
|
||||
@ -97,25 +95,21 @@ END:VCARD' | from vcf"
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
}),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
})],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
}]
|
||||
}
|
||||
|
||||
fn contact_to_value(contact: VcardContact, span: Span) -> Value {
|
||||
let mut row = IndexMap::new();
|
||||
row.insert(
|
||||
"properties".to_string(),
|
||||
properties_to_value(contact.properties, span),
|
||||
);
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! { "properties" => properties_to_value(contact.properties, span) },
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
fn properties_to_value(properties: Vec<Property>, span: Span) -> Value {
|
||||
@ -123,8 +117,6 @@ fn properties_to_value(properties: Vec<Property>, span: Span) -> Value {
|
||||
vals: properties
|
||||
.into_iter()
|
||||
.map(|prop| {
|
||||
let mut row = IndexMap::new();
|
||||
|
||||
let name = Value::String {
|
||||
val: prop.name,
|
||||
span,
|
||||
@ -138,10 +130,14 @@ fn properties_to_value(properties: Vec<Property>, span: Span) -> Value {
|
||||
None => Value::Nothing { span },
|
||||
};
|
||||
|
||||
row.insert("name".to_string(), name);
|
||||
row.insert("value".to_string(), value);
|
||||
row.insert("params".to_string(), params);
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(
|
||||
record! {
|
||||
"name" => name,
|
||||
"value" => value,
|
||||
"params" => params,
|
||||
},
|
||||
span,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
@ -160,5 +156,5 @@ fn params_to_value(params: Vec<(String, Vec<String>)>, span: Span) -> Value {
|
||||
row.insert(param_name, values);
|
||||
}
|
||||
|
||||
Value::from(Spanned { item: row, span })
|
||||
Value::record(row.into_iter().collect(), span)
|
||||
}
|
||||
|
Reference in New Issue
Block a user