tre 0.1.1

A simple, fast and interactive alternative to 'tree.'
Documentation
use ignore::{DirEntry, Error};
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::io::Error as IOError;

/// Enum expressing the possible file types returned by Walker
pub enum FileType {
    Directory,
    File,
    Link,
}

/// Aggregate statistics for the traversed filesystem
pub struct Stats {
    files: usize,
    directories: isize,
    links: usize,
    lines: Option<usize>,
}

/// StatCollector is responsible for aggregating statistics as the filesystem is traversed,
/// as well as parsing a DirEntry's file type
pub struct StatsCollector {
    stats: Stats,
}

impl StatsCollector {
    pub fn new() -> StatsCollector {
        StatsCollector {
            stats: Stats {
                files: 0,
                directories: -1,
                links: 0,
                lines: None,
            },
        }
    }

    /// Parse's and returns a file's type and update aggregate count statistics
    pub fn parse_and_collect(&mut self, entry: &DirEntry) -> Result<FileType, Error> {
        match entry.file_type() {
            Some(typ) if typ.is_dir() => {
                self.stats.directories += 1;
                Ok(FileType::Directory)
            }
            Some(typ) if typ.is_symlink() => {
                self.stats.links += 1;
                Ok(FileType::Link)
            }
            _ => {
                self.stats.files += 1;
                Ok(FileType::File)
            }
        }
    }

    /// Attempts to read contents of a file to count lines.
    pub fn count_lines(&mut self, entry: &DirEntry) -> Result<usize, IOError> {
        let mut f = File::open(entry.path())?;
        let mut s = String::new();

        f.read_to_string(&mut s)?;

        let line_count = s.lines().count();
        self.stats.lines = match self.stats.lines {
            Some(l) => Some(l + line_count),
            None => Some(line_count),
        };

        Ok(line_count)
    }
}

impl fmt::Display for StatsCollector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "\nDirectories: {}, Files: {}, Symbolic Links: {}, Lines: {}",
            self.stats.directories,
            self.stats.files,
            self.stats.links,
            self.stats.lines.unwrap_or(0)
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn print_stats() {
        let mut stats = StatsCollector::new();

        assert_eq!(
            "\nDirectories: -1, Files: 0, Symbolic Links: 0, Lines: 0",
            format!("{}", stats)
        );

        stats.stats.directories = 10;
        stats.stats.files = 10;
        stats.stats.links = 10;
        stats.stats.lines = Some(10);

        assert_eq!(
            "\nDirectories: 10, Files: 10, Symbolic Links: 10, Lines: 10",
            format!("{}", stats)
        );
    }
}