1use alloc::string::String;
4use alloc::vec::Vec;
5
6use crate::error::{CsvDecodeError, CsvFromStrError};
7use crate::limits::CsvLimits;
8use crate::reader::read_str_with_limits;
9use crate::writer::CsvWriter;
10
11pub trait CsvEncode {
17 fn header() -> Vec<&'static str>;
19
20 fn encode_fields(&self, out: &mut Vec<String>);
22}
23
24pub trait CsvDecode: Sized {
26 fn decode_fields(fields: &[&str]) -> Result<Self, CsvDecodeError>;
31}
32
33pub fn to_csv_string<T: CsvEncode>(records: &[T]) -> String {
38 let mut writer = CsvWriter::new();
39 writer.write_record(T::header());
40 let mut fields: Vec<String> = Vec::new();
41 for record in records {
42 fields.clear();
43 record.encode_fields(&mut fields);
44 writer.write_record(&fields);
45 }
46 writer.into_string()
47}
48
49pub fn to_csv_string_headerless<T: CsvEncode>(records: &[T]) -> String {
51 let mut writer = CsvWriter::new();
52 let mut fields: Vec<String> = Vec::new();
53 for record in records {
54 fields.clear();
55 record.encode_fields(&mut fields);
56 writer.write_record(&fields);
57 }
58 writer.into_string()
59}
60
61pub fn from_csv_str<T: CsvEncode + CsvDecode>(input: &str) -> Result<Vec<T>, CsvFromStrError> {
70 from_csv_str_with_limits(input, &CsvLimits::conservative())
71}
72
73pub fn from_csv_str_with_limits<T: CsvEncode + CsvDecode>(
75 input: &str,
76 limits: &CsvLimits,
77) -> Result<Vec<T>, CsvFromStrError> {
78 let records = read_str_with_limits(input, limits)?;
79 let mut rows = records.into_iter();
80
81 match rows.next() {
82 None => Ok(Vec::new()),
83 Some(header) => {
84 let expected = T::header();
85 if header.len() != expected.len()
86 || header.iter().zip(expected.iter()).any(|(a, b)| a != b)
87 {
88 return Err(CsvDecodeError::header_mismatch().at_record(0).into());
89 }
90 decode_rows::<T>(rows, 1)
91 }
92 }
93}
94
95pub fn from_csv_str_headerless<T: CsvDecode>(input: &str) -> Result<Vec<T>, CsvFromStrError> {
97 from_csv_str_headerless_with_limits(input, &CsvLimits::conservative())
98}
99
100pub fn from_csv_str_headerless_with_limits<T: CsvDecode>(
102 input: &str,
103 limits: &CsvLimits,
104) -> Result<Vec<T>, CsvFromStrError> {
105 let records = read_str_with_limits(input, limits)?;
106 decode_rows::<T>(records.into_iter(), 0)
107}
108
109fn decode_rows<T: CsvDecode>(
112 rows: impl Iterator<Item = Vec<String>>,
113 base_record: usize,
114) -> Result<Vec<T>, CsvFromStrError> {
115 let mut out = Vec::new();
116 for (offset, record) in rows.enumerate() {
117 let refs: Vec<&str> = record.iter().map(String::as_str).collect();
118 let value =
119 T::decode_fields(&refs).map_err(|error| error.at_record(base_record + offset))?;
120 out.push(value);
121 }
122 Ok(out)
123}