Решение на CSV Filter от Симеон Николов

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

Към профила на Симеон Николов

Резултати

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

Код

use std::collections::HashMap;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
pub fn skip_next(input: &str, target: char) -> Option<&str> {
input.strip_prefix(target)
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let divider = input.find(target);
if divider != None{
input.split_at(divider.unwrap())
}
else{
input.split_at(input.len())
}
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let divider = input.find(target);
if divider != None{
let mut tuple:(&str, &str) = take_until(input, target);
tuple.1 = skip_next(tuple.1,target).unwrap();
Some(tuple)
}
else{
None
}
}
pub struct Csv<R: BufRead> {
pub columns: Vec<String>,
reader: R,
selection: Option<Box<dyn Fn(&Row) -> Result<bool, CsvError>>>,
}
use std::io::{Error,ErrorKind,BufRead,Write};
type Row = HashMap<String, String>;
impl<R: BufRead> Csv<R> {
pub fn new(mut reader: R) -> Result<Self, CsvError> {
let mut first_row = String::new();
match reader.read_line(&mut first_row){
Ok(0) => Err(CsvError::InvalidHeader(String::from("There isn`t a valid header."))),
Ok(_) => {
let new_columns = first_row.split(',').map(|x| x.trim()).map(|x| x.to_string()).collect::<Vec<String>>();
Ok(Self{columns:new_columns,reader:reader,selection:None})
},
_ => Err(CsvError::IO(Error::new(ErrorKind::Other, "read error!")))
}
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
line.trim().to_string();
let mut row = Row::new();
let mut flag:bool = true;
let elements:Vec<&str> = line.split(',').collect();
for i in 0..elements.len(){
if elements[i].starts_with('"') == true && elements[i].ends_with('"') == true{
row.insert(self.columns[i].clone(),elements[i].replace('"'," ").trim().to_string());
}
else{
flag = false;
}
}
if elements.len()!=self.columns.len(){
Err(CsvError::InvalidRow(String::from("The amount of values on the row is different from the amount of columns")))
}
else if flag == false{
Err(CsvError::InvalidRow(String::from("Some data does not have brackets ")))
}
else{
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>( self, writer: W) -> Result<(), CsvError> {
todo!()
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
let mut new_str = String::new();
match self.reader.read_line(&mut new_str){
Ok(0) => None,
Ok(_) => match self.parse_line(new_str.as_str()){
Ok(new_row)=> match self.selection.as_ref().unwrap()(&new_row){
Err(_) => Some(Err(CsvError::InvalidRow(String::from("The row does not cover the condition")))),
Ok(false) => self.next(),
Ok(true) =>Some(Ok(new_row))
} ,
_ => Some(Err(self.parse_line(new_str.as_str()).unwrap_err()))
},
_ => Some(Err(CsvError::IO(Error::new(ErrorKind::Other, "read error!"))))
}
}
}
//tests
#[test]
fn test_string_parsing() {
assert_eq!(skip_next("[test]", '['), Some("test]"));
assert_eq!(take_until("one/two", '/'), ("one", "/two"));
assert_eq!(take_and_skip("one/two", '/'), Some(("one", "two")));
}
#[test]
fn test_csv_error() {
assert_match!(Csv::new(ErroringReader {}).err(), Some(CsvError::IO(_)));
}
#[test]
fn create_new_csv(){
let data = r#"
name, age, birth date
"Gen Z. Person", "20", "2000-01-01"
"#.trim().as_bytes();
let csv = Csv::new(BufReader::new(data)).unwrap();
assert_eq!(csv.columns[0],"name");
assert_eq!(csv.columns[1],"age");
assert_eq!(csv.columns[2],"birth date");
}
#[test]
fn parse_one_line(){
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();
let row = csv.parse_line(r#""Basic Name","13","2020-01-01""#).unwrap();
// println!("{:?}" ,row);
assert_eq! {
(row["name"].as_str(), row["age"].as_str(), row["birth date"].as_str()),
("Basic Name", "13", "2020-01-01"),
};
}
#[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-1cr5upb/solution)
warning: unused variable: `writer`
   --> src/lib.rs:115:39
    |
115 |     pub fn write_to<W: Write>( self,  writer: W) -> Result<(), CsvError> {
    |                                       ^^^^^^ help: if this is intentional, prefix it with an underscore: `_writer`
    |
    = note: `#[warn(unused_variables)]` on by default

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 4.05s
     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 ... FAILED
test solution_test::test_csv_iterating_with_no_selection ... FAILED
test solution_test::test_csv_parse_line ... FAILED
test solution_test::test_csv_parse_line_with_commas ... FAILED
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 ... 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_basic stdout ----
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidRow("Some data does not have brackets ")', tests/solution_test.rs:70:39
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidRow("Some data does not have brackets ")', 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_a_selection stdout ----
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidRow("Some data does not have brackets ")', tests/solution_test.rs:218:44

---- solution_test::test_csv_iterating_with_no_selection stdout ----
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidRow("Some data does not have brackets ")', tests/solution_test.rs:195:48
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidRow("Some data does not have brackets ")', tests/solution_test.rs:185:5

---- solution_test::test_csv_parse_line stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `("Name With  Spaces", "13", "0-0-0")`,
 right: `(" Name With  Spaces  ", " 13 ", "0-0-0")`', tests/solution_test.rs:151:5

---- solution_test::test_csv_parse_line_with_commas stdout ----
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidRow("The amount of values on the row is different from the amount of columns")', tests/solution_test.rs:162:56

---- solution_test::test_csv_selection_and_writing stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20210111-1538662-1cr5upb/solution/src/lib.rs:116:9
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:251:5

---- solution_test::test_csv_writing_without_a_selection stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20210111-1538662-1cr5upb/solution/src/lib.rs:116:9
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:224:5

---- solution_test::test_csv_writing_without_any_rows stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20210111-1538662-1cr5upb/solution/src/lib.rs:116:9
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:278:5


failures:
    solution_test::test_csv_basic
    solution_test::test_csv_duplicate_columns
    solution_test::test_csv_iterating_with_a_selection
    solution_test::test_csv_iterating_with_no_selection
    solution_test::test_csv_parse_line
    solution_test::test_csv_parse_line_with_commas
    solution_test::test_csv_selection_and_writing
    solution_test::test_csv_writing_without_a_selection
    solution_test::test_csv_writing_without_any_rows

test result: FAILED. 6 passed; 9 failed; 0 ignored; 0 measured; 0 filtered out

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

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

Симеон качи първо решение на 11.01.2021 15:08 (преди над 4 години)

Симеон качи решение на 11.01.2021 15:09 (преди над 4 години)

use std::collections::HashMap;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
pub fn skip_next(input: &str, target: char) -> Option<&str> {
input.strip_prefix(target)
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let divider = input.find(target);
if divider != None{
input.split_at(divider.unwrap())
}
else{
input.split_at(input.len())
}
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let divider = input.find(target);
if divider != None{
let mut tuple:(&str, &str) = take_until(input, target);
tuple.1 = skip_next(tuple.1,target).unwrap();
Some(tuple)
}
else{
None
}
}
pub struct Csv<R: BufRead> {
pub columns: Vec<String>,
reader: R,
selection: Option<Box<dyn Fn(&Row) -> Result<bool, CsvError>>>,
}
use std::io::{Error,ErrorKind,BufRead,Write};
type Row = HashMap<String, String>;
impl<R: BufRead> Csv<R> {
pub fn new(mut reader: R) -> Result<Self, CsvError> {
let mut first_row = String::new();
match reader.read_line(&mut first_row){
Ok(0) => Err(CsvError::InvalidHeader(String::from("There isn`t a valid header."))),
Ok(_) => {
let new_columns = first_row.split(',').map(|x| x.trim()).map(|x| x.to_string()).collect::<Vec<String>>();
Ok(Self{columns:new_columns,reader:reader,selection:None})
},
_ => Err(CsvError::IO(Error::new(ErrorKind::Other, "read error!")))
}
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
line.trim().to_string();
let mut row = Row::new();
let mut flag:bool = true;
let elements:Vec<&str> = line.split(',').collect();
for i in 0..elements.len(){
if elements[i].starts_with('"') == true && elements[i].ends_with('"') == true{
row.insert(self.columns[i].clone(),elements[i].replace('"'," ").trim().to_string());
}
else{
flag = false;
}
}
if elements.len()!=self.columns.len(){
Err(CsvError::InvalidRow(String::from("The amount of values on the row is different from the amount of columns")))
}
else if flag == false{
Err(CsvError::InvalidRow(String::from("Some data does not have brackets ")))
}
else{
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>( self, writer: W) -> Result<(), CsvError> {
todo!()
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
let mut new_str = String::new();
match self.reader.read_line(&mut new_str){
Ok(0) => None,
Ok(_) => match self.parse_line(new_str.as_str()){
Ok(new_row)=> match self.selection.as_ref().unwrap()(&new_row){
Err(_) => Some(Err(CsvError::InvalidRow(String::from("The row does not cover the condition")))),
- Ok(false) => self.next()
- ,
+ Ok(false) => self.next(),
Ok(true) =>Some(Ok(new_row))
} ,
_ => Some(Err(self.parse_line(new_str.as_str()).unwrap_err()))
},
_ => Some(Err(CsvError::IO(Error::new(ErrorKind::Other, "read error!"))))
}
}
}
//tests
#[test]
fn test_string_parsing() {
assert_eq!(skip_next("[test]", '['), Some("test]"));
assert_eq!(take_until("one/two", '/'), ("one", "/two"));
assert_eq!(take_and_skip("one/two", '/'), Some(("one", "two")));
}
#[test]
fn test_csv_error() {
assert_match!(Csv::new(ErroringReader {}).err(), Some(CsvError::IO(_)));
}
#[test]
fn create_new_csv(){
let data = r#"
name, age, birth date
"Gen Z. Person", "20", "2000-01-01"
"#.trim().as_bytes();
let csv = Csv::new(BufReader::new(data)).unwrap();
assert_eq!(csv.columns[0],"name");
assert_eq!(csv.columns[1],"age");
assert_eq!(csv.columns[2],"birth date");
}
#[test]
fn parse_one_line(){
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();
let row = csv.parse_line(r#""Basic Name","13","2020-01-01""#).unwrap();
// println!("{:?}" ,row);
assert_eq! {
(row["name"].as_str(), row["age"].as_str(), row["birth date"].as_str()),
("Basic Name", "13", "2020-01-01"),
};
}
#[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\"",
]);
}