#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
use csv::StringRecord;
use parse::CsvRow;
use serde::de::DeserializeOwned;
mod parse;
pub struct CSVLine {
separator: char,
}
impl CSVLine {
pub fn new() -> Self {
Default::default()
}
pub fn with_separator(mut self, separator: char) -> Self {
self.separator = separator;
self
}
pub fn decode_str<T: DeserializeOwned>(&self, s: &str) -> Result<T, csv::Error> {
let record = StringRecord::from_iter(CsvRow::new(s, self.separator));
record.deserialize(None)
}
}
impl Default for CSVLine {
fn default() -> Self {
Self { separator: ',' }
}
}
pub fn from_str<T: DeserializeOwned>(s: &str) -> Result<T, csv::Error> {
CSVLine::new().decode_str(s)
}
pub fn from_str_sep<T: DeserializeOwned>(s: &str, sep: char) -> Result<T, csv::Error> {
CSVLine::new().with_separator(sep).decode_str(s)
}
#[cfg(test)]
mod tests {
use serde::Deserialize;
use super::*;
#[test]
fn basic() {
#[derive(Debug, PartialEq, Deserialize)]
struct Foo(String);
assert_eq!(from_str::<Foo>("foo").unwrap(), Foo("foo".into()));
assert_eq!(from_str_sep::<Foo>("foo", ' ').unwrap(), Foo("foo".into()));
}
#[test]
fn empty() {
#[derive(Debug, PartialEq, Deserialize)]
struct Foo(Option<String>);
assert_eq!(from_str::<Foo>("").unwrap(), Foo(None));
assert_eq!(from_str_sep::<Foo>("", ' ').unwrap(), Foo(None));
}
#[test]
fn types() {
#[derive(Debug, PartialEq, Deserialize)]
struct Foo {
text: String,
maybe_text: Option<String>,
num: i32,
flag: bool,
}
assert_eq!(
from_str::<Foo>(r#""foo,bar",,1,true"#).unwrap(),
Foo {
text: "foo,bar".into(),
maybe_text: None,
num: 1,
flag: true
}
);
assert_eq!(
from_str_sep::<Foo>(r#""foo bar" 1 true"#, ' ').unwrap(),
Foo {
text: "foo bar".into(),
maybe_text: None,
num: 1,
flag: true
}
);
}
#[test]
fn tsv() {
#[derive(Debug, PartialEq, Deserialize)]
struct Foo(String, String);
assert_eq!(
CSVLine::new()
.with_separator('\t')
.decode_str::<Foo>("foo\tbar")
.unwrap(),
Foo("foo".into(), "bar".into())
);
}
}