use std::io::{BufRead, BufReader};
use std::iter::Iterator;
pub struct Blocks<F> {
buffer: BufReader<F>,
last_line: String,
tolerable: usize,
started: bool,
comments: Vec<String>,
}
#[deprecated(since = "0.2.1", note = "please, use `Blocks` instead.")]
pub type Block<F> = Blocks<F>;
impl<F> Blocks<F>
where
F: ::std::io::Read,
{
pub fn new(tolerable: usize, stream: F) -> Self {
Blocks {
buffer: BufReader::new(stream),
last_line: String::new(),
tolerable,
started: false,
comments: Vec::new(),
}
}
pub fn new_with_comments(tolerable: usize, stream: F, comments: &[String]) -> Self {
Blocks {
buffer: BufReader::new(stream),
last_line: String::new(),
tolerable,
started: false,
comments: comments.to_owned(),
}
}
}
impl<F> Iterator for Blocks<F>
where
std::io::BufReader<F>: std::io::BufRead,
{
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
let mut block = String::new();
let mut blank_counter = 0; let comment_as_blank = !self.comments.is_empty();
loop {
let mut line = String::new();
if self.last_line.is_empty() {
match self.buffer.read_line(&mut line) {
Ok(0) => {
break;
}
Ok(_) => {
if !self.started {
if is_blank(&line) || is_comment(&line, &self.comments) {
continue;
} else {
self.started = true;
}
}
}
Err(ref e) => {
panic!("{}", e.to_string());
}
}
} else {
line = self.last_line.clone();
self.last_line = String::new();
}
if is_blank(&line) || is_comment(&line, &self.comments) {
block += &line;
blank_counter += 1;
} else {
if blank_counter >= self.tolerable {
if self.tolerable == 0 {
block += &line;
} else {
self.last_line = line.clone();
}
break;
} else {
block += &line;
}
blank_counter = 0;
}
}
let block_is_garbage = if comment_as_blank {
let mut all_garbage = true;
for line in block.lines() {
let line = line.to_string();
if !is_comment(&line, &self.comments) && !is_blank(&line) {
all_garbage = false;
break;
}
}
all_garbage
} else {
is_blank(&block)
};
if block_is_garbage {
None
} else {
Some(block)
}
}
}
#[deprecated(
since = "0.2.1",
note = "please, use `count_blocks` instead."
)]
pub fn blank_lines<S: ::std::io::Read>(tolerance: usize, stream: S) -> usize {
count_blocks(tolerance, stream)
}
pub fn count_blocks<S>(tolerance: usize, stream: S) -> usize
where
S: ::std::io::Read,
{
let mut file = BufReader::new(stream);
let mut blank_counter: usize = tolerance;
let mut final_counter: usize = 0;
loop {
let mut line = String::new();
match file.read_line(&mut line) {
Ok(0) => {
break;
} Ok(_) => {}
Err(e) => {
panic!("{}", e.to_string());
}
}
if is_blank(&line) {
blank_counter += 1;
} else {
if blank_counter >= tolerance {
final_counter += 1;
}
blank_counter = 0;
}
}
final_counter
}
fn is_comment(s: &str, comments: &[String]) -> bool {
if comments.is_empty() {
return false;
}
let mut s = s.to_string();
loop {
let c0 = s.chars().nth(0);
if c0.is_none() {
break;
}
let c0 = c0.unwrap().to_string();
if is_blank(&c0) {
let _ = s.remove(0);
} else {
break;
}
}
for comment in comments {
if s.starts_with(comment) {
return true;
}
}
false
}
fn is_blank(s: &str) -> bool {
const EMPTY_CHARS: &[char] = &[' ', '\t', '\n', '\r'];
let mut some_non_blank_char = false;
for c in s.chars() {
let mut some_blank_char = false;
for e in EMPTY_CHARS {
if c == *e {
some_blank_char = true;
break;
}
}
if !some_blank_char {
some_non_blank_char = true;
break;
}
}
!some_non_blank_char
}
pub fn clean(s: &str) -> String {
remove_blank_at_end(&remove_blank_at_beginning(&s))
}
pub fn clean_all_blank(s: &str) -> String {
let mut t = String::new();
for line in s.lines() {
if !is_blank(&line.to_string()) {
t += &line;
}
}
t
}
fn remove_blank_at_beginning(s: &str) -> String {
let mut s = s.to_string();
for line in s.clone().lines() {
if is_blank(&line.to_string()) {
if line.is_empty() {
let _ = s.remove(0);
} else {
for _ in line.chars() {
let _ = s.remove(0);
}
}
} else {
break;
}
}
s
}
fn remove_blank_at_end(s: &str) -> String {
let mut s = s.to_string();
while let Some(line) = s.clone().lines().last() {
if is_blank(&line.to_string()) {
if line.is_empty() {
let _ = s.pop();
} else {
for _ in line.chars() {
let _ = s.pop();
}
}
} else {
break;
}
}
s
}
#[test]
fn blank_is_blank() {
let s = " \t\r\n".to_string();
if !is_blank(&s) {
panic!("is_blank(&String) problem.");
}
}
#[test]
fn empty_is_blank() {
let s = "".to_string();
if !is_blank(&s) {
panic!("is_blank(&String) problem.");
}
}
#[test]
#[should_panic(expected = "is_blank(&String) problem.")]
fn non_blank_is_non_blank() {
let s = " \t\ra\n".to_string();
if !is_blank(&s) {
panic!("is_blank(&String) problem.");
}
}
#[test]
fn comment_is_comment() {
let s = "\t//fldfjbas".to_string();
if !is_comment(&s, &vec!["//".to_string()]) {
panic!("is_blank(&String) problem.");
}
}
#[test]
#[should_panic(expected = "is_comment(&String, &Vec<_>) problem.")]
fn non_comment_is_non_comment() {
let s = " bhfjass".to_string();
if !is_comment(&s, &vec!["//".to_string()]) {
panic!("is_comment(&String, &Vec<_>) problem.");
}
}
#[test]
fn blank_is_not_comment() {
let s = " \t\r\n".to_string();
if is_comment(&s, &vec!["a".to_string()]) {
panic!("is_comment(...) problem.");
}
}
#[test]
fn clean_test() {
let s = " \t\r\n\n\n\t\r \n".to_string();
assert_eq!(clean(&s), "".to_string());
let s = "a\t\r\n\n\n\t\r \n".to_string();
assert_eq!(clean(&s), "a\t\r\n".to_string());
}
#[test]
fn clean_all_test() {
let s = " \t\r\n\n\n\n\t\r \n".to_string();
assert_eq!(clean_all_blank(&s), "".to_string());
}