Решение на CSV Filter от Антонина Ускова

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

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

Резултати

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

Код

pub fn skip_next(input: &str, target: char) -> Option<&str> {
let mut iter = input.chars();
match iter.next() {
Some(ch) if ch == target => Some(iter.as_str()),
_ => None,
}
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let index = input.find(target).unwrap_or(input.len());
(&input[0..index], &input[index..input.len()])
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
match take_until(input, target) {
(_, "") => None,
(first, second) => Some((
first,
skip_next(second, target).expect("Second in input pair doesn't contain target."),
)),
}
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
use std::{error::Error, fmt};
impl Error for CsvError {}
impl fmt::Display for CsvError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<std::io::Error> for CsvError {
fn from(e: std::io::Error) -> Self {
CsvError::IO(e)
}
}
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::collections::HashSet;
use std::io::Write;
impl<R: BufRead> Csv<R> {
pub fn new(mut reader: R) -> Result<Self, CsvError> {
let mut buf = String::new();
let read_bytes = reader.read_line(&mut buf);
if read_bytes.is_err() {
return Err(read_bytes.err().unwrap().into());
}
if read_bytes.unwrap() == 0 || buf.as_str().trim() == ""{
// IO error -> CsvError
return Err(CsvError::InvalidHeader(String::from("Empty header")));
}
let mut columns: Vec<String> = Vec::new();
let mut header = buf.as_str();
while let Some((first, second)) = take_and_skip(header, ',') {
columns.push(String::from(first.trim()));
header = second;
}
if header != "" {
columns.push(String::from(header.trim()));
}
let hash_set: HashSet<String> = columns.clone().into_iter().collect();
if columns.len() != hash_set.len() {
return Err(CsvError::InvalidHeader(String::from(
"Found duplicates in header",
)));
}
Ok(Self {
columns,
reader,
selection: None,
})
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let mut line = line.trim();
if line.is_empty() {
return Err(CsvError::InvalidRow(String::from("Row is empty")));
}
let mut row = Row::new();
let mut column_iter = self.columns.iter();
let mut trailing_comma = false;
while !line.is_empty() {
trailing_comma = false;
let column_name = if let Some(column_name) = column_iter.next() {
column_name.clone()
} else {
return Err(CsvError::InvalidRow(String::from(
"More values than columns",
)));
};
line = if let Some(value) = skip_next(line.trim(), '"') {
value
} else {
return Err(CsvError::InvalidRow(String::from(
"No trailing \" for value in row",
)));
};
let (value, next_value_for_line) = take_until(line, '"');
if value == line {
return Err(CsvError::InvalidRow(String::from(
"Value doesn't end with \"",
)));
};
let column_value = String::from(value);
row.insert(column_name, column_value);
let removed_quote = skip_next(next_value_for_line, '"').expect("Should start with \"");
line = removed_quote.trim();
if !line.is_empty() {
line = if let Some(removed_comma) = skip_next(line, ',') {
trailing_comma = true;
removed_comma.trim()
} else {
return Err(CsvError::InvalidRow(String::from(
"No trailing comma in non empty row",
)));
}
}
}
if trailing_comma {
return Err(CsvError::InvalidRow(String::from(
"Trailing comma at the end of the row",
)));
}
if row.len() != self.columns.len() {
return Err(CsvError::InvalidRow(String::from(
"More columns than values",
)));
}
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, mut writer: W) -> Result<(), CsvError> {
for i in 0..self.columns.len() {
writer.write(self.columns[i].as_bytes())?;
if i != (self.columns.len() - 1) {
writer.write(", ".as_bytes())?;
}
}
writer.write("\n".as_bytes())?;
let columns = self.columns.clone();
for row in self {
let row = match row {
Err(err) => return Err(err.into()),
Ok(r) => r,
};
for i in 0..columns.len() {
writer.write("\"".as_bytes())?;
writer.write(row[&columns[i]].as_bytes())?;
writer.write("\"".as_bytes())?;
if i != (columns.len() - 1) {
writer.write(", ".as_bytes())?;
}
}
writer.write("\n".as_bytes())?;
}
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();
match self.reader.read_line(&mut line) {
Err(e) => return Some(Err(e.into())),
Ok(size) if size == 0 => return None,
_ => (),
};
let row = match self.parse_line(line.as_str()) {
Err(e) => return Some(Err(e.into())),
Ok(row) => row,
};
let res = match &self.selection {
Some(sel) => sel(&row),
None => Ok(true),
};
match res {
Err(e) => return Some(Err(e)),
Ok(b) if b == true => return Some(Ok(row)),
_ => self.next(),
}
}
}

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

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

test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

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

Антонина качи първо решение на 11.01.2021 16:37 (преди над 4 години)

Антонина качи решение на 11.01.2021 16:49 (преди над 4 години)

pub fn skip_next(input: &str, target: char) -> Option<&str> {
let mut iter = input.chars();
match iter.next() {
Some(ch) if ch == target => Some(iter.as_str()),
_ => None,
}
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let index = input.find(target).unwrap_or(input.len());
(&input[0..index], &input[index..input.len()])
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
match take_until(input, target) {
(_, "") => None,
(first, second) => Some((
first,
skip_next(second, target).expect("Second in input pair doesn't contain target."),
)),
}
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
use std::{error::Error, fmt};
impl Error for CsvError {}
impl fmt::Display for CsvError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<std::io::Error> for CsvError {
fn from(e: std::io::Error) -> Self {
CsvError::IO(e)
}
}
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::collections::HashSet;
use std::io::Write;
impl<R: BufRead> Csv<R> {
pub fn new(mut reader: R) -> Result<Self, CsvError> {
let mut buf = String::new();
let read_bytes = reader.read_line(&mut buf);
if read_bytes.is_err() {
return Err(read_bytes.err().unwrap().into());
}
- if read_bytes.unwrap() == 0 {
+ if read_bytes.unwrap() == 0 || buf.as_str().trim() == ""{
// IO error -> CsvError
return Err(CsvError::InvalidHeader(String::from("Empty header")));
}
let mut columns: Vec<String> = Vec::new();
let mut header = buf.as_str();
while let Some((first, second)) = take_and_skip(header, ',') {
columns.push(String::from(first.trim()));
header = second;
}
if header != "" {
columns.push(String::from(header.trim()));
}
let hash_set: HashSet<String> = columns.clone().into_iter().collect();
if columns.len() != hash_set.len() {
return Err(CsvError::InvalidHeader(String::from(
"Found duplicates in header",
)));
}
Ok(Self {
columns,
reader,
selection: None,
})
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let mut line = line.trim();
if line.is_empty() {
return Err(CsvError::InvalidRow(String::from("Row is empty")));
}
let mut row = Row::new();
let mut column_iter = self.columns.iter();
while !line.is_empty() {
let column_name = if let Some(column_name) = column_iter.next() {
column_name.clone()
} else {
return Err(CsvError::InvalidRow(String::from(
"More values than columns",
)));
};
line = if let Some(value) = skip_next(line.trim(), '"') {
value
} else {
return Err(CsvError::InvalidRow(String::from(
"No trailing \" for value in row",
)));
};
let (value, next_value_for_line) = take_until(line, '"');
if value == line {
return Err(CsvError::InvalidRow(String::from(
"Value doesn't end with \"",
)));
};
let column_value = String::from(value);
row.insert(column_name, column_value);
let removed_quote = skip_next(next_value_for_line, '"').expect("Should start with \"");
line = removed_quote.trim();
if !line.is_empty() {
line = if let Some(removed_comma) = skip_next(line, ',') {
removed_comma.trim()
} else {
return Err(CsvError::InvalidRow(String::from(
"No trailing comma in non empty row",
)));
}
}
}
if row.len() != self.columns.len() {
return Err(CsvError::InvalidRow(String::from(
"More columns than values",
)));
}
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, mut writer: W) -> Result<(), CsvError> {
for i in 0..self.columns.len() {
writer.write(self.columns[i].as_bytes())?;
if i != (self.columns.len() - 1) {
writer.write(", ".as_bytes())?;
}
}
writer.write("\n".as_bytes())?;
let columns = self.columns.clone();
for row in self {
let row = match row {
Err(err) => return Err(err.into()),
Ok(r) => r,
};
for i in 0..columns.len() {
writer.write("\"".as_bytes())?;
writer.write(row[&columns[i]].as_bytes())?;
writer.write("\"".as_bytes())?;
if i != (columns.len() - 1) {
writer.write(", ".as_bytes())?;
}
}
writer.write("\n".as_bytes())?;
}
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();
match self.reader.read_line(&mut line) {
Err(e) => return Some(Err(e.into())),
Ok(size) if size == 0 => return None,
_ => (),
};
let row = match self.parse_line(line.as_str()) {
Err(e) => return Some(Err(e.into())),
Ok(row) => row,
};
let res = match &self.selection {
Some(sel) => sel(&row),
None => Ok(true),
};
match res {
Err(e) => return Some(Err(e)),
Ok(b) if b == true => return Some(Ok(row)),
_ => self.next(),
}
}
}

Антонина качи решение на 11.01.2021 16:53 (преди над 4 години)

pub fn skip_next(input: &str, target: char) -> Option<&str> {
let mut iter = input.chars();
match iter.next() {
Some(ch) if ch == target => Some(iter.as_str()),
_ => None,
}
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
let index = input.find(target).unwrap_or(input.len());
(&input[0..index], &input[index..input.len()])
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
match take_until(input, target) {
(_, "") => None,
(first, second) => Some((
first,
skip_next(second, target).expect("Second in input pair doesn't contain target."),
)),
}
}
#[derive(Debug)]
pub enum CsvError {
IO(std::io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
use std::{error::Error, fmt};
impl Error for CsvError {}
impl fmt::Display for CsvError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<std::io::Error> for CsvError {
fn from(e: std::io::Error) -> Self {
CsvError::IO(e)
}
}
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::collections::HashSet;
use std::io::Write;
impl<R: BufRead> Csv<R> {
pub fn new(mut reader: R) -> Result<Self, CsvError> {
let mut buf = String::new();
let read_bytes = reader.read_line(&mut buf);
if read_bytes.is_err() {
return Err(read_bytes.err().unwrap().into());
}
if read_bytes.unwrap() == 0 || buf.as_str().trim() == ""{
// IO error -> CsvError
return Err(CsvError::InvalidHeader(String::from("Empty header")));
}
let mut columns: Vec<String> = Vec::new();
let mut header = buf.as_str();
while let Some((first, second)) = take_and_skip(header, ',') {
columns.push(String::from(first.trim()));
header = second;
}
if header != "" {
columns.push(String::from(header.trim()));
}
let hash_set: HashSet<String> = columns.clone().into_iter().collect();
if columns.len() != hash_set.len() {
return Err(CsvError::InvalidHeader(String::from(
"Found duplicates in header",
)));
}
Ok(Self {
columns,
reader,
selection: None,
})
}
pub fn parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let mut line = line.trim();
if line.is_empty() {
return Err(CsvError::InvalidRow(String::from("Row is empty")));
}
let mut row = Row::new();
let mut column_iter = self.columns.iter();
+ let mut trailing_comma = false;
while !line.is_empty() {
+ trailing_comma = false;
let column_name = if let Some(column_name) = column_iter.next() {
column_name.clone()
} else {
return Err(CsvError::InvalidRow(String::from(
"More values than columns",
)));
};
line = if let Some(value) = skip_next(line.trim(), '"') {
value
} else {
return Err(CsvError::InvalidRow(String::from(
"No trailing \" for value in row",
)));
};
let (value, next_value_for_line) = take_until(line, '"');
if value == line {
return Err(CsvError::InvalidRow(String::from(
"Value doesn't end with \"",
)));
};
let column_value = String::from(value);
row.insert(column_name, column_value);
let removed_quote = skip_next(next_value_for_line, '"').expect("Should start with \"");
line = removed_quote.trim();
if !line.is_empty() {
line = if let Some(removed_comma) = skip_next(line, ',') {
+ trailing_comma = true;
removed_comma.trim()
} else {
return Err(CsvError::InvalidRow(String::from(
"No trailing comma in non empty row",
)));
}
}
+ }
+
+ if trailing_comma {
+ return Err(CsvError::InvalidRow(String::from(
+ "Trailing comma at the end of the row",
+ )));
}
if row.len() != self.columns.len() {
return Err(CsvError::InvalidRow(String::from(
"More columns than values",
)));
}
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, mut writer: W) -> Result<(), CsvError> {
for i in 0..self.columns.len() {
writer.write(self.columns[i].as_bytes())?;
if i != (self.columns.len() - 1) {
writer.write(", ".as_bytes())?;
}
}
writer.write("\n".as_bytes())?;
let columns = self.columns.clone();
for row in self {
let row = match row {
Err(err) => return Err(err.into()),
Ok(r) => r,
};
for i in 0..columns.len() {
writer.write("\"".as_bytes())?;
writer.write(row[&columns[i]].as_bytes())?;
writer.write("\"".as_bytes())?;
if i != (columns.len() - 1) {
writer.write(", ".as_bytes())?;
}
}
writer.write("\n".as_bytes())?;
}
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();
match self.reader.read_line(&mut line) {
Err(e) => return Some(Err(e.into())),
Ok(size) if size == 0 => return None,
_ => (),
};
let row = match self.parse_line(line.as_str()) {
Err(e) => return Some(Err(e.into())),
Ok(row) => row,
};
let res = match &self.selection {
Some(sel) => sel(&row),
None => Ok(true),
};
match res {
Err(e) => return Some(Err(e)),
Ok(b) if b == true => return Some(Ok(row)),
_ => self.next(),
}
}
}