vojaq_parser 0.1.1

Parser of the Vojaq file format
use crate::vojaq_line::VojaqLine;
use crate::vojaq_trio::VojaqTrio;
use std::str::Chars;

#[derive(Eq, PartialEq)]
enum ParsingStatus {
    Primo,
    Secondo,
    Terzo
}

pub fn parse_line(line: &str) -> VojaqLine {
    let l = line.trim();
    if l.is_empty() {
        VojaqLine::Empty
    } else if l.starts_with(r"\#") {
        VojaqLine::Comment
    } else {
        match parse_vojaq_trio(line) {
            Ok(trio) => VojaqLine::Trio(trio),
            Err(error) => VojaqLine::Invalid(error)
        }
    }
}

fn parse_vojaq_trio(line: &str) -> Result<VojaqTrio, String> {
    let mut it = line.chars();
    Ok(VojaqTrio::new(
        parse_testo(&mut it, ParsingStatus::Primo)?,
        parse_testo(&mut it, ParsingStatus::Secondo)?,
        parse_testo(&mut it, ParsingStatus::Terzo)?
    ))
}

fn parse_testo(it: &mut Chars, state: ParsingStatus) -> Result<String, String> {
    let mut testo = String::new();
    while let Some(c) = it.next() {
        match c {
            '\\' => {
                if let Some(ch) = it.next() {
                    match ch {
                        '{' | '}' | '\\' => testo.push(ch),
                        _ => return Err("Invalid escape sequence".to_owned())
                    }
                } else {
                    return Err("Backslash not followed by a character".to_owned())
                }
            },
            '{' => {
                if state == ParsingStatus::Primo {
                    return Ok(testo.trim().to_owned());
                } else {
                    return Err("Use of '{' outside of primo".to_owned());
                }
            },
            '}' => {
                if state == ParsingStatus::Secondo {
                    return Ok(testo.trim().to_owned());
                } else {
                    return Err("Use of '}' outside of secondo".to_owned());
                }
            }
            _ => testo.push(c)
        }
    }
    match state {
        ParsingStatus::Terzo => Ok(testo.trim().to_owned()),
        _ => Err("Reach end-of-line outside of terzo".to_owned())
    }
}