ranty 1.0.0

The Ranty procedural templating language
Documentation
use self::parser::RantyParser;
use crate::{RantyProgram, RantyProgramInfo};
use std::io::ErrorKind as IOErrorKind;
use std::{error::Error, fs};
use std::{fmt::Display, path::Path, rc::Rc};

pub(crate) mod lexer;
pub(crate) mod message;
pub(crate) mod parser;
pub(crate) mod reader;

pub use message::*;

/// Type alias for `Result<RantyProgram, CompilerErrorKind>`
pub type CompileResult = Result<RantyProgram, CompilerError>;

/// Describes why a compilation failed.
#[derive(Debug)]
pub enum CompilerError {
    /// Compilation failed due to one or more syntax errors.
    SyntaxError,
    /// Compilation failed due to a file I/O error.
    IOError(IOErrorKind),
}

impl Display for CompilerError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            CompilerError::SyntaxError => write!(f, "syntax error"),
            CompilerError::IOError(_) => write!(f, "I/O error"),
        }
    }
}

impl Error for CompilerError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        None
    }
    fn cause(&self) -> Option<&dyn Error> {
        self.source()
    }
}

/// Provides an interface through which the compiler can report errors and warnings.
pub trait Reporter {
    /// Passes a compiler message to the implementor for processing.
    fn report(&mut self, msg: CompilerMessage);
}

impl Reporter for () {
    fn report(&mut self, _msg: CompilerMessage) {}
}

impl Reporter for Vec<CompilerMessage> {
    fn report(&mut self, msg: CompilerMessage) {
        self.push(msg);
    }
}

pub(crate) fn compile_string<R: Reporter>(
    source: &str,
    reporter: &mut R,
    debug_enabled: bool,
    info: RantyProgramInfo,
) -> CompileResult {
    let info = Rc::new(info);

    let mut parser = RantyParser::new(source, reporter, debug_enabled, &info);

    // Return compilation result
    match parser.parse() {
        Ok(seq) => Ok(RantyProgram::new(seq, info)),
        Err(()) => Err(CompilerError::SyntaxError),
    }
}

pub(crate) fn compile_file<P: AsRef<Path>, R: Reporter>(
    path: P,
    reporter: &mut R,
    debug_enabled: bool,
) -> CompileResult {
    let source_name = path
        .as_ref()
        .canonicalize()
        .unwrap_or_else(|_| path.as_ref().to_path_buf())
        .to_string_lossy()
        .to_string();
    let file_read_result = fs::read_to_string(path);
    match file_read_result {
        Ok(source) => compile_string(
            &source,
            reporter,
            debug_enabled,
            RantyProgramInfo {
                name: None,
                path: Some(source_name),
            },
        ),
        // Something went wrong with reading the file
        Err(err) => {
            // Report file IO issue
            let problem = match err.kind() {
                IOErrorKind::NotFound => Problem::FileNotFound(source_name),
                _ => Problem::FileSystemError(err.to_string()),
            };
            reporter.report(CompilerMessage::new(problem, Severity::Error, None));
            Err(CompilerError::IOError(err.kind()))
        }
    }
}