use alloc::string::String;
use serde::{Deserialize, Deserializer};
use super::{decode::FieldDecode, error::FieldError};
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub enum Field<T> {
#[default]
Missing,
Valid(T),
Invalid(FieldError),
}
impl<T> Field<T> {
#[must_use]
pub fn invalid(error: impl Into<FieldError>) -> Self {
Self::Invalid(error.into())
}
impl_common_field_methods! {
map_return: Field<U>;
map_missing: Field::Missing;
map_valid: Field::Valid;
invalid_ignore: Self::Invalid(_),
invalid_error: Self::Invalid(error) => error;
invalid_map: Self::Invalid(error) => Field::Invalid(error);
}
#[must_use]
pub fn from_result(result: Result<T, impl Into<FieldError>>) -> Self {
match result {
Ok(value) => Self::Valid(value),
Err(error) => Self::Invalid(error.into()),
}
}
}
impl Field<String> {
#[must_use]
pub fn as_str(&self) -> Option<&str> {
self.as_ref().map(String::as_str)
}
}
impl<T> From<T> for Field<T> {
fn from(value: T) -> Self {
Self::Valid(value)
}
}
impl<'de, T> Deserialize<'de> for Field<T>
where
T: FieldDecode<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
T::decode_field(deserializer)
}
}
#[cfg(test)]
mod tests {
use alloc::{borrow::ToOwned, string::ToString};
use super::*;
#[test]
fn missing_is_the_default() {
let field = Field::<u64>::default();
assert!(field.is_missing());
assert_eq!(field.as_ref(), None);
assert_eq!(field.into_option(), None);
}
#[test]
fn invalid_field_exposes_error() {
let field = Field::<u64>::invalid("bad wire value");
assert!(field.is_invalid());
assert_eq!(field.error().unwrap().message(), "bad wire value");
}
#[test]
fn from_result_converts_ok_and_err() {
let valid = Field::from_result(Ok::<_, FieldError>(42));
let invalid = Field::<u64>::from_result(Err("bad wire value"));
assert_eq!(valid.as_ref(), Some(&42));
assert!(invalid.is_invalid());
assert_eq!(invalid.error().unwrap().message(), "bad wire value");
}
#[test]
fn invalid_field_equality_compares_displayed_error_messages() {
assert_eq!(
Field::<u64>::invalid("bad"),
Field::<u64>::invalid(String::from("bad")),
);
}
#[test]
fn valid_field_exposes_mutable_value() {
let mut field = Field::Valid(41);
assert!(field.is_valid());
*field.as_mut().unwrap() += 1;
assert_eq!(field.as_ref(), Some(&42));
}
#[test]
fn map_preserves_non_valid_states() {
let missing = Field::<u64>::Missing.map(|value| value.to_string());
let invalid = Field::<u64>::invalid("bad").map(|value| value.to_string());
assert!(missing.is_missing());
assert!(invalid.is_invalid());
}
#[test]
fn result_accessors_preserve_missing_valid_and_invalid() {
let missing = Field::<u64>::Missing;
let valid = Field::Valid(42);
let invalid = Field::<u64>::invalid("bad wire value");
assert_eq!(missing.as_result(), Ok(None));
assert_eq!(valid.as_result(), Ok(Some(&42)));
assert_eq!(
invalid.as_result().err().unwrap().message(),
"bad wire value"
);
assert_eq!(Field::<u64>::Missing.into_result(), Ok(None));
assert_eq!(Field::Valid(42).into_result(), Ok(Some(42)));
assert_eq!(
Field::<u64>::invalid("bad wire value")
.into_result()
.err()
.map(FieldError::into_message),
Some("bad wire value".to_owned())
);
}
}