Решение на CSV Filter от Андрей
Резултати
- 15 точки от тестове
- 0 бонус точки
- 15 точки общо
- 15 успешни тест(а)
- 0 неуспешни тест(а)
Код
use std::collections::{HashMap, HashSet};
use std::io::{self, BufRead, Write};
use std::num;
pub fn skip_next(input: &str, target: char) -> Option<&str> {
let mut chars = input.chars();
let next_char = chars.next()?;
if next_char == target {
Some(chars.as_str())
} else {
None
}
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
if let Some((index, _)) = input.char_indices().find(|(_, c)| *c == target) {
input.split_at(index)
} else {
(input, "")
}
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let (before, after) = take_until(input, target);
let after = skip_next(after, target)?;
Some((before, after))
}
#[derive(Debug)]
pub enum CsvError {
IO(io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
impl From<io::Error> for CsvError {
fn from(source: io::Error) -> Self {
Self::IO(source)
}
}
impl From<num::ParseIntError> for CsvError {
fn from(source: num::ParseIntError) -> Self {
Self::ParseError(source.to_string())
}
}
pub 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 first_line = String::new();
let read_bytes = reader.read_line(&mut first_line)?;
if read_bytes == 0 {
return Err(CsvError::InvalidHeader(first_line));
}
let columns: Vec<_> = first_line.split(",").map(str::trim).map(str::to_owned).collect();
if columns.iter().collect::<HashSet<_>>().len() != columns.len() {
return Err(CsvError::InvalidHeader(first_line));
}
Ok(Csv { reader, columns, selection: None })
}
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 parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let column_count = self.columns.len();
let mut row = HashMap::new();
let mut unparsed_row = line.trim();
for (i, column) in self.columns.iter().enumerate() {
macro_rules! error {
() => { CsvError::InvalidRow(unparsed_row.to_owned()) }
}
let remainder = skip_next(unparsed_row, '"').ok_or_else(|| error!())?;
let (body, remainder) = take_and_skip(remainder, '"').ok_or_else(|| error!())?;
row.insert(column.clone(), body.to_owned());
unparsed_row = remainder.trim();
if i + 1 < column_count {
unparsed_row = skip_next(unparsed_row, ',').ok_or_else(|| error!())?.trim();
} else if unparsed_row.len() > 0 {
return Err(error!());
}
}
Ok(row)
}
pub fn write_to<W: Write>(mut self, mut writer: W) -> Result<(), CsvError> {
writeln!(writer, "{}", self.columns.join(", "))?;
while let Some(row_result) = self.next() {
let row = row_result?;
let entries: Vec<_> = self.columns.iter().map(|c| format!("{:?}", row[c])).collect();
writeln!(writer, "{}", entries.join(", "))?;
}
Ok(())
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let mut next_line = String::new();
let row_result = match self.reader.read_line(&mut next_line) {
Ok(0) => return None,
Err(e) => return Some(Err(CsvError::IO(e))),
Ok(_) => self.parse_line(&next_line),
};
let callback = match &self.selection {
None => return Some(row_result),
Some(callback) => callback,
};
match row_result {
Err(e) => return Some(Err(e)),
Ok(row) => {
match callback(&row) {
Ok(true) => return Some(Ok(row)),
Ok(false) => continue,
Err(e) => return Some(Err(e)),
}
}
}
}
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210111-1538662-1dn4xv1/solution) Finished test [unoptimized + debuginfo] target(s) in 4.46s 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 коментара)
Андрей качи решение на 20.12.2020 18:36 (преди почти 5 години)
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::io::{self, BufRead, Write};
use std::num;
use std::str::FromStr;
pub fn skip_next(input: &str, target: char) -> Option<&str> {
- let mut chars = input.trim().chars();
+ let mut chars = input.chars();
let next_char = chars.next()?;
if next_char == target {
- Some(chars.as_str().trim())
+ Some(chars.as_str())
} else {
None
}
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
- if let Some((index, _)) = input.trim().char_indices().find(|(_, c)| *c == target) {
+ if let Some((index, _)) = input.char_indices().find(|(_, c)| *c == target) {
input.split_at(index)
} else {
(input, "")
}
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let (before, after) = take_until(input, target);
let after = skip_next(after, target)?;
Some((before, after))
}
#[derive(Debug)]
pub enum CsvError {
IO(io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
impl From<io::Error> for CsvError {
fn from(source: io::Error) -> Self {
Self::IO(source)
}
}
impl From<num::ParseIntError> for CsvError {
fn from(source: num::ParseIntError) -> Self {
Self::ParseError(source.to_string())
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd)]
pub struct Date {
pub year: u32,
pub month: u8,
pub day: u8,
}
impl Date {
pub fn new(year: u32, month: u8, day: u8) -> Self {
Date { year, month, day }
}
}
impl Ord for Date {
fn cmp(&self, other: &Self) -> Ordering {
(self.year, self.month, self.day).
cmp(&(other.year, other.month, other.day))
}
}
impl FromStr for Date {
type Err = CsvError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split("-");
let year = parts.next().ok_or_else(|| CsvError::ParseError(s.to_owned()))?.parse()?;
let month = parts.next().ok_or_else(|| CsvError::ParseError(s.to_owned()))?.parse()?;
let day = parts.next().ok_or_else(|| CsvError::ParseError(s.to_owned()))?.parse()?;
if parts.next().is_some() {
return Err(CsvError::ParseError(s.to_owned()));
}
Ok(Date { year, month, day })
}
}
pub 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 first_line = String::new();
let read_bytes = reader.read_line(&mut first_line)?;
if read_bytes == 0 {
return Err(CsvError::InvalidHeader(first_line));
}
let columns: Vec<_> = first_line.split(",").map(str::trim).map(str::to_owned).collect();
if columns.iter().collect::<HashSet<_>>().len() != columns.len() {
return Err(CsvError::InvalidHeader(first_line));
}
Ok(Csv { reader, columns, selection: None })
}
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 parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let column_count = self.columns.len();
let mut row = HashMap::new();
let mut unparsed_row = line.trim();
for (i, column) in self.columns.iter().enumerate() {
macro_rules! error {
() => { CsvError::InvalidRow(unparsed_row.to_owned()) }
}
let remainder = skip_next(unparsed_row, '"').ok_or_else(|| error!())?;
let (body, remainder) = take_and_skip(remainder, '"').ok_or_else(|| error!())?;
row.insert(column.clone(), body.to_owned());
- unparsed_row = remainder;
+ unparsed_row = remainder.trim();
if i + 1 < column_count {
- unparsed_row = skip_next(remainder, ',').ok_or_else(|| error!())?;
+ unparsed_row = skip_next(unparsed_row, ',').ok_or_else(|| error!())?.trim();
} else if unparsed_row.len() > 0 {
return Err(error!());
}
}
Ok(row)
}
pub fn write_to<W: Write>(mut self, mut writer: W) -> Result<(), CsvError> {
writeln!(writer, "{}", self.columns.join(", "))?;
while let Some(row_result) = self.next() {
let row = row_result?;
let entries: Vec<_> = self.columns.iter().map(|c| format!("{:?}", row[c])).collect();
writeln!(writer, "{}", entries.join(", "))?;
}
Ok(())
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let mut next_line = String::new();
let row_result = match self.reader.read_line(&mut next_line) {
Ok(0) => return None,
Err(e) => return Some(Err(CsvError::IO(e))),
Ok(_) => self.parse_line(&next_line),
};
let callback = match &self.selection {
None => return Some(row_result),
Some(callback) => callback,
};
match row_result {
Err(e) => return Some(Err(e)),
Ok(row) => {
match callback(&row) {
Ok(true) => return Some(Ok(row)),
Ok(false) => continue,
Err(e) => return Some(Err(e)),
}
}
}
}
}
}
Андрей качи решение на 21.12.2020 09:21 (преди почти 5 години)
-use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::io::{self, BufRead, Write};
use std::num;
-use std::str::FromStr;
pub fn skip_next(input: &str, target: char) -> Option<&str> {
let mut chars = input.chars();
let next_char = chars.next()?;
if next_char == target {
Some(chars.as_str())
} else {
None
}
}
pub fn take_until(input: &str, target: char) -> (&str, &str) {
if let Some((index, _)) = input.char_indices().find(|(_, c)| *c == target) {
input.split_at(index)
} else {
(input, "")
}
}
pub fn take_and_skip(input: &str, target: char) -> Option<(&str, &str)> {
let (before, after) = take_until(input, target);
let after = skip_next(after, target)?;
Some((before, after))
}
#[derive(Debug)]
pub enum CsvError {
IO(io::Error),
ParseError(String),
InvalidHeader(String),
InvalidRow(String),
InvalidColumn(String),
}
impl From<io::Error> for CsvError {
fn from(source: io::Error) -> Self {
Self::IO(source)
}
}
impl From<num::ParseIntError> for CsvError {
fn from(source: num::ParseIntError) -> Self {
Self::ParseError(source.to_string())
- }
-}
-
-#[derive(Debug, PartialEq, Eq, PartialOrd)]
-pub struct Date {
- pub year: u32,
- pub month: u8,
- pub day: u8,
-}
-
-impl Date {
- pub fn new(year: u32, month: u8, day: u8) -> Self {
- Date { year, month, day }
- }
-}
-
-impl Ord for Date {
- fn cmp(&self, other: &Self) -> Ordering {
- (self.year, self.month, self.day).
- cmp(&(other.year, other.month, other.day))
- }
-}
-
-impl FromStr for Date {
- type Err = CsvError;
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- let mut parts = s.split("-");
-
- let year = parts.next().ok_or_else(|| CsvError::ParseError(s.to_owned()))?.parse()?;
- let month = parts.next().ok_or_else(|| CsvError::ParseError(s.to_owned()))?.parse()?;
- let day = parts.next().ok_or_else(|| CsvError::ParseError(s.to_owned()))?.parse()?;
-
- if parts.next().is_some() {
- return Err(CsvError::ParseError(s.to_owned()));
- }
-
- Ok(Date { year, month, day })
}
}
pub 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 first_line = String::new();
let read_bytes = reader.read_line(&mut first_line)?;
if read_bytes == 0 {
return Err(CsvError::InvalidHeader(first_line));
}
let columns: Vec<_> = first_line.split(",").map(str::trim).map(str::to_owned).collect();
if columns.iter().collect::<HashSet<_>>().len() != columns.len() {
return Err(CsvError::InvalidHeader(first_line));
}
Ok(Csv { reader, columns, selection: None })
}
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 parse_line(&mut self, line: &str) -> Result<Row, CsvError> {
let column_count = self.columns.len();
let mut row = HashMap::new();
let mut unparsed_row = line.trim();
for (i, column) in self.columns.iter().enumerate() {
macro_rules! error {
() => { CsvError::InvalidRow(unparsed_row.to_owned()) }
}
let remainder = skip_next(unparsed_row, '"').ok_or_else(|| error!())?;
let (body, remainder) = take_and_skip(remainder, '"').ok_or_else(|| error!())?;
row.insert(column.clone(), body.to_owned());
unparsed_row = remainder.trim();
if i + 1 < column_count {
unparsed_row = skip_next(unparsed_row, ',').ok_or_else(|| error!())?.trim();
} else if unparsed_row.len() > 0 {
return Err(error!());
}
}
Ok(row)
}
pub fn write_to<W: Write>(mut self, mut writer: W) -> Result<(), CsvError> {
writeln!(writer, "{}", self.columns.join(", "))?;
while let Some(row_result) = self.next() {
let row = row_result?;
let entries: Vec<_> = self.columns.iter().map(|c| format!("{:?}", row[c])).collect();
writeln!(writer, "{}", entries.join(", "))?;
}
Ok(())
}
}
impl<R: BufRead> Iterator for Csv<R> {
type Item = Result<Row, CsvError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let mut next_line = String::new();
let row_result = match self.reader.read_line(&mut next_line) {
Ok(0) => return None,
Err(e) => return Some(Err(CsvError::IO(e))),
Ok(_) => self.parse_line(&next_line),
};
let callback = match &self.selection {
None => return Some(row_result),
Some(callback) => callback,
};
match row_result {
Err(e) => return Some(Err(e)),
Ok(row) => {
match callback(&row) {
Ok(true) => return Some(Ok(row)),
Ok(false) => continue,
Err(e) => return Some(Err(e)),
}
}
}
}
}
}