use std::borrow::Cow;
use super::{
DEFAULT_STRING_FIELD_LIMIT_BYTES, MeteredField, STRING_INITIAL_CAPACITY_BYTES,
TINY_SCALAR_INITIAL_CAPACITY_BYTES, TryFromFieldWithState, TypedMultipartError,
read_field_data, str_to_bool, tiny_scalar_limit, truncate_reflected_value,
};
fn wrong_field_type(
field: &MeteredField<'_>,
wanted: &'static str,
source: impl std::fmt::Display,
) -> TypedMultipartError {
TypedMultipartError::WrongFieldType {
field_name: field.name().unwrap_or_default().to_owned(),
wanted: Cow::Borrowed(wanted),
source: source.to_string(),
}
}
impl<S: Send + Sync> TryFromFieldWithState<S> for String {
async fn try_from_field_with_state(
field: MeteredField<'_>,
limit_bytes: Option<usize>,
_state: &S,
) -> Result<Self, TypedMultipartError> {
let limit = limit_bytes.unwrap_or(DEFAULT_STRING_FIELD_LIMIT_BYTES);
let field_data = read_field_data(field, Some(limit), STRING_INITIAL_CAPACITY_BYTES).await?;
let super::FieldBytes { field, data } = field_data;
Self::from_utf8(data).map_err(|e| wrong_field_type(&field, "String", e))
}
}
impl<S: Send + Sync> TryFromFieldWithState<S> for bool {
async fn try_from_field_with_state(
field: MeteredField<'_>,
limit_bytes: Option<usize>,
_state: &S,
) -> Result<Self, TypedMultipartError> {
let field_data = read_field_data(
field,
Some(tiny_scalar_limit(limit_bytes)),
TINY_SCALAR_INITIAL_CAPACITY_BYTES,
)
.await?;
let super::FieldBytes { field, data } = field_data;
let text = std::str::from_utf8(&data).map_err(|e| wrong_field_type(&field, "bool", e))?;
str_to_bool(text).ok_or_else(|| {
wrong_field_type(
&field,
"bool",
format_args!(
"invalid boolean value: `{}`",
truncate_reflected_value(text)
),
)
})
}
}
macro_rules! impl_try_from_field_for_number {
($($ty:ty),* $(,)?) => {
$(
impl<S: Send + Sync> TryFromFieldWithState<S> for $ty {
async fn try_from_field_with_state(
field: MeteredField<'_>,
limit_bytes: Option<usize>,
_state: &S,
) -> Result<Self, TypedMultipartError> {
let field_data = read_field_data(
field,
Some(tiny_scalar_limit(limit_bytes)),
TINY_SCALAR_INITIAL_CAPACITY_BYTES,
).await?;
let super::FieldBytes { field, data } = field_data;
let text = std::str::from_utf8(&data).map_err(|e| {
wrong_field_type(&field, stringify!($ty), e)
})?;
text.trim().parse::<$ty>().map_err(|e| {
wrong_field_type(&field, stringify!($ty), e)
})
}
}
)*
};
}
impl_try_from_field_for_number!(
i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize, f32, f64,
);
impl<S: Send + Sync> TryFromFieldWithState<S> for char {
async fn try_from_field_with_state(
field: MeteredField<'_>,
limit_bytes: Option<usize>,
_state: &S,
) -> Result<Self, TypedMultipartError> {
let field_data = read_field_data(
field,
Some(tiny_scalar_limit(limit_bytes)),
TINY_SCALAR_INITIAL_CAPACITY_BYTES,
)
.await?;
let super::FieldBytes { field, data } = field_data;
let text = std::str::from_utf8(&data).map_err(|e| wrong_field_type(&field, "char", e))?;
let mut chars = text.chars();
match (chars.next(), chars.next()) {
(Some(c), None) => Ok(c),
_ => Err(wrong_field_type(
&field,
"char",
"expected exactly one character",
)),
}
}
}