Решение на CSV Filter от Ивайло Иванов

Обратно към всички решения

Към профила на Ивайло Иванов

Резултати

  • 8 точки от тестове
  • 0 бонус точки
  • 8 точки общо
  • 8 успешни тест(а)
  • 7 неуспешни тест(а)

Код

use std::collections::HashMap;
use std::io::BufRead;
use std::io::Write;
use std::io::Error;
pub fn skip_next(input: &str, target: char) -> Option<&str> {
let mut chars = input.chars();
if chars.next().unwrap() == target {
Some(chars.as_str())
}
else {
None
}
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let split_at_i = input.find(target);
match split_at_i {
Some(i) => input.split_at(i),
None => (input, "")
}
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let split_at_i = input.find(target);
match split_at_i {
Some(i) => {
let (first, second) = input.split_at(i);
Some((first, &second[1..]))
},
None => None
}
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
impl From<Error> for CsvError {
fn from(e: Error) -> Self {
CsvError::IO(e)
}
}
type Row = HashMap<String, String>;
pub struct Csv<R: BufRead> {
pub columns: Vec<String>,
reader: R,
selection: Option<Box<dyn Fn(&Row) -> Result<bool, CsvError>>>,
}
impl<R: BufRead> Csv<R> {
pub fn new(mut reader: R) -> Result<Self, CsvError> {
let mut buf = String::new();
let num_bytes = reader.read_line(&mut buf)?;
if num_bytes == 0 {
return Err(CsvError::InvalidHeader("Header can't be 0 bytes!".to_string()))
}
let mut columns: Vec<String> = Vec::new();
loop{
let field = take_and_skip(&buf, ',');
if field == None {
columns.push(buf.to_string());
break;
}
let (first, second) = field.unwrap();
columns.push(first.to_string());
if second.len() != 0 {
// buf = match skip_next(second, ' '){
// Some(rest) => rest.to_string(),
// None => second.to_string()
// };
buf = second.trim().to_string();
}
else {
break;
}
}
Ok(Csv{
columns: columns,
reader: reader,
selection: None
})
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let mut line = line.trim();
line = match skip_next(line, '"') {
Some(line) => line,
None => return Err(CsvError::InvalidRow("Rows must begin with \"".to_string()))
};
let mut row = Row::new();
let mut col_iter = 0;
loop {
let (field, rest) = match take_and_skip(line, '"') {
Some(result) => result,
None => return Err(CsvError::InvalidRow("Fields in rows must end with \"".to_string()))
};
line = rest;
row.insert(String::from(self.columns[col_iter].clone()), String::from(field));
col_iter += 1;
line = match take_and_skip(line, '"') {
Some((comma, line)) => {
let comma = comma.trim();
if comma.len() != 1 || comma.chars().nth(0).unwrap() != ',' {
return Err(CsvError::InvalidRow("Fields must be separated by \",\"".to_string()))
}
line
},
None => {
if col_iter != self.columns.len() {
return Err(CsvError::InvalidRow("The number of fields in the rows must match the number of columns".to_string()))
}
break;
}
};
if col_iter >=self.columns.len(){
return Err(CsvError::InvalidRow("Rows must not contain more elements than columns!".to_string()));
}
}
if col_iter < self.columns.len() - 1 {
return Err(CsvError::InvalidRow("Rows must not contain less elements than columns!".to_string()));
}
Ok(row)
}
pub fn apply_selection<F>(&mut self, callback: F)
where F: Fn(&Row) -> Result<bool, CsvError> + 'static
{
self.selection = Some(Box::<_>::new(callback));
}
pub fn write_to<W: Write>(mut self, mut writer: W) -> Result<(), CsvError> {
let mut row = String::new();
let mut is_start = true;
for col in &self.columns {
if is_start == false {
row += ", "
}
row += col;
is_start = false;
}
row += "\n";
writer.write(row.as_bytes())?;
loop {
row = String::new();
let line = match self.next() {
Some(Ok(result)) => result,
Some(Err(e)) => return Err(e),
None => return Ok(())
};
let mut is_start = true;
for key in &self.columns {
if is_start == false {
row += ", ";
}
row += "\"";
row += line.get(key).unwrap();
row += "\"";
is_start = false;
}
row += "\n";
writer.write(row.as_bytes())?;
}
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
let mut buf = String::new();
let num_bytes = match self.reader.read_line(&mut buf) {
Ok(n) => n,
Err(e) => return Some(Err(CsvError::IO(e)))
};
if num_bytes == 0 {
return None
}
let row = match self.parse_line(&buf) {
Ok(row) => row,
Err(e) => return Some(Err(e))
};
// println!("\nrow={:?}", row);
let selection = match &self.selection {
Some(func) => func,
None => return None
};
// println!("selection={:?}", (selection)(&row));
match (selection)(&row) {
Ok(true) => Some(Ok(row)),
Ok(false) => self.next(),
Err(e) => Some(Err(e))
}
}
}
#[cfg(test)]
mod tests {
use crate::skip_next;
use crate::take_until;
use crate::take_and_skip;
use crate::Csv;
// use crate::CsvError;
use std::io::BufReader;
#[test]
fn skip_next_test() {
let input = "[test]";
let target = '[';
println!("before: {:?}, {:?}", input, target);
assert_eq!(skip_next(input, target), Some("test]"));
println!("after: {:?}, {:?}", input, target);
assert_eq!(skip_next("test]", '['), None);
assert_eq!(skip_next("t[est]", '['), None);
}
#[test]
fn take_until_test() {
assert_eq!(take_until("one/two", '/'), ("one", "/two"));
}
#[test]
fn test_string_parsing() {
assert_eq!(skip_next("[test]", '['), Some("test]"));
assert_eq!(skip_next("test]", '['), None);
assert_eq!(skip_next("t[est]", '['), None);
assert_eq!(take_until("one/two", '/'), ("one", "/two"));
assert_eq!(take_until("onetwo/", '/'), ("onetwo", "/"));
assert_eq!(take_until("/onetwo", '/'), ("", "/onetwo"));
assert_eq!(take_and_skip("one/two", '/'), Some(("one", "two")));
assert_eq!(take_and_skip("onetwo/", '/'), Some(("onetwo", "")));
assert_eq!(take_and_skip("/onetwo", '/'), Some(("", "onetwo")));
assert_eq!(take_and_skip("onetwo", '/'), None);
}
#[test]
fn csv_object_test_selection() {
let reader = BufReader::new(r#"
name, age, birth date
"Douglas Adams", "42", "1952-03-11"
"Gen Z. Person", "20", "2000-01-01"
"Ada Lovelace", "36", "1815-12-10"
"#.trim().as_bytes());
let mut csv = Csv::new(reader).unwrap();
csv.apply_selection(|row| {
Ok(row["age"].parse::<u32>().unwrap() < 40)
});
// Итерираме през резултата:
// while let Some(row) = csv.next() {
// println!("{:?}", row.unwrap().get("name"));
// // => Some("Douglas Adams")
// // => Some("Ada Lovelace")
// }
match csv.next(){
Some(row) => assert_eq!(row.unwrap().get("name").unwrap(), "Gen Z. Person"),
None => panic!()
}
match csv.next(){
Some(row) => assert_eq!(row.unwrap().get("name").unwrap(), "Ada Lovelace"),
None => panic!()
}
}
#[test]
fn csv_object_test_write_to() {
let reader = BufReader::new(r#"
name, age, birth date
"Douglas Adams", "42", "1952-03-11"
"Gen Z. Person", "20", "2000-01-01"
"Ada Lovelace", "36", "1815-12-10"
"#.trim().as_bytes());
let mut csv = Csv::new(reader).unwrap();
csv.apply_selection(|row| {
Ok(row["age"].parse::<u32>().unwrap() < 40)
});
let mut output = Vec::new();
csv.write_to(&mut output).unwrap();
// println!("{}", String::from_utf8(output).unwrap());
let desired_output = "name, age, birth date
\"Gen Z. Person\", \"20\", \"2000-01-01\"
\"Ada Lovelace\", \"36\", \"1815-12-10\"\n".to_string();
assert_eq!(String::from_utf8(output).unwrap(), desired_output);
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20210111-1538662-xrzlb9/solution)
    Finished test [unoptimized + debuginfo] target(s) in 4.09s
     Running target/debug/deps/solution_test-8916805fc40a2dab

running 15 tests
test solution_test::test_csv_basic ... FAILED
test solution_test::test_csv_duplicate_columns ... FAILED
test solution_test::test_csv_empty ... ok
test solution_test::test_csv_iterating_with_a_selection ... ok
test solution_test::test_csv_iterating_with_no_selection ... FAILED
test solution_test::test_csv_parse_line ... ok
test solution_test::test_csv_parse_line_with_commas ... ok
test solution_test::test_csv_selection_and_writing ... FAILED
test solution_test::test_csv_single_column_no_data ... ok
test solution_test::test_csv_writing_without_a_selection ... FAILED
test solution_test::test_csv_writing_without_any_rows ... ok
test solution_test::test_parsing_helpers_for_unicode ... FAILED
test solution_test::test_skip_next ... FAILED
test solution_test::test_take_and_skip ... ok
test solution_test::test_take_until ... ok

failures:

---- solution_test::test_csv_basic stdout ----
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:70:30
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:60:5

---- solution_test::test_csv_duplicate_columns stdout ----
thread 'main' panicked at 'Expression None does not match the pattern "Some(CsvError::InvalidHeader(_))"', tests/solution_test.rs:92:5

---- solution_test::test_csv_iterating_with_no_selection stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `[]`,
 right: `["Douglas Adams", "Gen Z. Person", "Ada Lovelace"]`', tests/solution_test.rs:196:9
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `[]`,
 right: `["Douglas Adams", "Gen Z. Person", "Ada Lovelace"]`', tests/solution_test.rs:185:5

---- solution_test::test_csv_selection_and_writing stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `["name, age    , birth date", "\"Gen Z. Person\", \"20\", \"2000-01-01\""]`,
 right: `["name, age, birth date", "\"Gen Z. Person\", \"20\", \"2000-01-01\""]`', tests/solution_test.rs:269:9
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `["name, age    , birth date", "\"Gen Z. Person\", \"20\", \"2000-01-01\""]`,
 right: `["name, age, birth date", "\"Gen Z. Person\", \"20\", \"2000-01-01\""]`', tests/solution_test.rs:251:5

---- solution_test::test_csv_writing_without_a_selection stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `["name, age    , birth date"]`,
 right: `["name, age, birth date", "\"Douglas Adams\", \"42\", \"1952-03-11\"", "\"Gen Z. Person\", \"20\", \"2000-01-01\"", "\"Ada Lovelace\", \"36\", \"1815-12-10\""]`', tests/solution_test.rs:240:9
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `["name, age    , birth date"]`,
 right: `["name, age, birth date", "\"Douglas Adams\", \"42\", \"1952-03-11\"", "\"Gen Z. Person\", \"20\", \"2000-01-01\"", "\"Ada Lovelace\", \"36\", \"1815-12-10\""]`', tests/solution_test.rs:224:5

---- solution_test::test_parsing_helpers_for_unicode stdout ----
thread 'main' panicked at 'byte index 1 is not a char boundary; it is inside '↓' (bytes 0..3) of `↓яга`', src/lib.rs:30:27

---- solution_test::test_skip_next stdout ----
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:9:21


failures:
    solution_test::test_csv_basic
    solution_test::test_csv_duplicate_columns
    solution_test::test_csv_iterating_with_no_selection
    solution_test::test_csv_selection_and_writing
    solution_test::test_csv_writing_without_a_selection
    solution_test::test_parsing_helpers_for_unicode
    solution_test::test_skip_next

test result: FAILED. 8 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

История (2 версии и 0 коментара)

Ивайло качи първо решение на 11.01.2021 12:15 (преди 6 месеца)

Ивайло качи решение на 11.01.2021 12:15 (преди 6 месеца)

use std::collections::HashMap;
use std::io::BufRead;
use std::io::Write;
use std::io::Error;
pub fn skip_next(input: &str, target: char) -> Option<&str> {
let mut chars = input.chars();
if chars.next().unwrap() == target {
Some(chars.as_str())
}
else {
None
}
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let split_at_i = input.find(target);
match split_at_i {
Some(i) => input.split_at(i),
None => (input, "")
}
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let split_at_i = input.find(target);
match split_at_i {
Some(i) => {
let (first, second) = input.split_at(i);
Some((first, &second[1..]))
},
None => None
}
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
impl From<Error> for CsvError {
fn from(e: Error) -> Self {
CsvError::IO(e)
}
}
type Row = HashMap<String, String>;
pub struct Csv<R: BufRead> {
pub columns: Vec<String>,
reader: R,
selection: Option<Box<dyn Fn(&Row) -> Result<bool, CsvError>>>,
}
impl<R: BufRead> Csv<R> {
- /// Конструира нова стойност от подадения вход. Третира се като "нещо, от което може да се чете
- /// ред по ред".
- ///
- /// Очакваме да прочетете първия ред от входа и да го обработите като заглавна част ("header").
- /// Това означава, че първия ред би трябвало да включва имена на колони, разделени със
- /// запетайки и може би празни места. Примерно:
- ///
- /// - name, age
- /// - name,age,birth date
- ///
- /// В случай, че има грешка от викане на методи на `reader`, тя би трябвало да е `io::Error`.
- /// върнете `CsvError::IO`, което опакова въпросната грешка.
- ///
- /// Ако първия ред е празен, прочитането ще ви върне 0 байта. Примерно, `read_line` връща
- /// `Ok(0)` в такъв случай. Това означава, че нямаме валиден header -- нито една колона няма,
- /// очакваме грешка `CsvError::InvalidHeader`.
- ///
- /// Ако има дублиране на колони -- две колони с едно и също име -- също върнете
- /// `CsvError::InvalidHeader`.
- ///
- /// Ако всичко е наред, върнете конструирана стойност, на която `columns` е списък с колоните,
- /// в същия ред, в който са подадени, без заобикалящите ги празни символи (използвайте
- /// `.trim()`).
- ///
pub fn new(mut reader: R) -> Result<Self, CsvError> {
let mut buf = String::new();
let num_bytes = reader.read_line(&mut buf)?;
if num_bytes == 0 {
return Err(CsvError::InvalidHeader("Header can't be 0 bytes!".to_string()))
}
let mut columns: Vec<String> = Vec::new();
loop{
let field = take_and_skip(&buf, ',');
if field == None {
columns.push(buf.to_string());
break;
}
let (first, second) = field.unwrap();
columns.push(first.to_string());
if second.len() != 0 {
// buf = match skip_next(second, ' '){
// Some(rest) => rest.to_string(),
// None => second.to_string()
// };
buf = second.trim().to_string();
}
else {
break;
}
}
Ok(Csv{
columns: columns,
reader: reader,
selection: None
})
}
- /// Функцията приема следващия ред за обработка и конструира `Row` стойност
- /// (`HashMap<String, String>`) със колоните и съответсващите им стойности на този ред.
- ///
- /// Алгоритъма е горе-долу:
- ///
- /// 1. Изчистете реда с `.trim()`.
- /// 2. Очаквате, че реда ще започне със `"`, иначе връщате грешка.
- /// 3. Прочитате съдържанието от отварящата кавичка до следващата. Това е съдържанието на
- /// стойността на текущата колона на този ред. Не го чистите от whitespace, просто го
- /// приемате както е.
- /// 4. Ако не намерите затваряща кавичка, това е грешка.
- /// 5. Запазвате си стойността в един `Row` (`HashMap`) -- ключа е името на текущата колона,
- /// до която сте стигнали, стойността е това, което току-що изпарсихте.
- /// 6. Ако нямате оставащи колони за обработка и нямате оставащо съдържание от реда, всичко
- /// е ок. Връщате реда.
- /// 7. Ако нямате оставащи колони, но имате още от реда, или обратното, това е грешка.
- ///
- /// За този процес, помощните функции, които дефинирахме по-горе може да ви свършат работа.
- /// *Може* да използвате вместо тях `.split` по запетайки, но ще имаме поне няколко теста със
- /// вложени запетайки. Бихте могли и с това да се справите иначе, разбира се -- ваш избор.
- ///
- /// Внимавайте с празното пространство преди и след запетайки -- викайте `.trim()` на ключови
- /// места. Всичко в кавички се взема както е, всичко извън тях се чисти от whitespace.
- ///
- /// Всички грешки, които ще връщате, се очаква да бъдат `CsvError::InvalidRow`.
- ///
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let mut line = line.trim();
line = match skip_next(line, '"') {
Some(line) => line,
None => return Err(CsvError::InvalidRow("Rows must begin with \"".to_string()))
};
let mut row = Row::new();
let mut col_iter = 0;
loop {
let (field, rest) = match take_and_skip(line, '"') {
Some(result) => result,
None => return Err(CsvError::InvalidRow("Fields in rows must end with \"".to_string()))
};
line = rest;
row.insert(String::from(self.columns[col_iter].clone()), String::from(field));
col_iter += 1;
line = match take_and_skip(line, '"') {
Some((comma, line)) => {
let comma = comma.trim();
if comma.len() != 1 || comma.chars().nth(0).unwrap() != ',' {
return Err(CsvError::InvalidRow("Fields must be separated by \",\"".to_string()))
}
line
},
None => {
if col_iter != self.columns.len() {
return Err(CsvError::InvalidRow("The number of fields in the rows must match the number of columns".to_string()))
}
break;
}
};
if col_iter >=self.columns.len(){
return Err(CsvError::InvalidRow("Rows must not contain more elements than columns!".to_string()));
}
}
if col_iter < self.columns.len() - 1 {
return Err(CsvError::InvalidRow("Rows must not contain less elements than columns!".to_string()));
}
Ok(row)
}
- /// Подадената функция, "callback", се очаква да се запази и да се използва по-късно за
- /// филтриране -- при итерация, само редове, за които се връща `true` се очаква да се извадят.
- ///
- /// Би трябвало `callback` да се вика от `.next()` и от `.write_to()`, вижте описанията на тези
- /// методи за детайли.
- ///
pub fn apply_selection<F>(&mut self, callback: F)
where F: Fn(&Row) -> Result<bool, CsvError> + 'static
{
self.selection = Some(Box::<_>::new(callback));
}
- /// Извикването на този метод консумира CSV-то и записва филтрираното съдържание в подадената
- /// `Write` стойност. Вижте по-долу за пример и детайли.
- ///
- /// Грешките, които се връщат са грешките, които идват от използваните други методи, плюс
- /// грешките от писане във `writer`-а, опаковани в `CsvError::IO`.
- ///
- /// В зависимост от това как си имплементирате метода, `mut` може би няма да ви трябва за
- /// `self` -- ако имате warning-и, просто го махнете.
- ///
pub fn write_to<W: Write>(mut self, mut writer: W) -> Result<(), CsvError> {
let mut row = String::new();
let mut is_start = true;
for col in &self.columns {
if is_start == false {
row += ", "
}
row += col;
is_start = false;
}
row += "\n";
writer.write(row.as_bytes())?;
loop {
row = String::new();
let line = match self.next() {
Some(Ok(result)) => result,
Some(Err(e)) => return Err(e),
None => return Ok(())
};
let mut is_start = true;
for key in &self.columns {
if is_start == false {
row += ", ";
}
row += "\"";
row += line.get(key).unwrap();
row += "\"";
is_start = false;
}
row += "\n";
writer.write(row.as_bytes())?;
}
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
- /// Итерацията се състои от няколко стъпки:
- ///
- /// 1. Прочитаме следващия ред от входа:
- /// -> Ако има грешка при четене, връщаме Some(CsvError::IO(...))
- /// -> Ако успешно се прочетат 0 байта, значи сме на края на входа, и няма какво повече да
- /// четем -- връщаме `None`
- /// -> Иначе, имаме успешно прочетен ред, продължаваме напред
- /// 2. Опитваме се да обработим прочетения ред със `parse_line`:
- /// -> Ако има грешка при парсене, връщаме Some(CsvError-а, който се връща от `parse_line`)
- /// -> Ако успешно извикаме `parse_line`, вече имаме `Row` стойност.
- /// 3. Проверяваме дали този ред изпълнява условието, запазено от `apply_selection`:
- /// -> Ако условието върне грешка, връщаме тази грешка опакована във `Some`.
- /// -> Ако условието върне Ok(false), *не* връщаме този ред, а пробваме следващия (обратно
- /// към стъпка 1)
- /// -> При Ok(true), връщаме този ред, опакован във `Some`
- ///
- /// Да, тази функция връща `Option<Result<...>>` :). `Option` защото може да има, може да няма
- /// следващ ред, `Result` защото четенето на реда (от примерно файл) може да не сработи.
- ///
fn next(&mut self) -> Option<Self::Item> {
let mut buf = String::new();
let num_bytes = match self.reader.read_line(&mut buf) {
Ok(n) => n,
Err(e) => return Some(Err(CsvError::IO(e)))
};
if num_bytes == 0 {
return None
}
let row = match self.parse_line(&buf) {
Ok(row) => row,
Err(e) => return Some(Err(e))
};
// println!("\nrow={:?}", row);
let selection = match &self.selection {
Some(func) => func,
None => return None
};
// println!("selection={:?}", (selection)(&row));
match (selection)(&row) {
Ok(true) => Some(Ok(row)),
Ok(false) => self.next(),
Err(e) => Some(Err(e))
}
}
}
#[cfg(test)]
mod tests {
use crate::skip_next;
use crate::take_until;
use crate::take_and_skip;
use crate::Csv;
// use crate::CsvError;
use std::io::BufReader;
#[test]
fn skip_next_test() {
let input = "[test]";
let target = '[';
println!("before: {:?}, {:?}", input, target);
assert_eq!(skip_next(input, target), Some("test]"));
println!("after: {:?}, {:?}", input, target);
assert_eq!(skip_next("test]", '['), None);
assert_eq!(skip_next("t[est]", '['), None);
}
#[test]
fn take_until_test() {
assert_eq!(take_until("one/two", '/'), ("one", "/two"));
}
#[test]
fn test_string_parsing() {
assert_eq!(skip_next("[test]", '['), Some("test]"));
assert_eq!(skip_next("test]", '['), None);
assert_eq!(skip_next("t[est]", '['), None);
assert_eq!(take_until("one/two", '/'), ("one", "/two"));
assert_eq!(take_until("onetwo/", '/'), ("onetwo", "/"));
assert_eq!(take_until("/onetwo", '/'), ("", "/onetwo"));
assert_eq!(take_and_skip("one/two", '/'), Some(("one", "two")));
assert_eq!(take_and_skip("onetwo/", '/'), Some(("onetwo", "")));
assert_eq!(take_and_skip("/onetwo", '/'), Some(("", "onetwo")));
assert_eq!(take_and_skip("onetwo", '/'), None);
}
#[test]
fn csv_object_test_selection() {
let reader = BufReader::new(r#"
name, age, birth date
"Douglas Adams", "42", "1952-03-11"
"Gen Z. Person", "20", "2000-01-01"
"Ada Lovelace", "36", "1815-12-10"
"#.trim().as_bytes());
let mut csv = Csv::new(reader).unwrap();
csv.apply_selection(|row| {
Ok(row["age"].parse::<u32>().unwrap() < 40)
});
// Итерираме през резултата:
// while let Some(row) = csv.next() {
// println!("{:?}", row.unwrap().get("name"));
// // => Some("Douglas Adams")
// // => Some("Ada Lovelace")
// }
match csv.next(){
Some(row) => assert_eq!(row.unwrap().get("name").unwrap(), "Gen Z. Person"),
None => panic!()
}
match csv.next(){
Some(row) => assert_eq!(row.unwrap().get("name").unwrap(), "Ada Lovelace"),
None => panic!()
}
}
#[test]
fn csv_object_test_write_to() {
let reader = BufReader::new(r#"
name, age, birth date
"Douglas Adams", "42", "1952-03-11"
"Gen Z. Person", "20", "2000-01-01"
"Ada Lovelace", "36", "1815-12-10"
"#.trim().as_bytes());
let mut csv = Csv::new(reader).unwrap();
csv.apply_selection(|row| {
Ok(row["age"].parse::<u32>().unwrap() < 40)
});
let mut output = Vec::new();
csv.write_to(&mut output).unwrap();
// println!("{}", String::from_utf8(output).unwrap());
let desired_output = "name, age, birth date
\"Gen Z. Person\", \"20\", \"2000-01-01\"
\"Ada Lovelace\", \"36\", \"1815-12-10\"\n".to_string();
assert_eq!(String::from_utf8(output).unwrap(), desired_output);
}
}