use std::fs::File;
use std::io::{BufRead, BufReader};
use std::io::Error as IOError;
use std::path::Path;
pub fn parse_file_by_line<P, T, C>(filename: P, callback: fn(Result<String, IOError>) -> T) -> Result<C, IOError>
where P: AsRef<Path>, C: FromIterator<T> {
let fd = File::open(filename)?;
let lines = BufReader::new(fd).lines();
Ok(lines
.map(callback)
.collect())
}
pub fn parse_file_by_lines_block<P, T, C>(filename: P, lines_per_block: usize, callback: fn(Vec<String>) -> T) -> Result<C, IOError>
where P: AsRef<Path>, C: FromIterator<T> {
let fd = File::open(filename)?;
let lines = BufReader::new(fd).lines();
let mut items = Vec::new();
let mut block_lines = Vec::new();
for line in lines {
block_lines.push(line?);
if block_lines.len() == lines_per_block {
items.push(callback(block_lines));
block_lines = Vec::new();
}
}
Ok(items.into_iter().collect())
}
pub fn parse_file_by_lines_block_with_blank_lines_separator<P, T, C>(filename: P, lines_per_block: usize, separator_lines_between_block: usize, callback: fn(Vec<String>) -> T) -> Result<C, IOError>
where P: AsRef<Path>, C: FromIterator<T> {
let fd = File::open(filename)?;
let lines = BufReader::new(fd).lines();
let mut items = Vec::new();
let mut block_lines = Vec::new();
let mut lines_to_ignore = 0;
for line in lines {
if lines_to_ignore > 0 {
lines_to_ignore -= 1;
continue;
}
block_lines.push(line?);
if block_lines.len() == lines_per_block {
items.push(callback(block_lines));
block_lines = Vec::new();
lines_to_ignore = separator_lines_between_block;
}
}
Ok(items.into_iter().collect())
}
pub fn parse_digits_grid_file<P>(filename: P) -> Result<Vec<Vec<u32>>, IOError> where P: AsRef<Path> {
parse_file_by_line(filename, |line| parse_digits_grid_line(&line.unwrap()).unwrap())
}
pub fn parse_digits_grid_line(line: &str) -> Option<Vec<u32>> {
line
.chars()
.map(|c| c.to_digit(10))
.collect()
}
pub fn parse_chars_grid_file<P>(filename: P) -> Result<Vec<Vec<char>>, IOError> where P: AsRef<Path> {
parse_file_by_line(filename, |line| parse_chars_grid_line(&line.unwrap()))
}
pub fn parse_chars_grid_line(line: &str) -> Vec<char> {
line
.chars()
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_file_by_line() {
let expected = vec!['@', 'A', 'B', 'C'];
let actual: Vec<_> = parse_file_by_line(
"tests/parser/ascii.txt",
|line| line.unwrap().parse::<u8>().unwrap() as char
).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn test_parse_file_by_line_with_non_existing_file() {
let result: Result<Vec<_>, _> = parse_file_by_line("/nonexisting", |_| ());
assert_eq!(true, result.is_err());
}
#[test]
fn test_parse_digits_grid_file() {
let expected_digits = vec![vec![1 as u32, 2, 3, 4, 5, 5], vec![8, 9, 1, 2, 4, 5]];
let actual_digits = parse_digits_grid_file("tests/parser/digits.dat").unwrap();
assert_eq!(actual_digits, expected_digits);
}
#[test]
fn test_parse_digits_grid_line() {
assert_eq!(vec![1 as u32, 2, 3, 4, 5], parse_digits_grid_line("12345").unwrap());
}
#[test]
fn parse_digits_grid_line_when_it_is_not() {
assert_eq!(None, parse_digits_grid_line("This is not a digits line."));
}
}