rested 0.11.0

Language/Interpreter for easily defining and running requests to an http server.
Documentation
use enum_tags_traits::TaggedEnum;

use crate::error_meta::ContextualError;
use crate::lexer::locations::{GetSpan, Span};
use crate::lexer::Token;
use crate::parser::error::{ParseError, ParserErrors};

use super::value::{Value, ValueTag};

#[derive(Clone, Debug, PartialEq)]
pub enum InterpreterErrorKind {
    UnknownConstant { constant: String },
    RequiredArguments { required: usize, received: usize },
    EnvVariableNotFound { name: String },
    RequestWithPathnameWithoutBaseUrl,
    UndefinedCallable { name: String },
    UndeclaredIdentifier { name: String },
    UnsupportedAttribute { name: String },
    DuplicateAttribute { name: String },
    TypeMismatch { expected: ValueTag, found: ValueTag },
    Other { error: String },
}

impl std::error::Error for InterpreterErrorKind {}

impl std::fmt::Display for InterpreterErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let formatted_error = match self {
            InterpreterErrorKind::UnknownConstant { constant } => {
                format!("trying to set an unknown constant {}", constant)
            }
            InterpreterErrorKind::RequiredArguments { required, received } => {
                match required {
                    1usize =>  format!("{} argument expected, received {}", required, received),
                    _ => format!("{} arguments expected, received {}", required, received)
                }
            }
            InterpreterErrorKind::EnvVariableNotFound { name } => {
                format!("no variable found by the name {:?}", name)
            }
            InterpreterErrorKind::RequestWithPathnameWithoutBaseUrl => {
                "BASE_URL needs to be set first for requests to work with just pathnames; try writing like set BASE_URL \"<api orgin>\" before this request".to_string()
            }
            InterpreterErrorKind::UndefinedCallable { name } => {
                format!("attempting to calling an undefined function: {}", name)
            }
            InterpreterErrorKind::UndeclaredIdentifier { name } => {
                format!("undeclared variable: {}", name)
            }
            InterpreterErrorKind::UnsupportedAttribute { name } => {
                format!("unsupported attribute: {}", name)
            }
            InterpreterErrorKind::DuplicateAttribute { name } => {
                format!(
                    "duplicate attribute: @{} is already set for this request",
                    name
                )
            }
            InterpreterErrorKind::Other { error } => error.clone(),
            InterpreterErrorKind::TypeMismatch { expected, found } => {
                format!(
                    "expected type {:?}, but found {:?}",
                    format!("{:?}", expected).to_lowercase(),
                    format!("{:?}", found).to_lowercase(),
                )
            },
        };

        f.write_str(&formatted_error)
    }
}

pub enum InterpreterError<'source> {
    ParseErrors(ParserErrors<'source>),
    EvalErrors(Box<[ContextualError<InterpreterErrorKind>]>),
}

impl<'source> std::error::Error for InterpreterError<'source> {}

impl<'source> std::fmt::Debug for InterpreterError<'source> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{self}")
    }
}

impl<'source> std::fmt::Display for InterpreterError<'source> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            InterpreterError::EvalErrors(errors) => {
                for err in errors.iter() {
                    write!(f, "{err}")?
                }
                Ok(())
            }
            InterpreterError::ParseErrors(ParserErrors { errors }) => {
                for err in errors.iter() {
                    write!(f, "{err}")?
                }
                Ok(())
            }
        }
    }
}

impl<'source> From<Box<ContextualError<ParseError<'source>>>>
    for Box<ContextualError<InterpreterErrorKind>>
{
    fn from(value: Box<ContextualError<ParseError<'source>>>) -> Self {
        Box::new(ContextualError {
            inner_error: InterpreterErrorKind::Other {
                error: value.inner_error.to_string(),
            },
            span: value.span,
            message: value.message,
            context: value.context,
        })
    }
}

impl<'source> From<ParserErrors<'source>> for InterpreterError<'source> {
    fn from(value: ParserErrors<'source>) -> Self {
        Self::ParseErrors(value)
    }
}

pub struct InterpErrorFactory<'i> {
    source_code: &'i str,
}

impl<'i> InterpErrorFactory<'i> {
    pub fn new(source: &'i str) -> Self {
        Self {
            source_code: source,
        }
    }
    pub fn unknown_constant(&self, token: &Token) -> ContextualError<InterpreterErrorKind> {
        ContextualError::new(
            InterpreterErrorKind::UnknownConstant {
                constant: token.text.to_string(),
            },
            token.span(),
            self.source_code,
        )
    }

    pub fn env_variable_not_found(
        &self,
        variable: String,
        span: Span,
    ) -> ContextualError<InterpreterErrorKind> {
        ContextualError::new(
            InterpreterErrorKind::EnvVariableNotFound { name: variable },
            span,
            self.source_code,
        )
    }

    pub fn required_args(
        &self,
        at: Span,
        required: usize,
        received: usize,
    ) -> ContextualError<InterpreterErrorKind> {
        ContextualError::new(
            InterpreterErrorKind::RequiredArguments { required, received },
            at,
            self.source_code,
        )
    }

    pub fn undeclared_identifier(&self, token: &Token) -> ContextualError<InterpreterErrorKind> {
        ContextualError::new(
            InterpreterErrorKind::UndeclaredIdentifier {
                name: token.text.to_string(),
            },
            token.span(),
            self.source_code,
        )
    }

    pub fn unsupported_attribute(&self, token: &Token) -> ContextualError<InterpreterErrorKind> {
        ContextualError::new(
            InterpreterErrorKind::UnsupportedAttribute {
                name: token.text.to_string(),
            },
            token.span(),
            self.source_code,
        )
    }

    pub fn duplicate_attribute(&self, token: &Token) -> ContextualError<InterpreterErrorKind> {
        ContextualError::new(
            InterpreterErrorKind::DuplicateAttribute {
                name: token.text.to_string(),
            },
            token.span(),
            self.source_code,
        )
    }

    pub fn undefined_callable(&self, token: &Token) -> ContextualError<InterpreterErrorKind> {
        ContextualError::new(
            InterpreterErrorKind::UndefinedCallable {
                name: token.text.to_string(),
            },
            token.span(),
            self.source_code,
        )
    }

    pub fn unset_base_url(&self, at: Span) -> ContextualError<InterpreterErrorKind> {
        ContextualError::new(
            InterpreterErrorKind::RequestWithPathnameWithoutBaseUrl,
            at,
            self.source_code,
        )
    }

    pub fn type_mismatch(
        &self,
        expected: ValueTag,
        found: Value,
        at: Span,
    ) -> ContextualError<InterpreterErrorKind> {
        ContextualError::new(
            InterpreterErrorKind::TypeMismatch {
                expected,
                found: found.tag(),
            },
            at,
            self.source_code,
        )
    }

    pub fn other<E: std::fmt::Display>(
        &self,
        span: Span,
        error: E,
    ) -> ContextualError<InterpreterErrorKind> {
        ContextualError::new(
            InterpreterErrorKind::Other {
                error: error.to_string(),
            },
            span,
            self.source_code,
        )
    }
}