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}; const DEFAULT_BODY_PREVIEW: usize = 50; pub const CMD_NAME: &str = "from eml"; pub fn from_eml_call(call: &EvaluatedCall, input: &Value) -> Result { let preview_body: usize = call .get_flag::("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 { 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::Record { cols: vec![ "Subject".to_string(), "From".to_string(), "To".to_string(), "Body".to_string(), ], vals: vec![ Value::test_string("Welcome"), Value::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 { 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(), example: "'From: test@email.com Subject: Welcome To: someone@somewhere.com Test' | from eml -b 1" .into(), result: Some(Value::Record { cols: vec![ "Subject".to_string(), "From".to_string(), "To".to_string(), "Body".to_string(), ], vals: vec![ Value::test_string("Welcome"), Value::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 { 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(), }), }, ] } fn emailaddress_to_value(span: Span, email_address: &EmailAddress) -> Value { let (n, a) = match email_address { EmailAddress::AddressOnly { address } => ( Value::nothing(span), Value::String { val: address.to_string(), span, }, ), EmailAddress::NameAndEmailAddress { name, address } => ( Value::String { val: name.to_string(), span, }, Value::String { val: address.to_string(), span, }, ), }; Value::Record { cols: vec!["Name".to_string(), "Address".to_string()], vals: vec![n, 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 { vals: addresses .iter() .map(|a| emailaddress_to_value(head, a)) .collect(), span: head, }, Unstructured(s) => Value::string(s, head), Empty => Value::nothing(head), } } fn from_eml(input: &Value, body_preview: usize, head: Span) -> Result { let value = input.as_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 { val: subj, span: 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 { val: body, span: head, }, ); } Ok(Value::from(Spanned { item: collected, span: head, })) }