Решение на CSV Filter от Биляна Йорданова

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

Към профила на Биляна Йорданова

Резултати

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

Код

pub fn skip_next(input: &str, target: char) -> Option<&str> {
let mut chars = input.chars();
if chars.next().unwrap() == target {
return Some(chars.as_str());
}
None
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
if input.find(target) == None {
return (input, "");
}
let mut char_indices = input.char_indices();
let char_len: usize;
loop {
let value = char_indices.next().unwrap();
if value.1 == target {
char_len = value.1.len_utf8();
break;
}
}
let pos = match char_indices.next() {
Some(ch) => ch.0,
None => input.len(),
};
return input.split_at(pos - char_len);
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
input.find(target)?;
let (first, second) = take_until(input, target);
Option::Some((
first,
skip_next(second, second.chars().next().unwrap()).unwrap(),
))
}
#[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 buf = String::new();
match reader.read_line(&mut buf) {
Err(e) => return Err(CsvError::IO(e)),
Ok(0) => return Err(CsvError::InvalidHeader(String::from("No header"))),
_ => (),
}
let columns: Vec<String> = buf
.split(',')
.map(|column| column.trim().to_string())
.collect();
for i in 0..columns.len() {
for j in i + 1..columns.len() {
if columns[i] == columns[j] {
return Err(CsvError::InvalidHeader(String::from(
"Duplicate column names",
)));
}
}
}
Ok(Csv {
columns,
reader,
selection: None,
})
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let line = line.trim();
let mut row = Row::new();
let mut line = match skip_next(line, '"') {
Some(s) => s,
None => {
return Err(CsvError::InvalidRow(String::from(
"Invalid beginning of row",
)))
}
};
for (i, col) in self.columns.iter().enumerate() {
let (value, remainder) = match take_and_skip(line, '"') {
Some(s) => s,
None => {
return Err(CsvError::InvalidRow(String::from(
"Missing closing quotation mark",
)))
}
};
line = remainder;
row.insert(col.clone(), value.to_string());
if i == self.columns.len() - 1 {
if !line.is_empty() {
return Err(CsvError::InvalidRow(String::from(
"Not enough values in row",
)));
}
} else {
let (delim, remainder) = match take_and_skip(line, '"') {
Some(s) => s,
None => {
return Err(CsvError::InvalidRow(String::from(
"Missing opening quotation mark",
)))
}
};
line = remainder;
if delim.trim() != "," {
return Err(CsvError::InvalidRow(String::from("Delimiter error")));
}
}
}
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 cols = self.columns.join(", ") + "\n";
if let Err(e) = writer.write(cols.as_bytes()) {
return Err(CsvError::IO(e));
}
while let Some(row) = self.next() {
let row = row?;
let mut line = String::new();
for (i, col) in self.columns.iter().enumerate() {
line += "\"";
line += row.get(col).unwrap();
line += "\"";
if i != self.columns.len() - 1 {
line += ", ";
}
}
line += "\n";
if let Err(e) = writer.write(line.as_bytes()) {
return Err(CsvError::IO(e));
}
}
Ok(())
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let mut line = String::new();
match self.reader.read_line(&mut line) {
Err(e) => return Some(Err(CsvError::IO(e))),
Ok(0) => return None,
_ => (),
};
let row = match self.parse_line(&line) {
Ok(row) => row,
Err(e) => return Some(Err(e)),
};
if self.selection.is_none() {
return Some(Ok(row));
}
match self.selection.as_ref().unwrap()(&row) {
Ok(true) => return Some(Ok(row)),
Ok(false) => (),
Err(e) => return Some(Err(e)),
}
}
}
}

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

Compiling solution v0.1.0 (/tmp/d20210111-1538662-10yc68q/solution)
    Finished test [unoptimized + debuginfo] target(s) in 4.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 ... ok
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 ... ok
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 ... FAILED
test solution_test::test_take_and_skip ... ok
test solution_test::test_take_until ... ok

failures:

---- solution_test::test_skip_next stdout ----
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:3:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    solution_test::test_skip_next

test result: FAILED. 14 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

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

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

Биляна качи първо решение на 08.01.2021 19:39 (преди почти 5 години)