use crate::combination::{MultipleValues, SingleValue};
use crate::{FieldError, FieldType, InvalidValue, split_list, trim_whitespace};
pub struct Slot<A> {
first: Option<usize>,
value: A,
}
impl<A: Default> Default for Slot<A> {
fn default() -> Self {
Self {
first: None,
value: A::default(),
}
}
}
pub trait Accumulate<'a>: Sized {
const INVALID: InvalidValue;
fn add(
slot: &mut Slot<Self>,
value: &'a [u8],
position: usize,
name: &'static str,
invalid: InvalidValue,
) -> Result<(), FieldError>;
fn finish(slot: Slot<Self>) -> Self {
slot.value
}
}
fn parse<'a, T: FieldType<'a>>(
value: &'a [u8],
position: usize,
name: &'static str,
invalid: InvalidValue,
) -> Result<Option<T>, FieldError> {
match T::parse(trim_whitespace(value)) {
Ok(value) => Ok(Some(value)),
Err(_) if invalid == InvalidValue::Ignore => Ok(None),
Err(error) => Err(FieldError::invalid(name, position, error)),
}
}
impl<'a, T: FieldType<'a>> Accumulate<'a> for Option<T>
where
T::Combination: SingleValue,
{
const INVALID: InvalidValue = T::INVALID;
fn add(
slot: &mut Slot<Self>,
value: &'a [u8],
position: usize,
name: &'static str,
invalid: InvalidValue,
) -> Result<(), FieldError> {
if let Some(first) = slot.first {
return Err(FieldError::duplicate(name, position, first));
}
slot.first = Some(position);
slot.value = parse(value, position, name, invalid)?;
Ok(())
}
}
impl<'a, T: FieldType<'a>> Accumulate<'a> for Vec<T>
where
T::Combination: MultipleValues,
{
const INVALID: InvalidValue = T::INVALID;
fn add(
slot: &mut Slot<Self>,
value: &'a [u8],
position: usize,
name: &'static str,
invalid: InvalidValue,
) -> Result<(), FieldError> {
if !<T::Combination as MultipleValues>::LIST {
slot.value.extend(parse(value, position, name, invalid)?);
return Ok(());
}
let start = slot.value.len();
for element in split_list(value) {
let element = match element {
Ok(element) => element,
Err(_) if invalid == InvalidValue::Ignore => {
slot.value.truncate(start);
return Ok(());
}
Err(error) => return Err(FieldError::invalid(name, position, error)),
};
slot.value.extend(parse(element, position, name, invalid)?);
}
Ok(())
}
}