dbstruct-derive 0.4.0

Build a typed database by defining a struct
Documentation
use core::fmt;
use proc_macro2::Span;

use crate::errors::Help;
use crate::GetSpan;

#[derive(thiserror::Error, Debug)]
pub enum ErrorVariant {
    #[error("Can only have a single dbstruct attribute on a struct field")]
    MultipleAttributes,
    #[error("Attribute must not be empty")]
    EmptyAttribute,
    #[error("Invalid token tree expected Group")]
    InvalidTokenTree,
    #[error("Not a dbstruct wrapper annotation (try DefaultTrait or DefaultValue)")]
    NotAWrapper(syn::Ident),
    #[error("Not valid syntax for a dbstruct attribute, expected a single word as option")]
    InvalidSyntax(proc_macro2::TokenTree),
    #[error("Each field can have a maximum of two wrapper attributes")]
    MultipleWrapperAttributes,
    #[error("Option is already initialized as None")]
    OptionNotAllowed,
    #[error("The empty type is not allowed")]
    EmptyTypeForbidden,
    #[error("dbstruct does not know how to represent a missing value for this type")]
    NoDefaultType,
    #[error("Invalid syntax: missing an expression for the default value")]
    MissingDefaultValue,
    #[error("Default value is not an expression")]
    ValueNotExpression(syn::parse::Error),
    #[error("Invalid argument for the Default attribute")]
    InvalidDefaultArg,
    #[error("Types must be fully owned and can not have lifetime params")]
    NotATypeGeneric,
    #[error("{ty} needs {n_needed} generic types")]
    TooFewGenerics { ty: &'static str, n_needed: u8 },
    #[error("Too many generics for {ty}, expected {n_needed}")]
    TooManyGenerics { ty: &'static str, n_needed: u8 },
}

#[derive(thiserror::Error, Debug)]
pub struct Error {
    pub variant: ErrorVariant,
    span: Option<Span>,
}

impl Help for Error {
    fn help(&self) -> Option<String> {
        use ErrorVariant::*;
        Some(match self.variant {
            NoDefaultType => {
                "you can wrap the type in an Option, add an attribute to use the Default trait: 
                `#[dbstruct(Default)]` or provide an expression to generate a default 
                value: `#[dbstruct(Default=<expr>)]"
            }
            InvalidSyntax(_) => {
                "try one of these: `#[dbstruct(Default)]`, `#[dbstruct(Default=\"<expr>\"]`"
            }
            OptionNotAllowed => "try removing the attribute",
            MultipleWrapperAttributes => {
                "when using Default=\"<expr>\" make sure the <expr> string is properly escaped"
            }
            _ => return None,
        }
        .to_owned())
    }
}

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

impl Error {
    pub fn span(&self) -> Span {
        match (&self.variant, self.span) {
            (ErrorVariant::NotAWrapper(item), None) => item.span(),
            (ErrorVariant::InvalidSyntax(item), None) => item.span(),
            (ErrorVariant::ValueNotExpression(item), None) => item.span(),
            (_, Some(span)) => span,
            (_var, _) => panic!(
                "error should track a span for {_var:?} as 
                the variant itself does not contain one"
            ),
        }
    }
}

impl GetSpan for super::Attribute {
    fn span(&self) -> Span {
        use super::Attribute::*;
        match self {
            // NoWrap { span } => *span,
            DefaultTrait { span } => *span,
            DefaultValue { expr } => syn::spanned::Spanned::span(expr),
        }
    }
}

impl ErrorVariant {
    pub(super) fn with_span(self, item: impl GetSpan) -> Error {
        Error {
            variant: self,
            span: Some(item.span()),
        }
    }

    pub(super) fn has_span(self) -> Error {
        Error {
            variant: self,
            span: None,
        }
    }
}