use crate::{Key, Value};
use nonempty::NonEmpty;
use std::borrow::Cow;
use std::num::ParseFloatError;
use std::str::FromStr;
use miette::{Diagnostic, SourceSpan};
use thiserror::Error;
use crate::DeserializeErrors;
use crate::util::value_type_description;
use crate::{DeserializeValue, Error};
#[derive(Diagnostic, Error, Debug, Clone)]
#[diagnostic(help("example decimals: `2.452`, `-155.7` or `100.0`"))]
pub enum DeserializeFloatError {
#[error("Invalid float")]
ParseFloat {
error: ParseFloatError,
#[label("{error}")]
span: SourceSpan,
},
#[error("Type mismatch")]
TypeMismatch {
details: String,
#[label("{details}")]
span: SourceSpan,
},
}
impl Error for DeserializeFloatError {
fn expected_type() -> Cow<'static, str> {
"decimal".into()
}
}
trait Float: FromStr<Err = ParseFloatError> {
fn deserialize_value(
_key: Key,
value: Value,
) -> Result<Self, DeserializeErrors<Self, NonEmpty<DeserializeFloatError>>> {
value
.as_ref()
.as_float()
.ok_or_else(|| {
DeserializeErrors::err(DeserializeFloatError::TypeMismatch {
details: DeserializeFloatError::details(value_type_description(&value)),
span: value.span().into(),
})
})?
.as_str()
.parse()
.map_err(|error| {
DeserializeErrors::err(DeserializeFloatError::ParseFloat {
error,
span: value.span().into(),
})
})
}
}
impl Float for f32 {}
impl Float for f64 {}
impl DeserializeValue<'_> for f32 {
type Error = DeserializeFloatError;
fn deserialize_value(
key: Key,
value: Value,
) -> Result<Self, DeserializeErrors<Self, NonEmpty<Self::Error>>> {
<Self as Float>::deserialize_value(key, value)
}
}
impl DeserializeValue<'_> for f64 {
type Error = DeserializeFloatError;
fn deserialize_value(
key: Key,
value: Value,
) -> Result<Self, DeserializeErrors<Self, NonEmpty<Self::Error>>> {
<Self as Float>::deserialize_value(key, value)
}
}