[READ-ONLY] Mirror of https://github.com/probablykasper/csv-pipeline.
0

Configure Feed

Select the types of activity you want to include in your feed.

Add map_col method

Kasper (Jan 9, 2023, 8:45 AM +0100) 405d828b 742aacbe

+93 -13
+4
src/headers.rs
··· 27 27 self.indexes.get(name).and_then(|index| row.get(*index)) 28 28 } 29 29 30 + pub fn get_index(&self, name: &str) -> Option<usize> { 31 + self.indexes.get(name).copied() 32 + } 33 + 30 34 pub fn get_row(&self) -> &Row { 31 35 &self.row 32 36 }
+9 -5
src/lib.rs
··· 13 13 Example, 14 14 Csv(csv::Error), 15 15 Io(std::io::Error), 16 + MissingColumn(String), 16 17 } 17 18 impl From<csv::Error> for Error { 18 19 fn from(error: csv::Error) -> Error { ··· 30 31 fn test_pipeline() { 31 32 let mut csv_str = String::new(); 32 33 let mut pipeline = PipelineBuilder::from_path("test/Countries.csv") 33 - .add_col("Language", |_headers, row| match row.get(1) { 34 - Some("Norway") => Ok("Norwegian".to_string()), 35 - _ => Ok("Unknown".to_string()), 34 + .add_col("Language", |headers, row| { 35 + match headers.get_field(row, "Country") { 36 + Some("Norway") => Ok("Norwegian".to_string()), 37 + _ => Ok("Unknown".to_string()), 38 + } 36 39 }) 40 + .map_col("Country", |id_str| Ok(id_str.to_uppercase())) 37 41 .flush(target::StringTarget::new(&mut csv_str)) 38 42 .build(); 39 43 ··· 45 49 assert_eq!( 46 50 csv_str, 47 51 "ID,Country,Language\n\ 48 - 1,Norway,Norwegian\n\ 49 - 2,Tuvalu,Unknown\n" 52 + 1,NORWAY,Norwegian\n\ 53 + 2,TUVALU,Unknown\n" 50 54 ); 51 55 }
+41 -4
src/pipeline.rs
··· 1 1 use super::headers::Headers; 2 - use crate::pipeline_iterators::{AddCol, Flush, Map}; 2 + use crate::pipeline_iterators::{AddCol, Flush, MapCol, MapRow}; 3 3 use crate::target::Target; 4 4 use crate::{Error, Row, RowResult}; 5 5 use csv::{Reader, ReaderBuilder, StringRecordsIntoIter}; ··· 62 62 self 63 63 } 64 64 65 - pub fn map<F>(mut self, get_value: F) -> Self 65 + /// Maps each row 66 + /// 67 + /// ## Example 68 + /// 69 + /// ``` 70 + /// use csv_pipeline::PipelineBuilder; 71 + /// 72 + /// PipelineBuilder::from_path("test/Countries.csv") 73 + /// .map(|headers, row| { 74 + /// Ok(row.into_iter().map(|field| field.to_uppercase()).collect()) 75 + /// }); 76 + /// ``` 77 + pub fn map<F>(mut self, get_row: F) -> Self 66 78 where 67 79 F: FnMut(&Headers, Row) -> Result<Row, Error> + 'a, 68 80 { 69 - self.iterator = Box::new(Map { 81 + self.iterator = Box::new(MapRow { 82 + iterator: self.iterator, 83 + f: get_row, 84 + headers: self.headers.clone(), 85 + }); 86 + self 87 + } 88 + 89 + /// Maps each field of a column 90 + /// 91 + /// ## Example 92 + /// 93 + /// ``` 94 + /// use csv_pipeline::PipelineBuilder; 95 + /// 96 + /// PipelineBuilder::from_path("test/Countries.csv") 97 + /// .map_col("Country", |field| { 98 + /// Ok(field.to_uppercase()) 99 + /// }); 100 + /// ``` 101 + pub fn map_col<F>(mut self, col: &str, get_value: F) -> Self 102 + where 103 + F: FnMut(&str) -> Result<String, Error> + 'a, 104 + { 105 + self.iterator = Box::new(MapCol { 70 106 iterator: self.iterator, 71 107 f: get_value, 72 - headers: self.headers.clone(), 108 + name: col.to_string(), 109 + index: self.headers.get_index(col), 73 110 }); 74 111 self 75 112 }
+39 -4
src/pipeline_iterators.rs
··· 15 15 type Item = RowResult; 16 16 17 17 fn next(&mut self) -> Option<Self::Item> { 18 - let row = match self.iterator.next()? { 18 + let mut row = match self.iterator.next()? { 19 19 Ok(row) => row, 20 20 Err(e) => return Some(Err(e)), 21 21 }; 22 22 match (self.f)(&self.headers, &row) { 23 23 Ok(value) => { 24 - let mut row = row; 25 24 row.push_field(&value); 26 25 Some(Ok(row)) 27 26 } ··· 30 29 } 31 30 } 32 31 33 - pub struct Map<I, F: FnMut(&Headers, Row) -> Result<Row, Error>> { 32 + pub struct MapRow<I, F: FnMut(&Headers, Row) -> Result<Row, Error>> { 34 33 pub iterator: I, 35 34 pub f: F, 36 35 pub headers: Headers, 37 36 } 38 - impl<I, F> Iterator for Map<I, F> 37 + impl<I, F> Iterator for MapRow<I, F> 39 38 where 40 39 I: Iterator<Item = RowResult>, 41 40 F: FnMut(&Headers, Row) -> Result<Row, Error>, ··· 51 50 Ok(value) => Some(Ok(value)), 52 51 Err(e) => Some(Err(e)), 53 52 } 53 + } 54 + } 55 + 56 + pub struct MapCol<I, F: FnMut(&str) -> Result<String, Error>> { 57 + pub iterator: I, 58 + pub f: F, 59 + pub name: String, 60 + pub index: Option<usize>, 61 + } 62 + impl<I, F> Iterator for MapCol<I, F> 63 + where 64 + I: Iterator<Item = RowResult>, 65 + F: FnMut(&str) -> Result<String, Error>, 66 + { 67 + type Item = RowResult; 68 + 69 + fn next(&mut self) -> Option<Self::Item> { 70 + let row = match self.iterator.next()? { 71 + Ok(row) => row, 72 + Err(e) => return Some(Err(e)), 73 + }; 74 + let mut row_vec: Vec<_> = row.into_iter().collect(); 75 + let index = match self.index { 76 + Some(index) => index, 77 + None => return Some(Err(Error::MissingColumn(self.name.clone()))), 78 + }; 79 + let field = match row_vec.get_mut(index) { 80 + Some(field) => field, 81 + None => return Some(Err(Error::MissingColumn(self.name.clone()))), 82 + }; 83 + let new_value = match (self.f)(field) { 84 + Ok(value) => value, 85 + Err(e) => return Some(Err(e)), 86 + }; 87 + *field = &new_value; 88 + Some(Ok(row_vec.into())) 54 89 } 55 90 } 56 91