use std::cell::Cell;
#[cfg(feature = "from_str")]
use std::str::FromStr;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use super::F64RealOrComplex;
use crate::error::{ConversionError, ParseError};
use crate::unit::Unit;
impl<V> Serialize for DynQuantity<V>
where
V: F64RealOrComplex + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("DynQuantity", 2)?;
state.serialize_field("value", &self.value)?;
state.serialize_field("unit", &self.unit)?;
state.end()
}
}
impl<'de, V> Deserialize<'de> for DynQuantity<V>
where
V: F64RealOrComplex + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<DynQuantity<V>, D::Error>
where
D: Deserializer<'de>,
{
let variants = QuantityVariants::<V>::deserialize(deserializer)?;
variants.try_into().map_err(serde::de::Error::custom)
}
}
#[derive(DeserializeUntaggedVerboseError)]
enum QuantityVariants<V>
where
V: F64RealOrComplex,
{
Quantity(QuantityAlias<V>),
#[cfg(feature = "from_str")]
String(String),
Value(V),
}
#[derive(serde::Deserialize)]
struct QuantityAlias<V: F64RealOrComplex> {
value: V,
unit: Unit,
}
impl<V: F64RealOrComplex> TryFrom<QuantityVariants<V>> for DynQuantity<V> {
type Error = ParseError;
fn try_from(variant: QuantityVariants<V>) -> Result<Self, Self::Error> {
match variant {
QuantityVariants::Quantity(variant) => {
return Ok(Self {
value: variant.value,
unit: variant.unit,
});
}
#[cfg(feature = "from_str")]
QuantityVariants::String(string) => {
return Self::from_str(&string);
}
QuantityVariants::Value(value) => {
return Ok(Self {
value,
unit: Unit::default(),
});
}
}
}
}
use std::marker::PhantomData;
use crate::DynQuantity;
use deserialize_untagged_verbose_error::DeserializeUntaggedVerboseError;
use num::Complex;
use serde::{Deserialize, Deserializer, de::DeserializeOwned};
#[derive(DeserializeUntaggedVerboseError)]
enum InnerOrString<T> {
Inner(T),
#[cfg(feature = "from_str")]
String(String),
}
thread_local!(
pub static SERIALIZE_WITH_UNITS: Cell<bool> = Cell::new(false)
);
pub fn serialize_with_units<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
SERIALIZE_WITH_UNITS.with(|ctx| {
ctx.set(true);
let res = f();
ctx.set(false);
res
})
}
pub fn serialize_quantity<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
T: Serialize + Clone + Into<DynQuantity<Complex<f64>>>,
{
SERIALIZE_WITH_UNITS.with(|ctx| {
if ctx.get() {
let quantity: DynQuantity<Complex<f64>> = value.clone().into();
let string = quantity.to_string();
string.serialize(serializer)
} else {
value.serialize(serializer)
}
})
}
pub fn serialize_opt_quantity<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
T: Serialize + Clone + Into<DynQuantity<Complex<f64>>>,
{
match value.as_ref() {
Some(v) => return serialize_quantity(v, serializer),
None => return serializer.serialize_none(),
}
}
pub fn serialize_angle<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
T: Serialize,
for<'a> &'a T: ToString,
{
SERIALIZE_WITH_UNITS.with(|ctx| {
if ctx.get() {
let mut string = value.to_string();
string.push_str(" rad");
string.serialize(serializer)
} else {
value.serialize(serializer)
}
})
}
pub fn serialize_opt_angle<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
T: Serialize,
for<'a> &'a T: ToString,
{
match value.as_ref() {
Some(v) => serialize_angle(v, serializer),
None => return serializer.serialize_none(),
}
}
pub fn deserialize_quantity<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: serde::de::Deserializer<'de>,
T: DeserializeOwned + TryFrom<DynQuantity<Complex<f64>>>,
<T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
{
match deserialize_opt_quantity(deserializer)? {
Some(quantity) => Ok(quantity),
None => Err(serde::de::Error::custom("expected a quantity, found none")),
}
}
pub fn deserialize_opt_quantity<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: serde::de::Deserializer<'de>,
T: DeserializeOwned + TryFrom<DynQuantity<Complex<f64>>>,
<T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
{
let quantity = Option::<InnerOrString<_>>::deserialize(deserializer)?;
match quantity {
Some(number_or_string) => {
match number_or_string {
InnerOrString::Inner(quantity) => Ok(Some(quantity)),
#[cfg(feature = "from_str")]
InnerOrString::String(string) => {
let quantity = DynQuantity::<Complex<f64>>::from_str(&string)
.map_err(serde::de::Error::custom)?;
T::try_from(quantity)
.map_err(serde::de::Error::custom)
.map(Some)
}
}
}
None => Ok(None),
}
}
pub fn deserialize_angle<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: serde::de::Deserializer<'de>,
{
match deserialize_opt_angle(deserializer)? {
Some(angle) => Ok(angle),
None => Err(serde::de::Error::custom("expected an angle, found none")),
}
}
pub fn deserialize_opt_angle<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let quantity = Option::<InnerOrString<f64>>::deserialize(deserializer)?;
match quantity {
Some(number_or_string) => {
match number_or_string {
InnerOrString::Inner(quantity) => Ok(Some(quantity)),
#[cfg(feature = "from_str")]
InnerOrString::String(string) => {
let quantity =
DynQuantity::<f64>::from_str(&string).map_err(serde::de::Error::custom)?;
return Ok(Some(quantity.value));
}
}
}
None => Ok(None),
}
}
pub fn deserialize_vec_of_quantities<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: serde::de::Deserializer<'de>,
T: DeserializeOwned + TryFrom<DynQuantity<Complex<f64>>>,
<T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
{
match deserialize_opt_vec_of_quantities(deserializer)? {
Some(vec) => Ok(vec),
None => Err(serde::de::Error::custom("expected a vector, found none")),
}
}
pub fn deserialize_opt_vec_of_quantities<'de, D, T>(
deserializer: D,
) -> Result<Option<Vec<T>>, D::Error>
where
D: serde::de::Deserializer<'de>,
T: DeserializeOwned + TryFrom<DynQuantity<Complex<f64>>>,
<T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
{
match Option::<QuantityVecEnum<T>>::deserialize(deserializer)? {
Some(q) => {
match q {
QuantityVecEnum::Vec(vec) => return Ok(Some(vec)),
QuantityVecEnum::QuantityVec(vec) => return Ok(Some(vec.0)),
#[cfg(feature = "from_str")]
QuantityVecEnum::String(string) => {
fn parse_vec<T, E>(
input: &str,
multiplier: &DynQuantity<Complex<f64>>,
) -> Result<Vec<T>, String>
where
T: TryFrom<DynQuantity<Complex<f64>>, Error = E>,
E: std::fmt::Display,
{
let mut output = Vec::new();
for slice in input.split(&['[', ',', ']'][..]) {
if slice.is_empty() {
continue;
}
let quantity = match DynQuantity::from_str(slice) {
Ok(quantity) => quantity,
Err(_) => continue,
};
let quantity_mult = quantity * multiplier.clone();
let element: T =
quantity_mult.try_into().map_err(|err: E| err.to_string())?;
output.push(element)
}
return Ok(output);
}
match string.find(']') {
Some(byte) => {
if let Some(quantity_str) = string.get(byte + 1..) {
let quantity = DynQuantity::from_str(quantity_str)
.map_err(serde::de::Error::custom)?;
let vec_str = string.get(..(byte + 1)).expect("must not be empty");
return parse_vec(vec_str, &quantity)
.map(Some)
.map_err(serde::de::Error::custom);
} else {
return parse_vec(
&string,
&DynQuantity::new(Complex::new(1.0, 0.0), Unit::default()),
)
.map(Some)
.map_err(serde::de::Error::custom);
};
}
None => {
return Err(serde::de::Error::custom(
"expected a vector, but did not find the closing bracket ]",
));
}
}
}
}
}
None => return Ok(None),
}
}
#[derive(DeserializeUntaggedVerboseError)]
enum QuantityVecEnum<T>
where
T: TryFrom<DynQuantity<Complex<f64>>>,
<T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
{
Vec(Vec<T>),
QuantityVec(QuantityVec<T>),
#[cfg(feature = "from_str")]
String(String),
}
struct QuantityVec<T>(Vec<T>)
where
T: TryFrom<DynQuantity<Complex<f64>>>,
<T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display;
impl<'de, T> Deserialize<'de> for QuantityVec<T>
where
T: serde::de::Deserialize<'de> + TryFrom<DynQuantity<Complex<f64>>>,
<T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Visitor<T> {
marker: PhantomData<T>,
}
impl<'de, T> serde::de::Visitor<'de> for Visitor<T>
where
T: serde::de::Deserialize<'de> + TryFrom<DynQuantity<Complex<f64>>>,
<T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
{
type Value = QuantityVec<T>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a sequence")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut vec: QuantityVec<T> = match seq.size_hint() {
Some(capacity) => QuantityVec(Vec::with_capacity(capacity)),
None => QuantityVec(Vec::new()),
};
let first_value = match seq.next_element::<InnerOrString<T>>()? {
Some(element) => element,
None => return Ok(vec), };
match first_value {
InnerOrString::Inner(number) => {
vec.0.push(number);
while let Some(quantity_rep) = seq.next_element::<InnerOrString<T>>()? {
match quantity_rep {
InnerOrString::Inner(quantity) => vec.0.push(quantity),
#[cfg(feature = "from_str")]
InnerOrString::String(_) => {
return Err(serde::de::Error::custom(
"either all elements of the vector must have the same quantity, or no element must have a quantity",
));
}
}
}
}
#[cfg(feature = "from_str")]
InnerOrString::String(string) => {
let first_element =
DynQuantity::from_str(&string).map_err(serde::de::Error::custom)?;
let output_element =
first_element.try_into().map_err(serde::de::Error::custom)?;
vec.0.push(output_element);
while let Some(quantity_rep) = seq.next_element::<InnerOrString<T>>()? {
match quantity_rep {
InnerOrString::Inner(_) => {
return Err(serde::de::Error::custom(
"either all elements of the vector must have the same quantity, or no element must have a quantity",
));
}
InnerOrString::String(string) => {
let element = DynQuantity::<Complex<f64>>::from_str(&string)
.map_err(serde::de::Error::custom)?;
if element.unit != first_element.unit {
return Err(serde::de::Error::custom(
ConversionError::UnitMismatch {
expected: first_element.unit,
found: element.unit,
},
));
}
let output_element =
element.try_into().map_err(serde::de::Error::custom)?;
vec.0.push(output_element);
}
}
}
}
}
Ok(vec)
}
}
let visitor = Visitor {
marker: PhantomData,
};
deserializer.deserialize_seq(visitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deserialize_number_or_string() {
{
let value: InnerOrString<DynQuantity<f64>> = serde_yaml::from_str("1.0").unwrap();
match value {
InnerOrString::Inner(value) => {
assert_eq!(value.value, 1.0);
}
InnerOrString::String(_) => unreachable!(),
}
}
{
let value: InnerOrString<DynQuantity<f64>> = serde_yaml::from_str("1.0 A").unwrap();
match value {
InnerOrString::Inner(value) => {
assert_eq!(value.value, 1.0);
assert_eq!(value.unit.ampere, 1);
}
InnerOrString::String(_) => unreachable!(),
}
}
{
let value: InnerOrString<DynQuantity<Complex<f64>>> =
serde_yaml::from_str("1.0 A").unwrap();
match value {
InnerOrString::Inner(value) => {
assert_eq!(value.value.re, 1.0);
assert_eq!(value.value.im, 0.0);
assert_eq!(value.unit.ampere, 1);
}
InnerOrString::String(_) => unreachable!(),
}
}
}
}