Решение на CSV Filter от Бетина Христова
Към профила на Бетина Христова
Резултати
- 12 точки от тестове
- 0 бонус точки
- 12 точки общо
- 12 успешни тест(а)
- 3 неуспешни тест(а)
Код
use std::collections::HashMap;
use std::io::BufRead;
use std::io::Write;
use std::collections::HashSet;
type Row = HashMap<String, String>;
pub fn skip_next(input: &str, target: char) -> Option<&str> {
if input.is_empty() {
return None;
}
if input.char_indices().map(|(_, value)| value).nth(0).unwrap() == target {
return Some(&input[target.len_utf8()..input.len()]);
}
return None
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let mut end = 0_usize;
let mut contains = false;
for (_, c) in input.chars().enumerate() {
if c == target {
contains = true;
break;
} else {
end = end + c.len_utf8();
}
}
if contains {
return (&input[0..end], &input[end..input.len()]);
} else {
return (input, "");
}
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let (take_input, take_target) = take_until(input, target);
let skipped = skip_next(take_target, target);
match skipped {
Some(val) => return Some((take_input, val)),
None => return None,
}
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(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 line = String::new();
let lines = reader.read_line(&mut line);
match lines {
Ok(0) => return Err(CsvError::InvalidHeader("Invalid header!".to_string())),
Ok(_) => (),
Err(e) => return Err(CsvError::IO(e))
}
let header_values: Vec<String> = line
.split(',')
.map(|line| line.trim().to_string())
.collect();
let unique_len = &header_values.iter()
.collect::<HashSet<_>>()
.len();
let all_len = &header_values.iter().len();
if all_len != unique_len {
return Err(CsvError::InvalidHeader("Invalid header!".to_string()));
}
return Ok(Csv{ columns: header_values, selection: None, reader: reader});
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let mut line_copy: &str = line.trim();
let mut map: Row = Row::new();
let mut columns_index = 0;
let mut opened = false;
loop {
match take_and_skip(line_copy, '"') {
Some(("", rest)) => {
line_copy = rest;
opened = true;
},
Some((",", rest)) => {
line_copy = rest;
},
Some((head, rest)) => {
if !head.chars().all(|x| x == ' ' || x == ',') {
let column: String = String::from(&self.columns[columns_index]);
map.insert(column, head.to_string());
columns_index = columns_index + 1;
line_copy = rest;
opened = false;
} else {
line_copy = rest;
}
},
None => {
if opened == true {
return Err(CsvError::InvalidRow("Invalid row!".to_string()))
}
break;
}
}
}
return Ok(map);
}
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 next_value = self.next();
let last_column = String::from(self.columns.last().unwrap());
for column in &self.columns {
if column == &last_column {
if let Err(e) = write!(writer, "{}", column.to_string()) {
return Err(CsvError::IO(e));
}
break;
}
if let Err(e) = write!(writer, "{}, ", column.to_string()) {
return Err(CsvError::IO(e));
}
}
loop {
if let Err(e) = write!(writer, "{}", '\n') {
return Err(CsvError::IO(e));
}
match next_value {
Some(Ok(value)) => {
for column in &self.columns {
if column == &last_column {
if let Err(e) = write!(writer, "\"{}\"", value[column].to_string()) {
return Err(CsvError::IO(e));
}
break;
}
if let Err(e) = write!(&mut writer, "\"{}\", ", value[column].to_string()) {
return Err(CsvError::IO(e));
}
}
next_value = self.next();
},
Some(Err(e)) => return Err(e),
None => return Ok(()),
}
}
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
let mut line = String::new();
let mut line_len = self.reader.read_line(&mut line);
let mut next_row = false;
loop {
if next_row {
line = String::new();
line_len = self.reader.read_line(&mut line);
}
match line_len {
Ok(0) => return None,
Err(e) => return Some(Err(CsvError::IO(e))),
_ => ()
}
match self.parse_line(&line) {
Ok(row) => match &self.selection {
Some(function) => {
match function(&row) {
Err(e) => return Some(Err(e)),
Ok(false) => next_row = true,
Ok(true) => return Some(Ok(row))
}
},
_ => ()
},
Err(e) => return Some(Err(e))
}
}
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210111-1538662-2d1o9u/solution) Finished test [unoptimized + debuginfo] target(s) in 4.82s 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 ... 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 ... ok 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 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Timeout', tests/solution_test.rs:60:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ---- solution_test::test_csv_iterating_with_no_selection stdout ---- thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Timeout', tests/solution_test.rs:185:5 ---- solution_test::test_csv_writing_without_a_selection stdout ---- thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Timeout', tests/solution_test.rs:224:5 failures: solution_test::test_csv_basic solution_test::test_csv_iterating_with_no_selection solution_test::test_csv_writing_without_a_selection 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 коментара)
Бетина качи решение на 04.01.2021 15:15 (преди почти 5 години)
use std::collections::HashMap;
use std::io::BufRead;
use std::io::Write;
use std::collections::HashSet;
type Row = HashMap<String, String>;
pub fn skip_next(input: &str, target: char) -> Option<&str> {
if input.is_empty() {
return None;
}
if input.char_indices().map(|(_, value)| value).nth(0).unwrap() == target {
return Some(&input[target.len_utf8()..input.len()]);
}
return None
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let mut end = 0_usize;
let mut contains = false;
for (_, c) in input.chars().enumerate() {
if c == target {
contains = true;
break;
} else {
end = end + c.len_utf8();
}
}
if contains {
return (&input[0..end], &input[end..input.len()]);
} else {
return (input, "");
}
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let (take_input, take_target) = take_until(input, target);
let skipped = skip_next(take_target, target);
match skipped {
Some(val) => return Some((take_input, val)),
None => return None,
}
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(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 line = String::new();
let lines = reader.read_line(&mut line);
match lines {
Ok(0) => return Err(CsvError::InvalidHeader("Invalid header!".to_string())),
Ok(_) => (),
Err(e) => return Err(CsvError::IO(e))
}
let header_values: Vec<String> = line
.split(',')
.map(|line| line.trim().to_string())
.collect();
let unique_len = &header_values.iter()
.collect::<HashSet<_>>()
.len();
let all_len = &header_values.iter().len();
if all_len != unique_len {
return Err(CsvError::InvalidHeader("Invalid header!".to_string()));
}
return Ok(Csv{ columns: header_values, selection: None, reader: reader});
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let mut line_copy: &str = line.trim();
let mut map: Row = Row::new();
let mut columns_index = 0;
let mut opened = false;
loop {
match take_and_skip(line_copy, '"') {
Some(("", rest)) => {
line_copy = rest;
opened = true;
},
Some((",", rest)) => {
line_copy = rest;
},
Some((head, rest)) => {
if !head.chars().all(|x| x == ' ' || x == ',') {
let column: String = String::from(&self.columns[columns_index]);
map.insert(column, head.to_string());
columns_index = columns_index + 1;
line_copy = rest;
opened = false;
} else {
line_copy = rest;
}
},
None => {
if opened == true {
return Err(CsvError::InvalidRow("Invalid row!".to_string()))
}
break;
}
}
}
return Ok(map);
}
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 next_value = self.next();
let last_column = String::from(self.columns.last().unwrap());
for column in &self.columns {
if column == &last_column {
- write!(writer, "{}", column.to_string());
+ if let Err(e) = write!(writer, "{}", column.to_string()) {
+ return Err(CsvError::IO(e));
+ }
+
break;
}
- write!(writer, "{}, ", column.to_string());
+ if let Err(e) = write!(writer, "{}, ", column.to_string()) {
+ return Err(CsvError::IO(e));
+ }
}
loop {
- write!(writer, "{}", '\n');
+ if let Err(e) = write!(writer, "{}", '\n') {
+ return Err(CsvError::IO(e));
+ }
match next_value {
Some(Ok(value)) => {
for column in &self.columns {
if column == &last_column {
- write!(writer, "\"{}\"", value[column].to_string());
+ if let Err(e) = write!(writer, "\"{}\"", value[column].to_string()) {
+ return Err(CsvError::IO(e));
+ }
break;
}
- write!(&mut writer, "\"{}\", ", value[column].to_string());
+ if let Err(e) = write!(&mut writer, "\"{}\", ", value[column].to_string()) {
+ return Err(CsvError::IO(e));
+ }
}
next_value = self.next();
},
Some(Err(e)) => return Err(e),
None => return Ok(()),
}
}
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
let mut line = String::new();
let mut line_len = self.reader.read_line(&mut line);
let mut next_row = false;
loop {
if next_row {
line = String::new();
line_len = self.reader.read_line(&mut line);
}
match line_len {
Ok(0) => return None,
Err(e) => return Some(Err(CsvError::IO(e))),
_ => ()
}
match self.parse_line(&line) {
Ok(row) => match &self.selection {
Some(function) => {
match function(&row) {
Err(e) => return Some(Err(e)),
Ok(false) => next_row = true,
Ok(true) => return Some(Ok(row))
}
},
_ => ()
},
Err(e) => return Some(Err(e))
}
}
}
}
+