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}