mirror of
https://github.com/nushell/nushell.git
synced 2025-05-17 00:10:53 +02:00
# Description This PR renames the conversion functions on `Value` to be more consistent. It follows the Rust [API guidelines](https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv) for ad-hoc conversions. The conversion functions on `Value` now come in a few forms: - `coerce_{type}` takes a `&Value` and attempts to convert the value to `type` (e.g., `i64` are converted to `f64`). This is the old behavior of some of the `as_{type}` functions -- these functions have simply been renamed to better reflect what they do. - The new `as_{type}` functions take a `&Value` and returns an `Ok` result only if the value is of `type` (no conversion is attempted). The returned value will be borrowed if `type` is non-`Copy`, otherwise an owned value is returned. - `into_{type}` exists for non-`Copy` types, but otherwise does not attempt conversion just like `as_type`. It takes an owned `Value` and always returns an owned result. - `coerce_into_{type}` has the same relationship with `coerce_{type}` as `into_{type}` does with `as_{type}`. - `to_{kind}_string`: conversion to different string formats (debug, abbreviated, etc.). Only two of the old string conversion functions were removed, the rest have been renamed only. - `to_{type}`: other conversion functions. Currently, only `to_path` exists. (And `to_string` through `Display`.) This table summaries the above: | Form | Cost | Input Ownership | Output Ownership | Converts `Value` case/`type` | | ---------------------------- | ----- | --------------- | ---------------- | -------- | | `as_{type}` | Cheap | Borrowed | Borrowed/Owned | No | | `into_{type}` | Cheap | Owned | Owned | No | | `coerce_{type}` | Cheap | Borrowed | Borrowed/Owned | Yes | | `coerce_into_{type}` | Cheap | Owned | Owned | Yes | | `to_{kind}_string` | Expensive | Borrowed | Owned | Yes | | `to_{type}` | Expensive | Borrowed | Owned | Yes | # User-Facing Changes Breaking API change for `Value` in `nu-protocol` which is exposed as part of the plugin API.
136 lines
4.6 KiB
Rust
136 lines
4.6 KiB
Rust
use eml_parser::eml::*;
|
|
use eml_parser::EmlParser;
|
|
use indexmap::map::IndexMap;
|
|
use nu_plugin::{EvaluatedCall, LabeledError};
|
|
use nu_protocol::{record, PluginExample, ShellError, Span, Value};
|
|
|
|
const DEFAULT_BODY_PREVIEW: usize = 50;
|
|
pub const CMD_NAME: &str = "from eml";
|
|
|
|
pub fn from_eml_call(call: &EvaluatedCall, input: &Value) -> Result<Value, LabeledError> {
|
|
let preview_body: usize = call
|
|
.get_flag::<i64>("preview-body")?
|
|
.map(|l| if l < 0 { 0 } else { l as usize })
|
|
.unwrap_or(DEFAULT_BODY_PREVIEW);
|
|
from_eml(input, preview_body, call.head)
|
|
}
|
|
|
|
pub fn examples() -> Vec<PluginExample> {
|
|
vec![
|
|
PluginExample {
|
|
description: "Convert eml structured data into record".into(),
|
|
example: "'From: test@email.com
|
|
Subject: Welcome
|
|
To: someone@somewhere.com
|
|
Test' | from eml"
|
|
.into(),
|
|
result: Some(Value::test_record(record! {
|
|
"Subject" => Value::test_string("Welcome"),
|
|
"From" => Value::test_record(record! {
|
|
"Name" => Value::nothing(Span::test_data()),
|
|
"Address" => Value::test_string("test@email.com"),
|
|
}),
|
|
"To" => Value::test_record(record! {
|
|
"Name" => Value::nothing(Span::test_data()),
|
|
"Address" => Value::test_string("someone@somewhere.com"),
|
|
}),
|
|
"Body" => Value::test_string("Test"),
|
|
})),
|
|
},
|
|
PluginExample {
|
|
description: "Convert eml structured data into record".into(),
|
|
example: "'From: test@email.com
|
|
Subject: Welcome
|
|
To: someone@somewhere.com
|
|
Test' | from eml -b 1"
|
|
.into(),
|
|
result: Some(Value::test_record(record! {
|
|
"Subject" => Value::test_string("Welcome"),
|
|
"From" => Value::test_record(record! {
|
|
"Name" => Value::nothing(Span::test_data()),
|
|
"Address" => Value::test_string("test@email.com"),
|
|
}),
|
|
"To" => Value::test_record(record! {
|
|
"Name" => Value::nothing(Span::test_data()),
|
|
"Address" => Value::test_string("someone@somewhere.com"),
|
|
}),
|
|
"Body" => Value::test_string("T"),
|
|
})),
|
|
},
|
|
]
|
|
}
|
|
|
|
fn emailaddress_to_value(span: Span, email_address: &EmailAddress) -> Value {
|
|
let (n, a) = match email_address {
|
|
EmailAddress::AddressOnly { address } => {
|
|
(Value::nothing(span), Value::string(address, span))
|
|
}
|
|
EmailAddress::NameAndEmailAddress { name, address } => {
|
|
(Value::string(name, span), Value::string(address, span))
|
|
}
|
|
};
|
|
|
|
Value::record(
|
|
record! {
|
|
"Name" => n,
|
|
"Address" => a,
|
|
},
|
|
span,
|
|
)
|
|
}
|
|
|
|
fn headerfieldvalue_to_value(head: Span, value: &HeaderFieldValue) -> Value {
|
|
use HeaderFieldValue::*;
|
|
|
|
match value {
|
|
SingleEmailAddress(address) => emailaddress_to_value(head, address),
|
|
MultipleEmailAddresses(addresses) => Value::list(
|
|
addresses
|
|
.iter()
|
|
.map(|a| emailaddress_to_value(head, a))
|
|
.collect(),
|
|
head,
|
|
),
|
|
Unstructured(s) => Value::string(s, head),
|
|
Empty => Value::nothing(head),
|
|
}
|
|
}
|
|
|
|
fn from_eml(input: &Value, body_preview: usize, head: Span) -> Result<Value, LabeledError> {
|
|
let value = input.coerce_string()?;
|
|
|
|
let eml = EmlParser::from_string(value)
|
|
.with_body_preview(body_preview)
|
|
.parse()
|
|
.map_err(|_| ShellError::CantConvert {
|
|
to_type: "structured eml data".into(),
|
|
from_type: "string".into(),
|
|
span: head,
|
|
help: None,
|
|
})?;
|
|
|
|
let mut collected = IndexMap::new();
|
|
|
|
if let Some(subj) = eml.subject {
|
|
collected.insert("Subject".to_string(), Value::string(subj, head));
|
|
}
|
|
|
|
if let Some(from) = eml.from {
|
|
collected.insert("From".to_string(), headerfieldvalue_to_value(head, &from));
|
|
}
|
|
|
|
if let Some(to) = eml.to {
|
|
collected.insert("To".to_string(), headerfieldvalue_to_value(head, &to));
|
|
}
|
|
|
|
for HeaderField { name, value } in &eml.headers {
|
|
collected.insert(name.to_string(), headerfieldvalue_to_value(head, value));
|
|
}
|
|
|
|
if let Some(body) = eml.body {
|
|
collected.insert("Body".to_string(), Value::string(body, head));
|
|
}
|
|
|
|
Ok(Value::record(collected.into_iter().collect(), head))
|
|
}
|