instruct/parse/
mod.rs

1use anyhow;
2use std::io;
3use std::{fs::File, io::Read};
4use thiserror::Error;
5
6use nom::error::{convert_error, VerboseError};
7use nom::Err;
8
9pub mod ast;
10mod combinator;
11
12#[derive(Error, Debug)]
13pub enum ParseError {
14    #[error("couldn't load file '{0}': {1}")]
15    FileNotFound(String, io::Error),
16    #[error("couldn't read file '{0}' as valid utf-8: {1}")]
17    InvalidFileContent(String, io::Error),
18    #[error("syntax error in task file: {0}")]
19    SyntaxError(String),
20}
21
22pub fn load_and_parse(path: &str) -> anyhow::Result<ast::Namespace> {
23    let mut content = String::new();
24    let mut content_path = match File::open(path) {
25        Ok(val) => val,
26        Err(e) => return Err(ParseError::FileNotFound(path.into(), e).into()),
27    };
28    match content_path.read_to_string(&mut content) {
29        Ok(_) => (),
30        Err(e) => return Err(ParseError::InvalidFileContent(path.into(), e).into()),
31    };
32
33    match combinator::namespace::module::<VerboseError<&str>>(&content) {
34        Ok((_, ast)) => Ok(ast),
35        Err(Err::Error(e)) | Err(Err::Failure(e)) => {
36            Err(ParseError::SyntaxError(convert_error(content.as_str(), e)).into())
37        }
38        Err(e) => panic!("invalid state! incomplete: {:?}", e),
39    }
40}