1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3
4use csv::StringRecord;
5use parse::CsvRow;
6use serde::de::DeserializeOwned;
7
8mod parse;
9
10pub struct CSVLine {
12 separator: char,
13}
14
15impl CSVLine {
16 pub fn new() -> Self {
18 Default::default()
19 }
20
21 pub fn with_separator(mut self, separator: char) -> Self {
23 self.separator = separator;
24 self
25 }
26
27 pub fn decode_str<T: DeserializeOwned>(&self, s: &str) -> Result<T, csv::Error> {
29 let record = StringRecord::from_iter(CsvRow::new(s, self.separator));
30 record.deserialize(None)
31 }
32}
33
34impl Default for CSVLine {
35 fn default() -> Self {
36 Self { separator: ',' }
37 }
38}
39
40pub fn from_str<T: DeserializeOwned>(s: &str) -> Result<T, csv::Error> {
42 CSVLine::new().decode_str(s)
43}
44
45pub fn from_str_sep<T: DeserializeOwned>(s: &str, sep: char) -> Result<T, csv::Error> {
61 CSVLine::new().with_separator(sep).decode_str(s)
62}
63
64#[cfg(test)]
65mod tests {
66 use serde::Deserialize;
67
68 use super::*;
69
70 #[test]
71 fn basic() {
72 #[derive(Debug, PartialEq, Deserialize)]
73 struct Foo(String);
74 assert_eq!(from_str::<Foo>("foo").unwrap(), Foo("foo".into()));
75 assert_eq!(from_str_sep::<Foo>("foo", ' ').unwrap(), Foo("foo".into()));
76 }
77
78 #[test]
79 fn empty() {
80 #[derive(Debug, PartialEq, Deserialize)]
81 struct Foo(Option<String>);
82 assert_eq!(from_str::<Foo>("").unwrap(), Foo(None));
83 assert_eq!(from_str_sep::<Foo>("", ' ').unwrap(), Foo(None));
84 }
85
86 #[test]
87 fn types() {
88 #[derive(Debug, PartialEq, Deserialize)]
89 struct Foo {
90 text: String,
91 maybe_text: Option<String>,
92 num: i32,
93 flag: bool,
94 }
95 assert_eq!(
96 from_str::<Foo>(r#""foo,bar",,1,true"#).unwrap(),
97 Foo {
98 text: "foo,bar".into(),
99 maybe_text: None,
100 num: 1,
101 flag: true
102 }
103 );
104 assert_eq!(
105 from_str_sep::<Foo>(r#""foo bar" 1 true"#, ' ').unwrap(),
106 Foo {
107 text: "foo bar".into(),
108 maybe_text: None,
109 num: 1,
110 flag: true
111 }
112 );
113 }
114
115 #[test]
116 fn tsv() {
117 #[derive(Debug, PartialEq, Deserialize)]
118 struct Foo(String, String);
119 assert_eq!(
120 CSVLine::new()
121 .with_separator('\t')
122 .decode_str::<Foo>("foo\tbar")
123 .unwrap(),
124 Foo("foo".into(), "bar".into())
125 );
126 }
127}