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

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

Към профила на Ивайло Атовски

Резултати

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

Код

pub fn skip_next(input: &str, target: char) -> Option<&str> {
if input.is_empty(){
return None;
}
let mut chars = input.chars();
match chars.next().unwrap() {
target => Some(chars.as_str()),
_ => None
}
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let byte = input.find(target);
match byte{
Some(x) => input.split_at(x),
None => (input, "")
}
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let (str1, str2) = take_until(input, target);
match skip_next(str2, target){
Some(str3) => Some( (str1, str3) ),
None => 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;
pub struct Csv<R: BufRead> {
pub columns: Vec<String>,
reader: R,
selection: Option<Box<dyn Fn(&Row) -> Result<bool, CsvError>>>,
}
use std::io::Write;
impl<R: BufRead> Csv<R> {
pub fn new(mut reader: R) -> Result<Self, CsvError> {
let mut buffer: String = String::new();
match reader.read_line(&mut buffer) {
Ok(x) if x!=0 => {
let mut aMap = Row::new();
let mut columns = Vec::<String>::new();
let mut str_rest =String::new();
match take_and_skip(&buffer, ','){
Some((str1,str2)) =>{
if !aMap.contains_key(str1){
aMap.insert(str1.trim().to_string(), "".to_string());
columns.push(str1.trim().to_string());
str_rest = str2.to_string();
}
else{
return Err(CsvError::InvalidHeader(String::from("Repetition")));
}
}
None => {
buffer = buffer.trim().trim_end_matches("\n").to_string();
if !aMap.contains_key(&buffer){
aMap.insert(buffer.trim().to_string(), "".to_string());
columns.push(buffer.trim().to_string());
return Ok(Self{columns: columns, reader: reader, selection: None});
}
else{
return Err(CsvError::InvalidHeader(String::from("Repetition")));
}
}
};
while let Some((str1, str2)) = take_and_skip(&str_rest, ','){
if !aMap.contains_key(str1){
aMap.insert(str1.trim().to_string(), "".to_string());
columns.push(str1.trim().to_string());
str_rest = str2.to_string();
}
else{
return Err(CsvError::InvalidHeader(String::from("Repetition")));
}
}
str_rest = str_rest.trim().trim_end_matches("\n").to_string();
if !aMap.contains_key(&str_rest){
aMap.insert(str_rest.clone(), "".to_string());
columns.push(str_rest.clone());
}
else{
return Err(CsvError::InvalidHeader(String::from("Repetition")));
}
Ok(Self{columns: columns, reader: reader, selection: None})
}
Ok(x) if x== 0 => Err(CsvError::InvalidHeader(String::from("InvalidHeader new"))),
Ok(_) => Err(CsvError::InvalidHeader(String::from("InvalidHeader new"))),
Err(e) => Err(CsvError::IO(e) )
}
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let mut row = Row::new();
let mut val = String::from("");
let str_line = line.trim().trim_end_matches("\n").to_string();
if str_line.chars().next().unwrap() != '"'{
return Err(CsvError::InvalidRow("Invalid Row - Не започва с \" ".to_string()));
}
let mut is_open =false;
let max_column: usize = self.columns.len();
let mut index: usize = 0;
for ch in str_line.chars() {
if ch == '"' && !is_open {
if index == max_column{
return Err(CsvError::InvalidRow("Invalid Row- Колоните са по малко от колкото на реда".to_string()));
}
is_open = true;
val.clear();
}
else if ch == '"' && is_open{
row.insert(self.columns[index].clone(), val.clone());
is_open = false;
index +=1;
}else{
val.push(ch);
}
}
if is_open{
return Err(CsvError::InvalidRow("Invalid Row- не е затворена \" накрая".to_string()));
}
if index != max_column{
return Err(CsvError::InvalidRow("Invalid Row- Колоните са повече от колкото на реда".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 string_all = String::new();
let col = self.columns.clone();
let col_size = col.len();
for index in 0..col_size {
string_all.push(' ');
string_all.push_str(col[index].clone().as_str());
string_all.push(',')
}
string_all.pop();
string_all.push('\n');
for elem in self {
match elem {
Ok(row) => {
for index in 0..col_size {
let key = col[index].clone();
let val = match row.get(&key){
Some(iner_val) => iner_val.clone(),
None => unreachable!()
};
string_all.push('"');
string_all.push_str(val.as_str());
string_all.push('"');
string_all.push_str(", ");
}
string_all = string_all.trim().to_string();
string_all.pop();
string_all.push('\n');
},
Err(e) => return Err(e)
}
}
match writer.write(string_all.as_bytes()){
Ok(_) => Ok(()),
Err(e) => {
Err(CsvError::IO(e))
}
}
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
let mut buffer: String = String::new();
match self.reader.read_line(&mut buffer) {
Ok(x) if x!=0 => {
match self.parse_line(&buffer){
Ok(row) => {
match &self.selection{
Some(box_fn)=>{
let is_ok = match box_fn(&row) {
Ok(is_ok) =>is_ok,
Err(e)=>return Some(Err(e))
};
if is_ok{
Some(Ok(row))
}
else {
self.next()
}
},
None=>{
Some(Err(CsvError::InvalidRow("Не е сложено Условие".to_string())))
}
}
},
Err(e)=> Some(Err(e))
}
},
Ok(x) if x == 0 => None,
Ok(_) => unreachable!(),
Err(e) => Some(Err(CsvError::IO(e) ))
}
}
}

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

Compiling solution v0.1.0 (/tmp/d20210111-1538662-3f0wc3/solution)
warning: unreachable pattern
  --> src/lib.rs:10:9
   |
9  |         target => Some(chars.as_str()),
   |         ------ matches any value
10 |         _ =>  None
   |         ^ unreachable pattern
   |
   = note: `#[warn(unreachable_patterns)]` on by default

warning: unused variable: `target`
 --> src/lib.rs:9:9
  |
9 |         target => Some(chars.as_str()),
  |         ^^^^^^ help: if this is intentional, prefix it with an underscore: `_target`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `target`
 --> src/lib.rs:1:31
  |
1 | pub fn skip_next(input: &str, target: char) -> Option<&str> {
  |                               ^^^^^^ help: if this is intentional, prefix it with an underscore: `_target`

warning: value assigned to `str_rest` is never read
  --> src/lib.rs:60:21
   |
60 |                 let mut str_rest =String::new();
   |                     ^^^^^^^^^^^^
   |
   = note: `#[warn(unused_assignments)]` on by default
   = help: maybe it is overwritten before being read?

warning: variable does not need to be mutable
   --> src/lib.rs:156:31
    |
156 |     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 `aMap` should have a snake case name
  --> src/lib.rs:58:25
   |
58 |                 let mut aMap = Row::new();
   |                         ^^^^ help: convert the identifier to snake case: `a_map`
   |
   = note: `#[warn(non_snake_case)]` on by default

warning: 6 warnings emitted

    Finished test [unoptimized + debuginfo] target(s) in 4.06s
     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 ... 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 ... 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 `Result::unwrap()` on an `Err` value: InvalidRow("Не е сложено Условие")', 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("Не е сложено Условие")', 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:106:5

---- solution_test::test_csv_iterating_with_no_selection stdout ----
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidRow("Не е сложено Условие")', tests/solution_test.rs:195:48
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidRow("Не е сложено Условие")', tests/solution_test.rs:185:5

---- solution_test::test_csv_writing_without_a_selection stdout ----
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidRow("Не е сложено Условие")', tests/solution_test.rs:234:35
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidRow("Не е сложено Условие")', 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

---- solution_test::test_skip_next stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Some("test]")`,
 right: `None`', tests/solution_test.rs:114:5


failures:
    solution_test::test_csv_basic
    solution_test::test_csv_duplicate_columns
    solution_test::test_csv_iterating_with_no_selection
    solution_test::test_csv_writing_without_a_selection
    solution_test::test_csv_writing_without_any_rows
    solution_test::test_skip_next

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

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

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

Ивайло качи първо решение на 10.01.2021 23:22 (преди над 4 години)