formbeam 0.0.5

Form system for the Hornbeam template engine (derive macros)
Documentation
use std::collections::BTreeMap;
use std::fmt::{Debug, Display};

use bevy_reflect::{FromReflect, Reflect, TypePath};
use static_assertions::assert_impl_all;

#[derive(Clone, Reflect)]
// This makes the struct opaque to the reflection engine, meaning it will
// be cloned absolutely instead of being converted to a DynamicEnum.
// However it won't implement Enum. I still think that's preferable, so `error_code()` will work etc.`
#[reflect_value]
pub enum FieldError {
    Missing,

    TooShort {
        current: u32,
        min_length: u32,
        max_length: u32,
        unit: FieldUnit,
    },
    TooLong {
        current: u32,
        min_length: u32,
        max_length: u32,
        unit: FieldUnit,
    },

    Custom {
        code: String,
        description: String,
        values: BTreeMap<String, String>,
    },
}

impl FieldError {
    pub fn error_code(&self) -> &str {
        match self {
            FieldError::Missing => "missing",
            FieldError::TooShort {
                unit: FieldUnit::Characters,
                ..
            } => "too_short_chars",
            FieldError::TooLong {
                unit: FieldUnit::Characters,
                ..
            } => "too_long_chars",
            FieldError::Custom { code, .. } => code,
        }
    }
}

#[derive(Copy, Clone, Reflect)]
pub enum FieldUnit {
    Characters,
}

impl Display for FieldUnit {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            FieldUnit::Characters => write!(f, "characters"),
        }
    }
}

impl Debug for FieldError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            FieldError::Missing => write!(f, "The field is missing"),
            FieldError::TooShort { current, min_length, max_length, unit } => write!(f, "The field is too short ({current}); it should be between {min_length} and {max_length} {unit}."),
            FieldError::TooLong { current, min_length, max_length, unit } => write!(f, "The field is too long ({current}); it should be between {min_length} and {max_length} {unit}."),
            FieldError::Custom {
                code,
                description,
                values,
            } => write!(f, "[{code}] {description} {values:?}"),
        }
    }
}

pub type FieldErrors = Vec<FieldError>;
assert_impl_all!(FieldErrors: Reflect, FromReflect, TypePath);