1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
use crate::line_parser::{LineParser, LineParserError};
use crate::line_visitor::LineVisitor;
use crate::models::Line;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

#[derive(Debug)]
pub enum LineProcessorError<E> {
    ParseError {
        line_no: usize,
        error: LineParserError,
    },
    VisitorError {
        line_no: usize,
        error: E,
    },
    IoError(std::io::Error),
}

pub struct LineProcessor;

impl LineProcessor {
    pub fn run_file<P, V>(
        &self,
        path: &Path,
        line_visitor: &mut V,
    ) -> Result<usize, LineProcessorError<V::Error>>
    where
        P: LineParser,
        V: LineVisitor,
    {
        match File::open(path) {
            Ok(file) => {
                let mut buf_reader = BufReader::new(file);
                self.run::<P, _, _>(&mut buf_reader, line_visitor)
            }
            Err(err) => Err(LineProcessorError::IoError(err)),
        }
    }

    pub fn run<P, R, V>(
        &self,
        mut rd: R,
        line_visitor: &mut V,
    ) -> Result<usize, LineProcessorError<V::Error>>
    where
        P: LineParser,
        R: BufRead,
        V: LineVisitor,
    {
        let mut line_no = 0; // we enumerate lines starting at 0
        let mut line_buffer = String::new();

        loop {
            line_buffer.clear();

            match rd.read_line(&mut line_buffer) {
                Ok(0) => {
                    // eof
                    let _ = line_visitor
                        .visit_end_of_file()
                        .map_err(|error| LineProcessorError::VisitorError { line_no, error })?;

                    return Ok(line_no);
                }
                Ok(_) => match P::parse_line(&line_buffer) {
                    Ok(line) => match line {
                        Line::Posting(posting) => {
                            let _ = line_visitor.visit_posting(posting).map_err(|error| {
                                LineProcessorError::VisitorError { line_no, error }
                            })?;
                        }
                        Line::Empty => {}
                        Line::TransactionHeader(txn_header) => {
                            let _ = line_visitor.visit_transaction_header(txn_header).map_err(
                                |error| LineProcessorError::VisitorError { line_no, error },
                            )?;
                        }
                        Line::TransactionComment(txn_comment) => {
                            let _ = line_visitor
                                .visit_transaction_comment(txn_comment)
                                .map_err(|error| LineProcessorError::VisitorError {
                                    line_no,
                                    error,
                                })?;
                        }
                    },
                    Err(error) => {
                        return Err(LineProcessorError::ParseError { line_no, error });
                    }
                },
                Err(io_err) => {
                    return Err(LineProcessorError::IoError(io_err));
                }
            }
            line_no += 1;
        }
    }
}