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

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

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

Резултати

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

Код

use std::io::{BufRead, BufReader};
use std::collections::HashMap;
use std::io::Write;
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
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 header = String::new();
match reader.read_line(&mut header) {
Ok(n) => {
if n == 0 {
return Err(CsvError::InvalidHeader(String::from("Invalid header: readLine returns 0")));
}
}
Err(error) => return Err(CsvError::IO(error)),
}
let mut remaining_header = header;
let mut columns = Vec::new();
while true {
let separated_line = take_and_skip(&remaining_header, ',');
if separated_line.is_none(){
let remaining_header_trimmed = remaining_header.trim();
if columns.contains(&String::from(remaining_header_trimmed)) {
return Err(CsvError::InvalidHeader(String::from("Invalid header: there is a columns duplication in the header")));
} else {
columns.push(String::from(remaining_header_trimmed));
}
break;
}
let separated_line_unwrapped = separated_line.unwrap();
let (first,second) = separated_line_unwrapped;
let first_trimmed = first.trim();
if columns.contains(&String::from(first_trimmed)) {
return Err(CsvError::InvalidHeader(String::from("Invalid header: there is a columns duplication in the header")));
} else {
columns.push(String::from(first_trimmed));
}
remaining_header = String::from(second);
}
return Ok(Csv{columns: columns, reader: reader, selection: None});
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let trimmed_line = line.trim();
let mut row = Row::new();
if trimmed_line.is_empty() {
return Err(CsvError::InvalidRow(String::from("Invalid row: the row is empty")));
}
let mut rest_of_line = trimmed_line;
for column in self.columns.iter() {
let separated_line = take_and_skip(&rest_of_line, ',');
if separated_line.is_none(){
row.insert(column.to_string(), String::from(rest_of_line));
break;
}
let separated_line_unwrapped = separated_line.unwrap();
let (first,second) = separated_line_unwrapped;
let first_trimmed = first.trim();
let first_trimmed_without_quotes : &str= &first_trimmed[1..(first_trimmed.chars().count()-1)];
row.insert(column.to_string(), String::from(first_trimmed_without_quotes));
rest_of_line = second;
}
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> {
todo!()
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
while true {
let mut line = String::new();
match self.reader.read_line(&mut line) {
Ok(number_of_bytes_read) => {
if number_of_bytes_read == 0 {
return None;
}
}
Err(error) => return Some(Err(CsvError::IO(error))),
}
let parsed_line = self.parse_line(&line);
let row;
match parsed_line {
Ok(parsed_line_value) => {
row = parsed_line_value;
}
Err(error) => return Some(Err(error)),
}
let selection_unwrapped = self.selection.as_ref().unwrap();
let selection_result = selection_unwrapped(&row);
match selection_result {
Ok(n) => {
if n {
return Some(Ok(row));
}
}
Err(error) => return Some(Err(error)),
}
}
return None;
}
}
pub fn skip_next(input: &str, target: char) -> Option<&str> {
if input.is_empty(){
return None
}
let first_letter = input.chars().nth(0).unwrap();
if first_letter == target {
return Some(&input[1..]);
}
return None;
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
for (i, c) in input.chars().enumerate() {
if c == target {
return input.split_at(i);
}
}
return (input, "");
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let (first, second) = take_until(input, target);
if second.is_empty() {
return None
}
let second_without_target = skip_next(&second, target).unwrap();
return Some((first, second_without_target))
}

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

Compiling solution v0.1.0 (/tmp/d20210111-1538662-1qjjaiw/solution)
warning: unused import: `BufReader`
 --> src/lib.rs:1:24
  |
1 | use std::io::{BufRead, BufReader};
  |                        ^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: denote infinite loops with `loop { ... }`
  --> src/lib.rs:38:9
   |
38 |         while true {
   |         ^^^^^^^^^^ help: use `loop`
   |
   = note: `#[warn(while_true)]` on by default

warning: denote infinite loops with `loop { ... }`
   --> src/lib.rs:110:9
    |
110 |         while true {
    |         ^^^^^^^^^^ help: use `loop`

warning: unused variable: `writer`
   --> src/lib.rs:101:37
    |
101 | pub fn write_to<W: Write>(mut self, mut writer: W) -> Result<(), CsvError> {
    |                                     ^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_writer`
    |
    = note: `#[warn(unused_variables)]` on by default

warning: variable does not need to be mutable
   --> src/lib.rs:101:27
    |
101 | pub fn write_to<W: Write>(mut self, mut writer: W) -> Result<(), CsvError> {
    |                           ----^^^^
    |                           |
    |                           help: remove this `mut`
    |
    = note: `#[warn(unused_mut)]` on by default

warning: variable does not need to be mutable
   --> src/lib.rs:101:37
    |
101 | pub fn write_to<W: Write>(mut self, mut writer: W) -> Result<(), CsvError> {
    |                                     ----^^^^^^
    |                                     |
    |                                     help: remove this `mut`

warning: 6 warnings emitted

    Finished test [unoptimized + debuginfo] target(s) in 3.85s
     Running target/debug/deps/solution_test-8916805fc40a2dab

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

failures:

---- solution_test::test_csv_basic stdout ----
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', /tmp/d20210111-1538662-1qjjaiw/solution/src/lib.rs:133:63
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_iterating_with_no_selection stdout ----
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', /tmp/d20210111-1538662-1qjjaiw/solution/src/lib.rs:133:63
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:185:5

---- solution_test::test_csv_parse_line stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `("Basic Name", "13", "\"2020-01-01\"")`,
 right: `("Basic Name", "13", "2020-01-01")`', tests/solution_test.rs:145:5

---- solution_test::test_csv_parse_line_with_commas stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `("Nam", "13")`,
 right: `("Name, Basic", "13")`', tests/solution_test.rs:163:5

---- solution_test::test_csv_selection_and_writing stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20210111-1538662-1qjjaiw/solution/src/lib.rs:102: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-1qjjaiw/solution/src/lib.rs:102: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-1qjjaiw/solution/src/lib.rs:102:9
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:278: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:158:22

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

---- solution_test::test_take_until stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `("ба", "ба/яга")`,
 right: `("баба", "/яга")`', tests/solution_test.rs:121:5


failures:
    solution_test::test_csv_basic
    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
    solution_test::test_parsing_helpers_for_unicode
    solution_test::test_take_and_skip
    solution_test::test_take_until

test result: FAILED. 5 passed; 10 failed; 0 ignored; 0 measured; 0 filtered out

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

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

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