trixy 0.4.0

A rust crate used to generate multi-language apis for your application
Documentation
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/

use thiserror::Error;

use std::{error::Error, fmt::Display};

use crate::parser::{
    command_spec::{checked::Identifier, unchecked::Attribute},
    error::{AdditionalHelp, ErrorContext, ErrorContextDisplay},
    lexing::TokenSpan,
    parsing::unchecked::error::SpannedParsingError as OldSpannedParsingError,
};

#[derive(Error, Debug)]
pub enum ParsingError {
    #[error("The type ('{r#type}') was not declared before!")]
    TypeNotDeclared { r#type: Identifier, span: TokenSpan },
    #[error(transparent)]
    PreParseError(#[from] OldSpannedParsingError),
    #[error("The enum ('{name}') has the same name as it's namespace")]
    EnumWithNamespaceName {
        name: Identifier,
        enum_span: TokenSpan,
        namespace_span: TokenSpan,
    },
    #[error("The enum ('{name}') has the same name as it's namespace if it's in Pascal case")]
    EnumWithNamespaceNamePascal {
        name: Identifier,
        enum_span: TokenSpan,
        namespace_span: TokenSpan,
    },
    #[error("The enum ('{name}') has no variants. This is not supported by c")]
    EnumWithoutVariants {
        name: Identifier,
        enum_span: TokenSpan,
    },

    #[error(
        "The enum ('{name}') is marked as an error enumeration, but has a variant ('{variant}'), which has no display derived on it."
    )]
    EnumWithoutMarkedState {
        name: Identifier,
        variant: Identifier,
        enum_span: TokenSpan,
        state_span: TokenSpan,
    },

    #[error("Your provided type ('{r#type}') has {got} generic args, but I expected at least: {expected_min}")]
    NotEnoughGenericArgs {
        expected_min: usize,
        got: usize,
        r#type: Identifier,
        span: TokenSpan,
    },
    #[error("Your provided type ('{r#type}') has {got} generic args, but I expected at most: {expected_max}")]
    TooManyGenericArgs {
        expected_max: usize,
        got: usize,
        r#type: Identifier,
        span: TokenSpan,
    },
    #[error("The {specified} attribute can't be used here!")]
    WrongAttributeInPosition {
        specified: Attribute,
        span: TokenSpan,
    },
    #[error("The structure named: {structure} has been used multiple times")]
    StructureCopied {
        structure: Identifier,
        span: TokenSpan,
    },
    #[error("The enumeration named: {enumeration} has been used multiple times")]
    EnumerationCopied {
        enumeration: Identifier,
        span: TokenSpan,
    },
}

impl ParsingError {
    pub fn span(&self) -> &TokenSpan {
        match self {
            ParsingError::TypeNotDeclared { span, .. } => span,
            ParsingError::PreParseError(err) => err.source.span(),
            ParsingError::EnumWithNamespaceName { enum_span, .. } => enum_span,
            ParsingError::EnumWithNamespaceNamePascal { enum_span, .. } => enum_span,
            ParsingError::NotEnoughGenericArgs { span, .. } => span,
            ParsingError::TooManyGenericArgs { span, .. } => span,
            ParsingError::WrongAttributeInPosition { span, .. } => span,
            ParsingError::StructureCopied { span, .. } => span,
            ParsingError::EnumerationCopied { span, .. } => span,
            ParsingError::EnumWithoutVariants { enum_span, .. } => enum_span,
            ParsingError::EnumWithoutMarkedState { state_span, .. } => state_span,
        }
    }
}

impl AdditionalHelp for ParsingError {
    fn additional_help(&self) -> String {
        match self {
            ParsingError::TypeNotDeclared { .. } => "This type should have been mentioned in the namespaces above, or in the namespace of this type usage".to_owned(),
            ParsingError::PreParseError(err) => ErrorContextDisplay::source(err).additional_help(),
            ParsingError::EnumWithNamespaceNamePascal {..}
            | ParsingError::EnumWithNamespaceName {..} => "Change the name of this Enumeration as the generation process in trixy-macros needs to use this name".to_owned(),
            ParsingError::NotEnoughGenericArgs { got, expected_min, .. } => format!("Add generic args until you have gone from {} to {}", got, expected_min),
            ParsingError::TooManyGenericArgs { got, expected_max, .. } => format!("Remove generic args until you have gone from {} to {}", got, expected_max),
            ParsingError::WrongAttributeInPosition { .. } => format!("Remove this attribute"),
            ParsingError::StructureCopied {..} => format!("Change this name to be something else, or delete the struct"),
            ParsingError::EnumerationCopied {..} => format!("Change this name to be something else, or delete the enum"),
            ParsingError::EnumWithoutVariants {..} => format!("Add an variant to this enumeration (for example: `__never`, if you want an enumeration that should not be constructed)"),
            ParsingError::EnumWithoutMarkedState {..} => format!("Add an `#[error = \"...\"]` attribute before the variant.")
        }
    }
}

#[derive(Debug)]
pub struct SpannedParsingError {
    pub source: Box<ParsingError>,
    pub context: ErrorContext,
}

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

impl Display for SpannedParsingError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.error_fmt(f)
    }
}

impl ErrorContextDisplay for SpannedParsingError {
    type Error = ParsingError;

    fn context(&self) -> &crate::parser::error::ErrorContext {
        &self.context
    }

    fn line_number(&self) -> usize {
        self.context.line_number
    }

    fn line_above(&self) -> &str {
        &self.context.line_above
    }

    fn line_below(&self) -> &str {
        &self.context.line_below
    }

    fn line(&self) -> &str {
        &self.context.line
    }

    fn source(&self) -> &<SpannedParsingError as ErrorContextDisplay>::Error {
        &self.source
    }
}