sword-macros 0.3.0

Structured web framework built on top of tokio ecosystem, providing powerful features for building robust web applications.
Documentation
use syn::{Error, Fields, Ident};

use super::parse::HttpErrorConfig;
use crate::errors::{MessageValue, validate_message_references, validate_named_field_refs};

pub struct HttpErrorValidator;

impl HttpErrorValidator {
    pub fn validate_variant_config(
        enum_name: &Ident,
        variant_name: &Ident,
        fields: &Fields,
        config: &HttpErrorConfig,
    ) -> syn::Result<()> {
        if config.transparent {
            crate::errors::validate_transparent_single_unnamed(variant_name, fields)?;
            return Ok(());
        }

        if config.code.is_none() {
            return Err(Error::new_spanned(
                variant_name,
                "missing `code` after merging #[http_error(...)] and #[http(...)]",
            ));
        }

        match fields {
            Fields::Named(named) => {
                validate_message_references(enum_name, variant_name, named, &config.message)?;

                let extra_refs: Vec<(&str, &str)> = config
                    .error_field
                    .as_deref()
                    .map(|field| ("error", field))
                    .into_iter()
                    .chain(
                        config
                            .errors_field
                            .as_deref()
                            .map(|field| ("errors", field)),
                    )
                    .collect();

                validate_named_field_refs(enum_name, variant_name, named, &extra_refs)?;
            }
            Fields::Unnamed(unnamed) => {
                crate::errors::validate_single_unnamed(variant_name, unnamed)?;

                if Self::message_field_name(config).is_some()
                    || matches!(config.message, Some(MessageValue::Interpolated(_)))
                    || config.error_field.is_some()
                    || config.errors_field.is_some()
                {
                    return Err(Error::new_spanned(
                        variant_name,
                        "tuple variants do not support `message = field`, interpolated messages, `error`, or `errors`; use named fields or construct the client message before creating the error",
                    ));
                }
            }
            Fields::Unit => {
                if Self::message_field_name(config).is_some()
                    || matches!(config.message, Some(MessageValue::Interpolated(_)))
                    || config.error_field.is_some()
                    || config.errors_field.is_some()
                {
                    return Err(Error::new_spanned(
                        variant_name,
                        "unit variants do not support field-based or interpolated `message`, `error`, or `errors`",
                    ));
                }
            }
        }

        Ok(())
    }

    pub fn message_field_name(config: &HttpErrorConfig) -> Option<&str> {
        match &config.message {
            Some(MessageValue::Field(field)) => Some(field.as_str()),
            _ => None,
        }
    }
}