use std::collections::HashSet;
use crate::Text;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum FieldErrorKind {
Required,
Invalid(Text),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct FieldError<K = String> {
pub key: K,
pub kind: FieldErrorKind,
}
impl<K> FieldError<K> {
pub fn required(key: impl Into<K>) -> Self {
Self {
key: key.into(),
kind: FieldErrorKind::Required,
}
}
pub fn invalid(key: impl Into<K>, message: impl Into<Text>) -> Self {
Self {
key: key.into(),
kind: FieldErrorKind::Invalid(message.into()),
}
}
pub fn message(&self, label: impl Into<Text>) -> Text {
match &self.kind {
FieldErrorKind::Required => crate::text!("tablo-field-required", label = label),
FieldErrorKind::Invalid(message) => message.clone(),
}
}
}
pub(crate) fn unreadable(key: &str) -> FieldError {
FieldError::invalid(key, Text::message("tablo-field-unreadable"))
}
#[derive(Debug, Clone)]
pub struct FieldErrors<K = String> {
errors: Vec<FieldError<K>>,
}
impl<K> Default for FieldErrors<K> {
fn default() -> Self {
Self { errors: Vec::new() }
}
}
impl<K> FieldErrors<K> {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, key: impl Into<K>, message: impl Into<Text>) {
self.errors.push(FieldError::invalid(key, message));
}
pub fn push(&mut self, error: FieldError<K>) {
self.errors.push(error);
}
pub fn is_empty(&self) -> bool {
self.errors.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &FieldError<K>> {
self.errors.iter()
}
pub fn extend(&mut self, other: Self) {
self.errors.extend(other.errors);
}
pub(crate) fn keyed(self, key: fn(K) -> &'static str) -> FieldErrors {
let mut keyed = FieldErrors::new();
for error in self {
keyed.push(FieldError {
key: key(error.key).to_string(),
kind: error.kind,
});
}
keyed
}
}
impl<K> IntoIterator for FieldErrors<K> {
type Item = FieldError<K>;
type IntoIter = std::vec::IntoIter<FieldError<K>>;
fn into_iter(self) -> Self::IntoIter {
self.errors.into_iter()
}
}
impl FieldErrors {
pub fn contains_key(&self, key: &str) -> bool {
self.errors.iter().any(|error| error.key == key)
}
pub fn first(&self, key: &str) -> Option<&FieldError> {
self.errors.iter().find(|error| error.key == key)
}
pub(crate) fn replace(&mut self, other: Self) {
let owned: HashSet<&str> = other
.errors
.iter()
.map(|error| error.key.as_str())
.collect();
self.errors
.retain(|kept| !owned.contains(kept.key.as_str()));
self.errors.extend(other.errors);
}
}