forked from extern/nushell
from vcf
from ics
and from ini
(#367)
* MathEval Variance and Stddev * Fix tests and linting * Typo * Deal with streams when they are not tables * `from toml` command * From ods * From XLSX * From ics * From ini * From vcf * Forgot a eprintln!
This commit is contained in:
@ -230,21 +230,10 @@ fn from_eml(
|
||||
);
|
||||
}
|
||||
|
||||
let (cols, vals) = collected
|
||||
.into_iter()
|
||||
.fold((vec![], vec![]), |mut acc, (k, v)| {
|
||||
acc.0.push(k);
|
||||
acc.1.push(v);
|
||||
acc
|
||||
});
|
||||
|
||||
let record = Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
Ok(PipelineData::Value(Value::from(Spanned {
|
||||
item: collected,
|
||||
span: head,
|
||||
};
|
||||
|
||||
Ok(PipelineData::Value(record))
|
||||
})))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
327
crates/nu-command/src/formats/from/ics.rs
Normal file
327
crates/nu-command/src/formats/from/ics.rs
Normal file
@ -0,0 +1,327 @@
|
||||
extern crate ical;
|
||||
use ical::parser::ical::component::*;
|
||||
use ical::property::Property;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
|
||||
Spanned, Value,
|
||||
};
|
||||
use std::io::BufReader;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FromIcs;
|
||||
|
||||
impl Command for FromIcs {
|
||||
fn name(&self) -> &str {
|
||||
"from ics"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from ics").category(Category::Formats)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Parse text as .ics and create table."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let config = stack.get_config()?;
|
||||
from_ics(input, head, &config)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
example: "'BEGIN:VCALENDAR
|
||||
END:VCALENDAR' | from ics",
|
||||
description: "Converts ics formatted string to table",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec![
|
||||
"properties".to_string(),
|
||||
"events".to_string(),
|
||||
"alarms".to_string(),
|
||||
"to-Dos".to_string(),
|
||||
"journals".to_string(),
|
||||
"free-busys".to_string(),
|
||||
"timezones".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::List {
|
||||
vals: vec![],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::List {
|
||||
vals: vec![],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::List {
|
||||
vals: vec![],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::List {
|
||||
vals: vec![],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::List {
|
||||
vals: vec![],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::List {
|
||||
vals: vec![],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::List {
|
||||
vals: vec![],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn from_ics(input: PipelineData, head: Span, config: &Config) -> Result<PipelineData, ShellError> {
|
||||
let input_string = input.collect_string("", config);
|
||||
let input_bytes = input_string.as_bytes();
|
||||
let buf_reader = BufReader::new(input_bytes);
|
||||
let parser = ical::IcalParser::new(buf_reader);
|
||||
|
||||
let mut output = vec![];
|
||||
|
||||
for calendar in parser {
|
||||
match calendar {
|
||||
Ok(c) => output.push(calendar_to_value(c, head)),
|
||||
Err(_) => output.push(Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
"input cannot be parsed as .ics".to_string(),
|
||||
head,
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
Ok(Value::List {
|
||||
vals: output,
|
||||
span: head,
|
||||
}
|
||||
.into_pipeline_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 })
|
||||
}
|
||||
|
||||
fn events_to_value(events: Vec<IcalEvent>, span: Span) -> Value {
|
||||
Value::List {
|
||||
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 })
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn alarms_to_value(alarms: Vec<IcalAlarm>, span: Span) -> Value {
|
||||
Value::List {
|
||||
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 })
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn todos_to_value(todos: Vec<IcalTodo>, span: Span) -> Value {
|
||||
Value::List {
|
||||
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 })
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn journals_to_value(journals: Vec<IcalJournal>, span: Span) -> Value {
|
||||
Value::List {
|
||||
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 })
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn free_busys_to_value(free_busys: Vec<IcalFreeBusy>, span: Span) -> Value {
|
||||
Value::List {
|
||||
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 })
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn timezones_to_value(timezones: Vec<IcalTimeZone>, span: Span) -> Value {
|
||||
Value::List {
|
||||
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 })
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn timezone_transitions_to_value(transitions: Vec<IcalTimeZoneTransition>, span: Span) -> Value {
|
||||
Value::List {
|
||||
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 })
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn properties_to_value(properties: Vec<Property>, span: Span) -> Value {
|
||||
Value::List {
|
||||
vals: properties
|
||||
.into_iter()
|
||||
.map(|prop| {
|
||||
let mut row = IndexMap::new();
|
||||
|
||||
let name = Value::String {
|
||||
val: prop.name,
|
||||
span,
|
||||
};
|
||||
let value = match prop.value {
|
||||
Some(val) => Value::String { val, span },
|
||||
None => Value::nothing(span),
|
||||
};
|
||||
let params = match prop.params {
|
||||
Some(param_list) => params_to_value(param_list, span),
|
||||
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 })
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn params_to_value(params: Vec<(String, Vec<String>)>, span: Span) -> Value {
|
||||
let mut row = IndexMap::new();
|
||||
|
||||
for (param_name, param_values) in params {
|
||||
let values: Vec<Value> = param_values
|
||||
.into_iter()
|
||||
.map(|val| Value::string(val, span))
|
||||
.collect();
|
||||
let values = Value::List { vals: values, span };
|
||||
row.insert(param_name, values);
|
||||
}
|
||||
|
||||
Value::from(Spanned { item: row, span })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(FromIcs {})
|
||||
}
|
||||
}
|
109
crates/nu-command/src/formats/from/ini.rs
Normal file
109
crates/nu-command/src/formats/from/ini.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FromIni;
|
||||
|
||||
impl Command for FromIni {
|
||||
fn name(&self) -> &str {
|
||||
"from ini"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from ini").category(Category::Formats)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Parse text as .ini and create table"
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
example: "'[foo]
|
||||
a=1
|
||||
b=2' | from ini",
|
||||
description: "Converts ini formatted string to table",
|
||||
result: Some(Value::Record {
|
||||
cols: vec!["foo".to_string()],
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
val: "1".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::String {
|
||||
val: "2".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
}]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let config = stack.get_config()?;
|
||||
from_ini(input, head, &config)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_ini_string_to_value(s: String, span: Span) -> Result<Value, ShellError> {
|
||||
let v: Result<IndexMap<String, IndexMap<String, String>>, serde_ini::de::Error> =
|
||||
serde_ini::from_str(&s);
|
||||
match v {
|
||||
Ok(index_map) => {
|
||||
let (cols, vals) = index_map
|
||||
.into_iter()
|
||||
.fold((vec![], vec![]), |mut acc, (k, v)| {
|
||||
let (cols, vals) = v.into_iter().fold((vec![], vec![]), |mut acc, (k, v)| {
|
||||
acc.0.push(k);
|
||||
acc.1.push(Value::String { val: v, span });
|
||||
acc
|
||||
});
|
||||
acc.0.push(k);
|
||||
acc.1.push(Value::Record { cols, vals, span });
|
||||
acc
|
||||
});
|
||||
Ok(Value::Record { cols, vals, span })
|
||||
}
|
||||
Err(err) => Err(ShellError::UnsupportedInput(
|
||||
format!("Could not load ini: {}", err),
|
||||
span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_ini(input: PipelineData, head: Span, config: &Config) -> Result<PipelineData, ShellError> {
|
||||
let concat_string = input.collect_string("", config);
|
||||
|
||||
match from_ini_string_to_value(concat_string, head) {
|
||||
Ok(x) => Ok(x.into_pipeline_data()),
|
||||
Err(other) => Err(other),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(FromIni {})
|
||||
}
|
||||
}
|
@ -2,11 +2,14 @@ mod command;
|
||||
mod csv;
|
||||
mod delimited;
|
||||
mod eml;
|
||||
mod ics;
|
||||
mod ini;
|
||||
mod json;
|
||||
mod ods;
|
||||
mod toml;
|
||||
mod tsv;
|
||||
mod url;
|
||||
mod vcf;
|
||||
mod xlsx;
|
||||
mod yaml;
|
||||
|
||||
@ -14,10 +17,13 @@ pub use self::csv::FromCsv;
|
||||
pub use self::toml::FromToml;
|
||||
pub use command::From;
|
||||
pub use eml::FromEml;
|
||||
pub use ics::FromIcs;
|
||||
pub use ini::FromIni;
|
||||
pub use json::FromJson;
|
||||
pub use ods::FromOds;
|
||||
pub use tsv::FromTsv;
|
||||
pub use url::FromUrl;
|
||||
pub use vcf::FromVcf;
|
||||
pub use xlsx::FromXlsx;
|
||||
pub use yaml::FromYaml;
|
||||
pub use yaml::FromYml;
|
||||
|
211
crates/nu-command/src/formats/from/vcf.rs
Normal file
211
crates/nu-command/src/formats/from/vcf.rs
Normal file
@ -0,0 +1,211 @@
|
||||
use ical::parser::vcard::component::*;
|
||||
use ical::property::Property;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
|
||||
Spanned, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FromVcf;
|
||||
|
||||
impl Command for FromVcf {
|
||||
fn name(&self) -> &str {
|
||||
"from vcf"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from vcf").category(Category::Formats)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Parse text as .vcf and create table."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let config = stack.get_config()?;
|
||||
from_vcf(input, head, &config)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
example: "'BEGIN:VCARD
|
||||
N:Foo
|
||||
FN:Bar
|
||||
EMAIL:foo@bar.com
|
||||
END:VCARD' | from vcf",
|
||||
description: "Converts ics formatted string to table",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["properties".to_string()],
|
||||
vals: vec![Value::List {
|
||||
vals: vec![
|
||||
Value::Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"value".to_string(),
|
||||
"params".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
val: "N".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::String {
|
||||
val: "Foo".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::Nothing {
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"value".to_string(),
|
||||
"params".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
val: "FN".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::String {
|
||||
val: "Bar".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::Nothing {
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::Record {
|
||||
cols: vec![
|
||||
"name".to_string(),
|
||||
"value".to_string(),
|
||||
"params".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::String {
|
||||
val: "EMAIL".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::String {
|
||||
val: "foo@bar.com".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
Value::Nothing {
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn from_vcf(input: PipelineData, head: Span, config: &Config) -> Result<PipelineData, ShellError> {
|
||||
let input_string = input.collect_string("", config);
|
||||
let input_bytes = input_string.as_bytes();
|
||||
let cursor = std::io::Cursor::new(input_bytes);
|
||||
let parser = ical::VcardParser::new(cursor);
|
||||
|
||||
let iter = parser.map(move |contact| match contact {
|
||||
Ok(c) => contact_to_value(c, head),
|
||||
Err(_) => Value::Error {
|
||||
error: ShellError::UnsupportedInput("input cannot be parsed as .vcf".to_string(), head),
|
||||
},
|
||||
});
|
||||
|
||||
let collected: Vec<_> = iter.collect();
|
||||
Ok(Value::List {
|
||||
vals: collected,
|
||||
span: head,
|
||||
}
|
||||
.into_pipeline_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 })
|
||||
}
|
||||
|
||||
fn properties_to_value(properties: Vec<Property>, span: Span) -> Value {
|
||||
Value::List {
|
||||
vals: properties
|
||||
.into_iter()
|
||||
.map(|prop| {
|
||||
let mut row = IndexMap::new();
|
||||
|
||||
let name = Value::String {
|
||||
val: prop.name,
|
||||
span,
|
||||
};
|
||||
let value = match prop.value {
|
||||
Some(val) => Value::String { val, span },
|
||||
None => Value::Nothing { span },
|
||||
};
|
||||
let params = match prop.params {
|
||||
Some(param_list) => params_to_value(param_list, span),
|
||||
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 })
|
||||
})
|
||||
.collect::<Vec<Value>>(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn params_to_value(params: Vec<(String, Vec<String>)>, span: Span) -> Value {
|
||||
let mut row = IndexMap::new();
|
||||
|
||||
for (param_name, param_values) in params {
|
||||
let values: Vec<Value> = param_values
|
||||
.into_iter()
|
||||
.map(|val| Value::string(val, span))
|
||||
.collect();
|
||||
let values = Value::List { vals: values, span };
|
||||
row.insert(param_name, values);
|
||||
}
|
||||
|
||||
Value::from(Spanned { item: row, span })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(FromVcf {})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user