Решение на CSV Filter от Борислав Димитров

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

Към профила на Борислав Димитров

Резултати

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

Код

pub fn skip_next(input: &str, target: char) -> Option<&str> {
let inp_to_vec: Vec<char> = input.chars().collect();
if !inp_to_vec.is_empty() && inp_to_vec[0] == target {
Some(&input[inp_to_vec[0].len_utf8()..])
} else {
None
}
}
pub fn skip_next1(input: &str, target: char) -> Option<&str> {
let inp_to_vec: Vec<char> = input.chars().collect();
let mut n = 0;
for i in 0..inp_to_vec.len(){
if inp_to_vec[i] == target {
n = n + inp_to_vec[i].len_utf8();
return Some(&input[n..]);
}
n = n + inp_to_vec[i].len_utf8();
}
None
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let inp_to_vec: Vec<char> = input.chars().collect();
let mut n = 0;
for i in 0..inp_to_vec.len(){
if inp_to_vec[i] == target {
return (&input[..n],&input[n..]);
}
n = n + inp_to_vec[i].len_utf8();
}
(&input,"")
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let inp_to_vec: Vec<char> = input.chars().collect();
let mut n = 0;
for i in 0..inp_to_vec.len(){
if inp_to_vec[i] == target {
return Some((&input[..n],&input[n+inp_to_vec[i].len_utf8()..]));
}
n = n + inp_to_vec[i].len_utf8();
}
None
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
use std::collections::HashMap;
type Row = HashMap<String, String>;
use std::io::{BufRead, Write};
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 columns: Vec<String> = Vec::new();
let mut buf: String = String::new();
let selection: Option<Box<dyn Fn(&Row) -> Result<bool, CsvError>>> = Some(Box::new(|_| Ok(true)));
let _ = match reader.read_line(&mut buf) {
Err(e) => return Err(CsvError::IO(e)),
Ok(0) => return Err(CsvError::InvalidHeader(String::from("Empty header"))),
Ok(_) => ()
};
while buf != "" {
let (x, y) = take_and_skip(buf.trim(), ',').unwrap_or((buf.trim(),""));
if !columns.contains(&x.trim().to_string()) {
columns.push(x.trim().to_string());
} else {
return Err(CsvError::InvalidHeader(String::from("Duplicate columns")));
}
buf = y.to_string();
}
Ok(Csv{columns, reader, selection})
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let mut curr_line = line;
let mut row = Row::new();
let mut c_c = 0; //comma_counter
for i in 0..self.columns.len()-1 {
curr_line = curr_line.trim();
if curr_line.chars().nth(0).unwrap() == '\"' {
let _ = match skip_next1(curr_line, '\"') {
Some(l) => {curr_line = l;},
None => {return Err(CsvError::InvalidRow(String::from("")));}
};
let _ = match take_until(curr_line, '\"') {
(_,"") => {return Err(CsvError::InvalidRow(String::from("")));}
(x,y) => {
row.insert(self.columns[i].clone(), x.to_string());
curr_line = match skip_next1(y.trim(), ','){
Some(l) => {
c_c = c_c + 1;
l
},
None => {
if c_c+1 != self.columns.len() {
return Err(CsvError::InvalidRow(String::from("")));
} else {
return Ok(row);
}
}
};
}
};
} else {
return Err(CsvError::InvalidRow(String::from("First element not \"")));
}
}
if c_c+1 != self.columns.len() {
return Err(CsvError::InvalidRow(String::from("")));
} else {
curr_line = curr_line.trim();
let _ = match skip_next1(curr_line, '\"') {
Some(l) => {curr_line = l;},
None => {return Err(CsvError::InvalidRow(String::from("")));}
};
let _ = match take_until(curr_line, '\"') {
(_,"") => {return Err(CsvError::InvalidRow(String::from("")));}
(x,_) => { curr_line=x; }
};
row.insert(self.columns[self.columns.len()-1].clone(), curr_line.to_string());
return 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 buf = String::new();
let mut c_c = 0; //comma_counter
for el in self.columns.iter() {
match writer.write_all(el.as_bytes()) {
Ok(_) => (),
Err(e) => {return Err(CsvError::IO(e))}
}
if c_c < self.columns.len()-1 {
let _ = writer.write_all(", ".as_bytes());
c_c = c_c + 1;
}
}
let mut vec_row: Vec<Row> = Vec::new();
loop {
match self.reader.read_line(&mut buf) {
Ok(0) => { break;},
Ok(_) => {
let row = match self.parse_line(&buf){
Err(e) => {return Err(e);},
Ok(r) => r,
};
vec_row.push(row.clone());
buf.clear();
},
Err(e) => {return Err(CsvError::IO(e))}
}
}
let filtered_row = vec_row.into_iter().filter(|row| self.selection.as_ref().unwrap()(&row).unwrap());
for vecs in filtered_row {
let _ = writer.write_all("\n\"".as_bytes());
c_c = 0;
for el in self.columns.iter() {
match writer.write_all(vecs.get(&el.clone()).unwrap().as_bytes()){
Ok(_) => (),
Err(e) => {return Err(CsvError::IO(e))}
}
if c_c < self.columns.len()-1 {
let _ = writer.write_all("\", \"".as_bytes());
c_c = c_c + 1;
}
}
}
let _ = writer.write_all("\"".as_bytes());
Ok(())
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
let mut buf = String::new();
loop {
match self.reader.read_line(&mut buf) {
Ok(0) => {return None;},
Err(e) => {return Some(Err(CsvError::IO(e)));},
Ok(_) => {
let row = match self.parse_line(&buf){
Err(e) => {return Some(Err(e));},
Ok(r) => r,
};
let _ = match self.selection.as_ref().unwrap()(&row) {
Err(e) => {return Some(Err(e));},
Ok(false) => (),
Ok(true) => {return Some(Ok(row));}
};
}
}
};
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string_parsing() { //UTF-8 supported
assert_eq!(skip_next("(foo", '('), Some("foo"));
assert_eq!(skip_next("(foo", ')'), None);
assert_eq!(skip_next("", ')'), None);
assert_eq!(skip_next("\"test]", '\"'), Some("test]"));
assert_eq!(skip_next(" foo/bar ", '/'), None);
assert_eq!(skip_next(" foo\"bar\" ", '"'), None);
assert_eq!(skip_next("абвгabcd", 'а'), Some("бвгabcd"));
assert_eq!(skip_next("абвгabcd", 'b'), None);
assert_eq!(skip_next("абвгabcd", 'я'), None);
assert_eq!(skip_next("абвгabcd", 'z'), None);
assert_eq!(skip_next("あなたは本当にそれを翻訳しましたか?", 'z'), None);
assert_eq!(skip_next("あなたは本当にそれを翻訳しましたか?", 'あ'), Some("なたは本当にそれを翻訳しましたか?"));
assert_eq!(skip_next(r#" "Basic Name", "13" , "2020-01-01" "#.trim(), '"'),
skip_next(r#" "Basic Name", "13" , "2020-01-01" "#.trim(), '\"'));
assert_eq!(take_until("", '/'), ("", ""));
assert_eq!(take_until("one/two", '/'), ("one", "/two"));
assert_eq!(take_until("one \"two\"", '"'),("one ","\"two\""));
assert_eq!(take_until(" foo/bar ", '/'), (" foo", "/bar "));
assert_eq!(take_until("foobar", '/'), ("foobar", ""));
assert_eq!(take_until("абвг/abc/d", '/'), ("абвг", "/abc/d"));
assert_eq!(take_until("абв/гabc/d", '/'), ("абв", "/гabc/d"));
assert_eq!(take_until("абвгa/bc/d", '/'), ("абвгa", "/bc/d"));
assert_eq!(take_until("абвгabcd", '/'), ("абвгabcd", ""));
assert_eq!(take_until("あなたは本当にそれを翻訳しましたか?", '/'),
("あなたは本当にそれを翻訳しましたか?", ""));
assert_eq!(take_until("あなたは本当にそれを翻訳しましたか?", 'を'),
("あなたは本当にそれ", "を翻訳しましたか?"));
assert_eq!(take_and_skip("", '/'), None);
assert_eq!(take_and_skip("one/two", '/'), Some(("one", "two")));
assert_eq!(take_and_skip(" foo,bar ", ','), Some((" foo", "bar ")));
assert_eq!(take_and_skip(" foo\"bar\" ", '"'), Some((" foo", "bar\" ")));
assert_eq!(take_and_skip("foobar", '/'), None);
assert_eq!(take_and_skip("абвг,abcd", ','), Some(("абвг", "abcd")));
assert_eq!(take_and_skip("абвФгabcd", 'Ф'), Some(("абв", "гabcd")));
assert_eq!(take_and_skip("абвгaZbcd", 'Z'), Some(("абвгa", "bcd")));
assert_eq!(take_and_skip("абвгfoobar", '/'), None);
assert_eq!(take_and_skip("あなたは本当にそれを翻訳しましたか?", '/'), None);
assert_eq!(take_and_skip("あなたは本当にそれを翻訳しましたか?", 'を'),
Some(("あなたは本当にそれ", "翻訳しましたか?")));
assert_eq!(take_and_skip("あなたは本当にそれa翻訳しましたか?", 'a'),
Some(("あなたは本当にそれ", "翻訳しましたか?")));
assert_eq!(take_and_skip("あなたは本当にそれa翻訳しましたか?", '翻'),
Some(("あなたは本当にそれa", "訳しましたか?")));
assert_eq!(take_and_skip("あなたは本当にそれa翻訳しましたか?", 'れ'),
Some(("あなたは本当にそ", "a翻訳しましたか?")));
}
#[test]
fn test_csv_error() {
assert_match!(Csv::new(ErroringReader {}).err(), Some(CsvError::IO(_)));
}
#[test]
fn test_basic_csv() {
let data = r#"
name, age, birth date
"Gen Z. Person", "20", "2000-01-01"
"#.trim().as_bytes();
let mut csv = Csv::new(BufReader::new(data)).unwrap();
csv.apply_selection(|_row| Ok(true));
// Парсене на един ред:
let row = csv.parse_line(r#""Basic Name","13","2020-01-01""#).unwrap();
assert_eq! {
(row["name"].as_str(), row["age"].as_str(), row["birth date"].as_str()),
("Basic Name", "13", "2020-01-01"),
};
// Употреба като итератор:
let filtered_names = csv.map(|row| row.unwrap()["name"].clone()).collect::<Vec<_>>();
assert_eq!(filtered_names, &["Gen Z. Person"]);
// Писане в някакъв изход
let mut csv = Csv::new(BufReader::new(data)).unwrap();
csv.apply_selection(|_row| Ok(true));
let mut output = Vec::new();
csv.write_to(&mut output).unwrap();
let output_lines = output.lines().
map(Result::unwrap).
collect::<Vec<String>>();
assert_eq!(output_lines, &[
"name, age, birth date",
"\"Gen Z. Person\", \"20\", \"2000-01-01\"",
]);
}
}

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

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

running 15 tests
test solution_test::test_csv_basic ... ok
test solution_test::test_csv_duplicate_columns ... ok
test solution_test::test_csv_empty ... ok
test solution_test::test_csv_iterating_with_a_selection ... FAILED
test solution_test::test_csv_iterating_with_no_selection ... ok
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 ... ok
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 ... FAILED
test solution_test::test_parsing_helpers_for_unicode ... ok
test solution_test::test_skip_next ... ok
test solution_test::test_take_and_skip ... ok
test solution_test::test_take_until ... ok

failures:

---- solution_test::test_csv_iterating_with_a_selection stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `["Douglas Adams"]`,
 right: `["Douglas Adams", "Ada Lovelace"]`', tests/solution_test.rs:219:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

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


failures:
    solution_test::test_csv_iterating_with_a_selection
    solution_test::test_csv_writing_without_a_selection
    solution_test::test_csv_writing_without_any_rows

test result: FAILED. 12 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out

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

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

Борислав качи първо решение на 11.01.2021 16:15 (преди над 4 години)

Борислав качи решение на 11.01.2021 16:55 (преди над 4 години)

pub fn skip_next(input: &str, target: char) -> Option<&str> {
let inp_to_vec: Vec<char> = input.chars().collect();
if !inp_to_vec.is_empty() && inp_to_vec[0] == target {
Some(&input[inp_to_vec[0].len_utf8()..])
} else {
None
}
}
pub fn skip_next1(input: &str, target: char) -> Option<&str> {
let inp_to_vec: Vec<char> = input.chars().collect();
let mut n = 0;
for i in 0..inp_to_vec.len(){
if inp_to_vec[i] == target {
n = n + inp_to_vec[i].len_utf8();
return Some(&input[n..]);
}
n = n + inp_to_vec[i].len_utf8();
}
None
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let inp_to_vec: Vec<char> = input.chars().collect();
let mut n = 0;
for i in 0..inp_to_vec.len(){
if inp_to_vec[i] == target {
return (&input[..n],&input[n..]);
}
n = n + inp_to_vec[i].len_utf8();
}
(&input,"")
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let inp_to_vec: Vec<char> = input.chars().collect();
let mut n = 0;
for i in 0..inp_to_vec.len(){
if inp_to_vec[i] == target {
return Some((&input[..n],&input[n+inp_to_vec[i].len_utf8()..]));
}
n = n + inp_to_vec[i].len_utf8();
}
None
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
use std::collections::HashMap;
type Row = HashMap<String, String>;
use std::io::{BufRead, Write};
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 columns: Vec<String> = Vec::new();
let mut buf: String = String::new();
let selection: Option<Box<dyn Fn(&Row) -> Result<bool, CsvError>>> = Some(Box::new(|_| Ok(true)));
let _ = match reader.read_line(&mut buf) {
Err(e) => return Err(CsvError::IO(e)),
Ok(0) => return Err(CsvError::InvalidHeader(String::from("Empty header"))),
Ok(_) => ()
};
while buf != "" {
let (x, y) = take_and_skip(buf.trim(), ',').unwrap_or((buf.trim(),""));
if !columns.contains(&x.trim().to_string()) {
columns.push(x.trim().to_string());
} else {
return Err(CsvError::InvalidHeader(String::from("Duplicate columns")));
}
buf = y.to_string();
}
Ok(Csv{columns, reader, selection})
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let mut curr_line = line;
let mut row = Row::new();
let mut c_c = 0; //comma_counter
for i in 0..self.columns.len()-1 {
curr_line = curr_line.trim();
if curr_line.chars().nth(0).unwrap() == '\"' {
let _ = match skip_next1(curr_line, '\"') {
Some(l) => {curr_line = l;},
None => {return Err(CsvError::InvalidRow(String::from("")));}
};
let _ = match take_until(curr_line, '\"') {
(_,"") => {return Err(CsvError::InvalidRow(String::from("")));}
(x,y) => {
row.insert(self.columns[i].clone(), x.to_string());
curr_line = match skip_next1(y.trim(), ','){
Some(l) => {
c_c = c_c + 1;
l
},
None => {
if c_c+1 != self.columns.len() {
return Err(CsvError::InvalidRow(String::from("")));
} else {
return Ok(row);
}
}
};
}
};
} else {
return Err(CsvError::InvalidRow(String::from("First element not \"")));
}
}
if c_c+1 != self.columns.len() {
return Err(CsvError::InvalidRow(String::from("")));
} else {
curr_line = curr_line.trim();
let _ = match skip_next1(curr_line, '\"') {
Some(l) => {curr_line = l;},
None => {return Err(CsvError::InvalidRow(String::from("")));}
};
let _ = match take_until(curr_line, '\"') {
(_,"") => {return Err(CsvError::InvalidRow(String::from("")));}
(x,_) => { curr_line=x; }
};
row.insert(self.columns[self.columns.len()-1].clone(), curr_line.to_string());
return 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 buf = String::new();
let mut c_c = 0; //comma_counter
for el in self.columns.iter() {
match writer.write_all(el.as_bytes()) {
Ok(_) => (),
Err(e) => {return Err(CsvError::IO(e))}
}
if c_c < self.columns.len()-1 {
let _ = writer.write_all(", ".as_bytes());
c_c = c_c + 1;
}
}
-
+ let mut vec_row: Vec<Row> = Vec::new();
loop {
match self.reader.read_line(&mut buf) {
Ok(0) => { break;},
Ok(_) => {
- let _ = writer.write_all("\n\"".as_bytes());
let row = match self.parse_line(&buf){
Err(e) => {return Err(e);},
Ok(r) => r,
};
- c_c = 0;
- for el in self.columns.iter() {
- match writer.write_all(row.get(&el.clone()).unwrap().as_bytes()) {
- Ok(_) => (),
- Err(e) => {return Err(CsvError::IO(e))}
- }
- if c_c < self.columns.len()-1 {
- let _ = writer.write_all("\", \"".as_bytes());
- c_c = c_c + 1;
- }
- }
+ vec_row.push(row.clone());
buf.clear();
},
Err(e) => {return Err(CsvError::IO(e))}
+ }
+ }
+ let filtered_row = vec_row.into_iter().filter(|row| self.selection.as_ref().unwrap()(&row).unwrap());
+ for vecs in filtered_row {
+ let _ = writer.write_all("\n\"".as_bytes());
+ c_c = 0;
+ for el in self.columns.iter() {
+ match writer.write_all(vecs.get(&el.clone()).unwrap().as_bytes()){
+ Ok(_) => (),
+ Err(e) => {return Err(CsvError::IO(e))}
+ }
+ if c_c < self.columns.len()-1 {
+ let _ = writer.write_all("\", \"".as_bytes());
+ c_c = c_c + 1;
+ }
}
}
let _ = writer.write_all("\"".as_bytes());
Ok(())
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
let mut buf = String::new();
loop {
match self.reader.read_line(&mut buf) {
Ok(0) => {return None;},
Err(e) => {return Some(Err(CsvError::IO(e)));},
Ok(_) => {
let row = match self.parse_line(&buf){
Err(e) => {return Some(Err(e));},
Ok(r) => r,
};
let _ = match self.selection.as_ref().unwrap()(&row) {
Err(e) => {return Some(Err(e));},
Ok(false) => (),
Ok(true) => {return Some(Ok(row));}
};
}
}
};
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string_parsing() { //UTF-8 supported
assert_eq!(skip_next("(foo", '('), Some("foo"));
assert_eq!(skip_next("(foo", ')'), None);
assert_eq!(skip_next("", ')'), None);
assert_eq!(skip_next("\"test]", '\"'), Some("test]"));
assert_eq!(skip_next(" foo/bar ", '/'), None);
assert_eq!(skip_next(" foo\"bar\" ", '"'), None);
assert_eq!(skip_next("абвгabcd", 'а'), Some("бвгabcd"));
assert_eq!(skip_next("абвгabcd", 'b'), None);
assert_eq!(skip_next("абвгabcd", 'я'), None);
assert_eq!(skip_next("абвгabcd", 'z'), None);
assert_eq!(skip_next("あなたは本当にそれを翻訳しましたか?", 'z'), None);
assert_eq!(skip_next("あなたは本当にそれを翻訳しましたか?", 'あ'), Some("なたは本当にそれを翻訳しましたか?"));
assert_eq!(skip_next(r#" "Basic Name", "13" , "2020-01-01" "#.trim(), '"'),
skip_next(r#" "Basic Name", "13" , "2020-01-01" "#.trim(), '\"'));
assert_eq!(take_until("", '/'), ("", ""));
assert_eq!(take_until("one/two", '/'), ("one", "/two"));
assert_eq!(take_until("one \"two\"", '"'),("one ","\"two\""));
assert_eq!(take_until(" foo/bar ", '/'), (" foo", "/bar "));
assert_eq!(take_until("foobar", '/'), ("foobar", ""));
assert_eq!(take_until("абвг/abc/d", '/'), ("абвг", "/abc/d"));
assert_eq!(take_until("абв/гabc/d", '/'), ("абв", "/гabc/d"));
assert_eq!(take_until("абвгa/bc/d", '/'), ("абвгa", "/bc/d"));
assert_eq!(take_until("абвгabcd", '/'), ("абвгabcd", ""));
assert_eq!(take_until("あなたは本当にそれを翻訳しましたか?", '/'),
("あなたは本当にそれを翻訳しましたか?", ""));
assert_eq!(take_until("あなたは本当にそれを翻訳しましたか?", 'を'),
("あなたは本当にそれ", "を翻訳しましたか?"));
assert_eq!(take_and_skip("", '/'), None);
assert_eq!(take_and_skip("one/two", '/'), Some(("one", "two")));
assert_eq!(take_and_skip(" foo,bar ", ','), Some((" foo", "bar ")));
assert_eq!(take_and_skip(" foo\"bar\" ", '"'), Some((" foo", "bar\" ")));
assert_eq!(take_and_skip("foobar", '/'), None);
assert_eq!(take_and_skip("абвг,abcd", ','), Some(("абвг", "abcd")));
assert_eq!(take_and_skip("абвФгabcd", 'Ф'), Some(("абв", "гabcd")));
assert_eq!(take_and_skip("абвгaZbcd", 'Z'), Some(("абвгa", "bcd")));
assert_eq!(take_and_skip("абвгfoobar", '/'), None);
assert_eq!(take_and_skip("あなたは本当にそれを翻訳しましたか?", '/'), None);
assert_eq!(take_and_skip("あなたは本当にそれを翻訳しましたか?", 'を'),
Some(("あなたは本当にそれ", "翻訳しましたか?")));
assert_eq!(take_and_skip("あなたは本当にそれa翻訳しましたか?", 'a'),
Some(("あなたは本当にそれ", "翻訳しましたか?")));
assert_eq!(take_and_skip("あなたは本当にそれa翻訳しましたか?", '翻'),
Some(("あなたは本当にそれa", "訳しましたか?")));
assert_eq!(take_and_skip("あなたは本当にそれa翻訳しましたか?", 'れ'),
Some(("あなたは本当にそ", "a翻訳しましたか?")));
}
#[test]
fn test_csv_error() {
assert_match!(Csv::new(ErroringReader {}).err(), Some(CsvError::IO(_)));
}
#[test]
fn test_basic_csv() {
let data = r#"
name, age, birth date
"Gen Z. Person", "20", "2000-01-01"
"#.trim().as_bytes();
let mut csv = Csv::new(BufReader::new(data)).unwrap();
csv.apply_selection(|_row| Ok(true));
// Парсене на един ред:
let row = csv.parse_line(r#""Basic Name","13","2020-01-01""#).unwrap();
assert_eq! {
(row["name"].as_str(), row["age"].as_str(), row["birth date"].as_str()),
("Basic Name", "13", "2020-01-01"),
};
// Употреба като итератор:
let filtered_names = csv.map(|row| row.unwrap()["name"].clone()).collect::<Vec<_>>();
assert_eq!(filtered_names, &["Gen Z. Person"]);
// Писане в някакъв изход
let mut csv = Csv::new(BufReader::new(data)).unwrap();
csv.apply_selection(|_row| Ok(true));
let mut output = Vec::new();
csv.write_to(&mut output).unwrap();
let output_lines = output.lines().
map(Result::unwrap).
collect::<Vec<String>>();
assert_eq!(output_lines, &[
"name, age, birth date",
"\"Gen Z. Person\", \"20\", \"2000-01-01\"",
]);
}
}