Skip to main content

dyn_quantity/quantity/
serde_impl.rs

1/*!
2This module is only available if the [`serde`] feature is enabled.
3It provides various functions which can be used to (fallible)
4deserialize a valid [`DynQuantity`] representation into any type `T` which
5implements [`TryFrom<DynQuantity>`]. See the docstring of [`DynQuantity`] for an
6overview over all possible representations.
7*/
8
9use std::cell::Cell;
10#[cfg(feature = "from_str")]
11use std::str::FromStr;
12
13use serde::ser::{Serialize, SerializeStruct, Serializer};
14
15use super::F64RealOrComplex;
16use crate::error::{ConversionError, ParseError};
17use crate::unit::Unit;
18
19impl<V> Serialize for DynQuantity<V>
20where
21    V: F64RealOrComplex + Serialize,
22{
23    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
24    where
25        S: Serializer,
26    {
27        let mut state = serializer.serialize_struct("DynQuantity", 2)?;
28        state.serialize_field("value", &self.value)?;
29        state.serialize_field("unit", &self.unit)?;
30        state.end()
31    }
32}
33
34impl<'de, V> Deserialize<'de> for DynQuantity<V>
35where
36    V: F64RealOrComplex + Deserialize<'de>,
37{
38    fn deserialize<D>(deserializer: D) -> Result<DynQuantity<V>, D::Error>
39    where
40        D: Deserializer<'de>,
41    {
42        let variants = QuantityVariants::<V>::deserialize(deserializer)?;
43        variants.try_into().map_err(serde::de::Error::custom)
44    }
45}
46
47/**
48A [`DynQuantity`] can be deserialized a couple of different representations.
49 */
50#[derive(DeserializeUntaggedVerboseError)]
51enum QuantityVariants<V>
52where
53    V: F64RealOrComplex,
54{
55    /**
56    Native representation of [`DynQuantity`] (via an alias struct in order
57    to avoid infinite recursion)-
58     */
59    Quantity(QuantityAlias<V>),
60    /**
61    String representation using the [`std::str::FromStr`] implementation for
62    [`DynQuantity`].
63
64    Only available if the `from_str` feature is enabled.
65     */
66    #[cfg(feature = "from_str")]
67    String(String),
68    /**
69    A value without any units - in that case, the unit exponents are assumed
70    to be zero and the value to be dimensionless.
71     */
72    Value(V),
73}
74
75#[derive(serde::Deserialize)]
76struct QuantityAlias<V: F64RealOrComplex> {
77    value: V,
78    unit: Unit,
79}
80
81impl<V: F64RealOrComplex> TryFrom<QuantityVariants<V>> for DynQuantity<V> {
82    type Error = ParseError;
83
84    fn try_from(variant: QuantityVariants<V>) -> Result<Self, Self::Error> {
85        match variant {
86            QuantityVariants::Quantity(variant) => {
87                return Ok(Self {
88                    value: variant.value,
89                    unit: variant.unit,
90                });
91            }
92            #[cfg(feature = "from_str")]
93            QuantityVariants::String(string) => {
94                return Self::from_str(&string);
95            }
96            QuantityVariants::Value(value) => {
97                return Ok(Self {
98                    value,
99                    unit: Unit::default(),
100                });
101            }
102        }
103    }
104}
105
106use std::marker::PhantomData;
107
108use crate::DynQuantity;
109use deserialize_untagged_verbose_error::DeserializeUntaggedVerboseError;
110use num::Complex;
111use serde::{Deserialize, Deserializer, de::DeserializeOwned};
112
113#[derive(DeserializeUntaggedVerboseError)]
114enum InnerOrString<T> {
115    Inner(T),
116    #[cfg(feature = "from_str")]
117    String(String),
118}
119
120thread_local!(
121    /**
122    A thread-local, static variable which enables / disables serialization of
123    quantities with or without units. It is used within the functions
124    [`serialize_quantity`], [`serialize_opt_quantity`], [`serialize_angle`] and
125    [`serialize_opt_angle`] as a thread-local context to decide whether a
126    quantity should be serialized with or without its units. By default, its
127    value is `false`, meaning that quantities are serialized without their
128    units. The [`serialize_with_units`] function sets it temporarily to `true`,
129    then performs the actual serialization, and afterwards resets it to `false`
130    again (return to default behaviour).
131
132    Direct interaction with this variable is only necessary when writing a
133    multithreaded serializer, see the docstring of [`serialize_with_units`].
134    Otherwise, it is highly recommended to leave this variable alone.
135    */
136    pub static SERIALIZE_WITH_UNITS: Cell<bool> = Cell::new(false)
137);
138
139/**
140A wrapper around a serialization function / closure which enables serialization
141with units.
142
143# Overview
144
145By default, a quantity or angle is often serialized as its raw value in base SI
146units, which is very efficient. Sometimes, it might be useful to store a value
147together with its units. If a struct field uses [`serialize_quantity`],
148[`serialize_opt_quantity`], [`serialize_angle`] or [`serialize_opt_angle`]
149for serialization and this wrapper is applied to the actual serialization
150function, then the units are stored together with the raw value in a string.
151
152# Multithreaded serialization
153
154This function is a thin wrapper around the passed in serialization function
155which just sets [`SERIALIZE_WITH_UNITS`] to `true`, then calls the passed in
156function, stores the result, then sets [`SERIALIZE_WITH_UNITS`] back to `false`
157and finally returns the result. Since [`SERIALIZE_WITH_UNITS`] is a thread-local
158variable, it will be set to its default value `false` if a new thread is created
159inside the passed function. Hence, a multithreaded serializer needs to adjust
160the value of [`SERIALIZE_WITH_UNITS`] in each thread it creates - which is why
161[`SERIALIZE_WITH_UNITS`] is exposed in the first place.
162
163# Examples
164
165```
166use serde::{Serialize};
167use uom::si::{f64::Length, length::{millimeter, kilometer}};
168use dyn_quantity::*;
169use indoc::indoc;
170
171#[derive(Serialize, Debug)]
172struct Quantities {
173    #[serde(serialize_with = "serialize_quantity")]
174    length: Length,
175    #[serde(serialize_with = "serialize_opt_quantity")]
176    opt_length: Option<Length>,
177    #[serde(serialize_with = "serialize_angle")]
178    angle: f64,
179    #[serde(serialize_with = "serialize_opt_angle")]
180    opt_angle: Option<f64>,
181}
182
183let quantities = Quantities {
184    length: Length::new::<millimeter>(1.0),
185    opt_length: Some(Length::new::<kilometer>(1.0)),
186    angle: 1.0,
187    opt_angle: Some(2.0),
188};
189
190// Without units (standard serialization)
191let expected = indoc! {"
192---
193length: 0.001
194opt_length: 1000.0
195angle: 1.0
196opt_angle: 2.0
197
198"};
199let actual = serde_yaml::to_string(&quantities).expect("serialization succeeds");
200assert_eq!(expected, actual);
201
202// With units
203let expected = indoc! {"
204---
205length: 0.001 m
206opt_length: 1000 m
207angle: 1 rad
208opt_angle: 2 rad
209
210"};
211let actual = serialize_with_units(||{serde_yaml::to_string(&quantities)}).expect("serialization succeeds");
212assert_eq!(expected, actual);
213```
214 */
215pub fn serialize_with_units<F, R>(f: F) -> R
216where
217    F: FnOnce() -> R,
218{
219    SERIALIZE_WITH_UNITS.with(|ctx| {
220        ctx.set(true);
221        let res = f();
222        ctx.set(false);
223        res
224    })
225}
226
227/**
228Enables serialization of a quantity (any type implementing
229[`Into<DynQuantity>`]) into a string containing both the value and the units.
230
231When a value is serialized using [`serialize_with_units`], this function stores
232a quantity as a string containing both the raw value and the units.
233If [`serialize_with_units`] is not used, this function serializes its field
234using the default [`Serialize`] implementation of the type.
235
236For examples see the [`serialize_with_units`] documentation.
237 */
238pub fn serialize_quantity<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
239where
240    S: serde::ser::Serializer,
241    T: Serialize + Clone + Into<DynQuantity<Complex<f64>>>,
242{
243    SERIALIZE_WITH_UNITS.with(|ctx| {
244        if ctx.get() {
245            let quantity: DynQuantity<Complex<f64>> = value.clone().into();
246            let string = quantity.to_string();
247            string.serialize(serializer)
248        } else {
249            value.serialize(serializer)
250        }
251    })
252}
253
254/**
255Like [`serialize_quantity`], but serializes an [`&Option<T>`]
256instead of a `&T` implementing [`Into<DynQuantity>`].
257
258For examples see the [`serialize_with_units`] documentation.
259 */
260pub fn serialize_opt_quantity<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
261where
262    S: serde::ser::Serializer,
263    T: Serialize + Clone + Into<DynQuantity<Complex<f64>>>,
264{
265    match value.as_ref() {
266        Some(v) => return serialize_quantity(v, serializer),
267        None => return serializer.serialize_none(),
268    }
269}
270
271/**
272Enables serialization of an angle into a string containing both the value and
273the "rad" unit.
274
275When a value is serialized using [`serialize_with_units`], this function stores
276an angle as a string containing both the raw value and the "rad" unit.
277If [`serialize_with_units`] is not used, this function serializes its field
278using the default [`Serialize`] implementation of the type.
279
280For examples see the [`serialize_with_units`] documentation.
281 */
282pub fn serialize_angle<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
283where
284    S: serde::ser::Serializer,
285    T: Serialize,
286    for<'a> &'a T: ToString,
287{
288    SERIALIZE_WITH_UNITS.with(|ctx| {
289        if ctx.get() {
290            let mut string = value.to_string();
291            string.push_str(" rad");
292            string.serialize(serializer)
293        } else {
294            value.serialize(serializer)
295        }
296    })
297}
298
299/**
300Like [`serialize_angle`], but serializes an [`&Option<T>`]
301instead of a `&T` implementing [`Into<DynQuantity>`].
302
303For examples see the [`serialize_with_units`] documentation.
304 */
305pub fn serialize_opt_angle<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
306where
307    S: serde::ser::Serializer,
308    T: Serialize,
309    for<'a> &'a T: ToString,
310{
311    match value.as_ref() {
312        Some(v) => serialize_angle(v, serializer),
313        None => return serializer.serialize_none(),
314    }
315}
316
317// =============================================================================
318
319/**
320Deserializes a type `T` implementing [`TryFrom<DynQuantity>`] from a valid
321[`DynQuantity`] representation (see docstring of [`DynQuantity`]).
322
323This function is meant to be used in conjunction with [`serde`]s `Deserialize`
324macro and the `deserialize_with` annotation:
325
326# Examples
327```
328use serde::{Deserialize};
329use uom::si::{f64::Length, length::meter};
330use dyn_quantity::deserialize_quantity;
331use indoc::indoc;
332
333#[derive(Deserialize, Debug)]
334struct LengthWrapper {
335    #[serde(deserialize_with = "deserialize_quantity")]
336    length: Length,
337}
338
339let ser = indoc! {"
340---
341length: 1200 mm
342"};
343let wrapper: LengthWrapper = serde_yaml::from_str(&ser).unwrap();
344assert_eq!(wrapper.length.get::<meter>(), 1.2);
345```
346 */
347pub fn deserialize_quantity<'de, D, T>(deserializer: D) -> Result<T, D::Error>
348where
349    D: serde::de::Deserializer<'de>,
350    T: DeserializeOwned + TryFrom<DynQuantity<Complex<f64>>>,
351    <T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
352{
353    match deserialize_opt_quantity(deserializer)? {
354        Some(quantity) => Ok(quantity),
355        None => Err(serde::de::Error::custom("expected a quantity, found none")),
356    }
357}
358
359/**
360Like [`deserialize_quantity`], but deserializes into an [`Option<T>`]
361instead of a `T` implementing [`TryFrom<DynQuantity>`].
362
363# Examples
364```
365use serde::{Deserialize};
366use uom::si::{f64::Length, length::meter};
367use dyn_quantity::deserialize_opt_quantity;
368use indoc::indoc;
369
370#[derive(Deserialize, Debug)]
371struct OptLengthWrapper {
372    #[serde(deserialize_with = "deserialize_opt_quantity")]
373    opt_length: Option<Length>,
374}
375
376let ser = indoc! {"
377---
378opt_length: 1200 mm
379"};
380let wrapper: OptLengthWrapper = serde_yaml::from_str(&ser).unwrap();
381assert_eq!(wrapper.opt_length.unwrap().get::<meter>(), 1.2);
382
383let ser = indoc! {"
384---
385opt_length:
386"};
387let wrapper: OptLengthWrapper = serde_yaml::from_str(&ser).unwrap();
388assert!(wrapper.opt_length.is_none());
389```
390 */
391pub fn deserialize_opt_quantity<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
392where
393    D: serde::de::Deserializer<'de>,
394    T: DeserializeOwned + TryFrom<DynQuantity<Complex<f64>>>,
395    <T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
396{
397    let quantity = Option::<InnerOrString<_>>::deserialize(deserializer)?;
398    match quantity {
399        Some(number_or_string) => {
400            match number_or_string {
401                InnerOrString::Inner(quantity) => Ok(Some(quantity)),
402                #[cfg(feature = "from_str")]
403                InnerOrString::String(string) => {
404                    // Deserialize using the SI unit parser
405                    let quantity = DynQuantity::<Complex<f64>>::from_str(&string)
406                        .map_err(serde::de::Error::custom)?;
407                    T::try_from(quantity)
408                        .map_err(serde::de::Error::custom)
409                        .map(Some)
410                }
411            }
412        }
413        None => Ok(None),
414    }
415}
416
417/**
418Deserializes an angle from a valid [`DynQuantity`] representation (see
419docstring of [`DynQuantity`]). The output value is always in radians.
420
421This function is meant to be used in conjunction with [`serde`]s `Deserialize`
422macro and the `deserialize_with` annotation:
423
424# Examples
425```
426use serde::{Deserialize};
427use dyn_quantity::deserialize_angle;
428use indoc::indoc;
429use std::f64::consts::PI;
430
431#[derive(Deserialize, Debug)]
432struct AngleWrapper {
433    #[serde(deserialize_with = "deserialize_angle")]
434    angle: f64,
435}
436
437let ser = indoc! {"
438---
439angle: 360 / 2 degree
440"};
441let wrapper: AngleWrapper = serde_yaml::from_str(&ser).unwrap();
442assert_eq!(wrapper.angle, PI);
443```
444 */
445pub fn deserialize_angle<'de, D>(deserializer: D) -> Result<f64, D::Error>
446where
447    D: serde::de::Deserializer<'de>,
448{
449    match deserialize_opt_angle(deserializer)? {
450        Some(angle) => Ok(angle),
451        None => Err(serde::de::Error::custom("expected an angle, found none")),
452    }
453}
454
455/**
456Like [`deserialize_angle`], but deserializes into an [`Option<f64>`]
457instead of a [`f64`].
458
459# Examples
460```
461use serde::{Deserialize};
462use dyn_quantity::deserialize_opt_angle;
463use indoc::indoc;
464
465#[derive(Deserialize, Debug)]
466struct OptAngleWrapper {
467    #[serde(deserialize_with = "deserialize_opt_angle")]
468    opt_angle: Option<f64>,
469}
470
471let ser = indoc! {"
472---
473opt_angle: 2 rad
474"};
475let wrapper: OptAngleWrapper = serde_yaml::from_str(&ser).unwrap();
476assert_eq!(wrapper.opt_angle.unwrap(), 2.0);
477
478let ser = indoc! {"
479---
480opt_angle:
481"};
482let wrapper: OptAngleWrapper = serde_yaml::from_str(&ser).unwrap();
483assert!(wrapper.opt_angle.is_none());
484```
485 */
486pub fn deserialize_opt_angle<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
487where
488    D: serde::de::Deserializer<'de>,
489{
490    let quantity = Option::<InnerOrString<f64>>::deserialize(deserializer)?;
491    match quantity {
492        Some(number_or_string) => {
493            match number_or_string {
494                InnerOrString::Inner(quantity) => Ok(Some(quantity)),
495                #[cfg(feature = "from_str")]
496                InnerOrString::String(string) => {
497                    // Deserialize using the SI unit parser
498                    let quantity =
499                        DynQuantity::<f64>::from_str(&string).map_err(serde::de::Error::custom)?;
500                    return Ok(Some(quantity.value));
501                }
502            }
503        }
504        None => Ok(None),
505    }
506}
507
508/**
509Deserializes a vector of `T` which implements [`TryFrom<DynQuantity>`] from:
510
5111) A vector representation of [`DynQuantity`] or
5122) A string representing a vector of numbers with a unit behind it.
513
514# Examples:
515```
516use indoc::indoc;
517use serde::{Deserialize};
518use uom::si::{f64::Length, length::meter};
519use dyn_quantity::deserialize_vec_of_quantities;
520
521#[derive(Deserialize, Debug)]
522struct VecWrapper {
523    #[serde(deserialize_with = "deserialize_vec_of_quantities")]
524    vec: Vec<Length>,
525}
526
527// Variant 1: Vector representation of `DynQuantity`
528let ser = indoc! {"
529---
530vec: [1 m, 2 mm, 3 km]
531"};
532let wrapper: VecWrapper = serde_yaml::from_str(&ser).unwrap();
533assert_eq!(wrapper.vec[0].get::<meter>(), 1.0);
534assert_eq!(wrapper.vec[1].get::<meter>(), 0.002);
535assert_eq!(wrapper.vec[2].get::<meter>(), 3000.0);
536
537// Variant 2: Vector of numbers with unit at the end
538let ser = indoc! {"
539---
540vec: '[1, 2e-3, 3e3] m'
541"};
542let wrapper: VecWrapper = serde_yaml::from_str(&ser).unwrap();
543assert_eq!(wrapper.vec[0].get::<meter>(), 1.0);
544assert_eq!(wrapper.vec[1].get::<meter>(), 0.002);
545assert_eq!(wrapper.vec[2].get::<meter>(), 3000.0);
546```
547 */
548pub fn deserialize_vec_of_quantities<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
549where
550    D: serde::de::Deserializer<'de>,
551    T: DeserializeOwned + TryFrom<DynQuantity<Complex<f64>>>,
552    <T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
553{
554    match deserialize_opt_vec_of_quantities(deserializer)? {
555        Some(vec) => Ok(vec),
556        None => Err(serde::de::Error::custom("expected a vector, found none")),
557    }
558}
559
560/**
561Like [`deserialize_vec_of_quantities`], but deserializes into an [`Option<Vec<T>>`]
562instead of a [`Vec<T>`].
563
564# Examples:
565```
566use indoc::indoc;
567use serde::{Deserialize};
568use uom::si::{f64::Length, length::meter};
569use dyn_quantity::deserialize_opt_vec_of_quantities;
570
571#[derive(Deserialize, Debug)]
572struct OptVecWrapper {
573    #[serde(deserialize_with = "deserialize_opt_vec_of_quantities")]
574    vec: Option<Vec<Length>>,
575}
576
577// Vector given (variant 1)
578let ser = indoc! {"
579---
580vec: [1 m, 2 mm, 3 km]
581"};
582let wrapper: OptVecWrapper = serde_yaml::from_str(&ser).unwrap();
583let vec = wrapper.vec.unwrap();
584assert_eq!(vec[0].get::<meter>(), 1.0);
585assert_eq!(vec[1].get::<meter>(), 0.002);
586assert_eq!(vec[2].get::<meter>(), 3000.0);
587
588// No vector given
589let ser = indoc! {"
590---
591vec:
592"};
593let wrapper: OptVecWrapper = serde_yaml::from_str(&ser).unwrap();
594assert!(wrapper.vec.is_none());
595```
596*/
597pub fn deserialize_opt_vec_of_quantities<'de, D, T>(
598    deserializer: D,
599) -> Result<Option<Vec<T>>, D::Error>
600where
601    D: serde::de::Deserializer<'de>,
602    T: DeserializeOwned + TryFrom<DynQuantity<Complex<f64>>>,
603    <T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
604{
605    match Option::<QuantityVecEnum<T>>::deserialize(deserializer)? {
606        Some(q) => {
607            match q {
608                QuantityVecEnum::Vec(vec) => return Ok(Some(vec)),
609                QuantityVecEnum::QuantityVec(vec) => return Ok(Some(vec.0)),
610                #[cfg(feature = "from_str")]
611                QuantityVecEnum::String(string) => {
612                    fn parse_vec<T, E>(
613                        input: &str,
614                        multiplier: &DynQuantity<Complex<f64>>,
615                    ) -> Result<Vec<T>, String>
616                    where
617                        T: TryFrom<DynQuantity<Complex<f64>>, Error = E>,
618                        E: std::fmt::Display,
619                    {
620                        let mut output = Vec::new();
621                        for slice in input.split(&['[', ',', ']'][..]) {
622                            // Skip slices which are empty or contain
623                            if slice.is_empty() {
624                                continue;
625                            }
626                            let quantity = match DynQuantity::from_str(slice) {
627                                Ok(quantity) => quantity,
628                                Err(_) => continue,
629                            };
630                            let quantity_mult = quantity * multiplier.clone();
631                            let element: T =
632                                quantity_mult.try_into().map_err(|err: E| err.to_string())?;
633                            output.push(element)
634                        }
635                        return Ok(output);
636                    }
637
638                    // Remove the unit from the string by finding the closing
639                    // bracket of the vector "]". The slice before the closing
640                    // bracket can then be deserialized as a vector of floats,
641                    // while the slice behind the closing bracket can be
642                    // interpreted as Unit.
643                    match string.find(']') {
644                        Some(byte) => {
645                            if let Some(quantity_str) = string.get(byte + 1..) {
646                                let quantity = DynQuantity::from_str(quantity_str)
647                                    .map_err(serde::de::Error::custom)?;
648
649                                let vec_str = string.get(..(byte + 1)).expect("must not be empty");
650                                return parse_vec(vec_str, &quantity)
651                                    .map(Some)
652                                    .map_err(serde::de::Error::custom);
653                            } else {
654                                return parse_vec(
655                                    &string,
656                                    &DynQuantity::new(Complex::new(1.0, 0.0), Unit::default()),
657                                )
658                                .map(Some)
659                                .map_err(serde::de::Error::custom);
660                            };
661                        }
662                        None => {
663                            return Err(serde::de::Error::custom(
664                                "expected a vector, but did not find the closing bracket ]",
665                            ));
666                        }
667                    }
668                }
669            }
670        }
671        None => return Ok(None),
672    }
673}
674
675#[derive(DeserializeUntaggedVerboseError)]
676enum QuantityVecEnum<T>
677where
678    T: TryFrom<DynQuantity<Complex<f64>>>,
679    <T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
680{
681    Vec(Vec<T>),
682    QuantityVec(QuantityVec<T>),
683    #[cfg(feature = "from_str")]
684    String(String),
685}
686
687struct QuantityVec<T>(Vec<T>)
688where
689    T: TryFrom<DynQuantity<Complex<f64>>>,
690    <T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display;
691
692impl<'de, T> Deserialize<'de> for QuantityVec<T>
693where
694    T: serde::de::Deserialize<'de> + TryFrom<DynQuantity<Complex<f64>>>,
695    <T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
696{
697    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
698    where
699        D: Deserializer<'de>,
700    {
701        struct Visitor<T> {
702            marker: PhantomData<T>,
703        }
704
705        impl<'de, T> serde::de::Visitor<'de> for Visitor<T>
706        where
707            T: serde::de::Deserialize<'de> + TryFrom<DynQuantity<Complex<f64>>>,
708            <T as TryFrom<DynQuantity<Complex<f64>>>>::Error: std::fmt::Display,
709        {
710            type Value = QuantityVec<T>;
711
712            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
713                formatter.write_str("a sequence")
714            }
715
716            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
717            where
718                A: serde::de::SeqAccess<'de>,
719            {
720                let mut vec: QuantityVec<T> = match seq.size_hint() {
721                    Some(capacity) => QuantityVec(Vec::with_capacity(capacity)),
722                    None => QuantityVec(Vec::new()),
723                };
724
725                let first_value = match seq.next_element::<InnerOrString<T>>()? {
726                    Some(element) => element,
727                    None => return Ok(vec), // Empty vec
728                };
729
730                match first_value {
731                    InnerOrString::Inner(number) => {
732                        vec.0.push(number);
733                        while let Some(quantity_rep) = seq.next_element::<InnerOrString<T>>()? {
734                            match quantity_rep {
735                                InnerOrString::Inner(quantity) => vec.0.push(quantity),
736                                #[cfg(feature = "from_str")]
737                                InnerOrString::String(_) => {
738                                    return Err(serde::de::Error::custom(
739                                        "either all elements of the vector must have the same quantity, or no element must have a quantity",
740                                    ));
741                                }
742                            }
743                        }
744                    }
745                    #[cfg(feature = "from_str")]
746                    InnerOrString::String(string) => {
747                        let first_element =
748                            DynQuantity::from_str(&string).map_err(serde::de::Error::custom)?;
749                        let output_element =
750                            first_element.try_into().map_err(serde::de::Error::custom)?;
751                        vec.0.push(output_element);
752
753                        while let Some(quantity_rep) = seq.next_element::<InnerOrString<T>>()? {
754                            // Loop through all other elements and check if their unit is equal to
755                            // first_element_unit
756                            match quantity_rep {
757                                InnerOrString::Inner(_) => {
758                                    return Err(serde::de::Error::custom(
759                                        "either all elements of the vector must have the same quantity, or no element must have a quantity",
760                                    ));
761                                }
762                                InnerOrString::String(string) => {
763                                    let element = DynQuantity::<Complex<f64>>::from_str(&string)
764                                        .map_err(serde::de::Error::custom)?;
765                                    if element.unit != first_element.unit {
766                                        return Err(serde::de::Error::custom(
767                                            ConversionError::UnitMismatch {
768                                                expected: first_element.unit,
769                                                found: element.unit,
770                                            },
771                                        ));
772                                    }
773                                    let output_element =
774                                        element.try_into().map_err(serde::de::Error::custom)?;
775                                    vec.0.push(output_element);
776                                }
777                            }
778                        }
779                    }
780                }
781
782                Ok(vec)
783            }
784        }
785
786        let visitor = Visitor {
787            marker: PhantomData,
788        };
789        deserializer.deserialize_seq(visitor)
790    }
791}
792
793#[cfg(test)]
794mod tests {
795    use super::*;
796
797    #[test]
798    fn test_deserialize_number_or_string() {
799        {
800            let value: InnerOrString<DynQuantity<f64>> = serde_yaml::from_str("1.0").unwrap();
801            match value {
802                InnerOrString::Inner(value) => {
803                    assert_eq!(value.value, 1.0);
804                }
805                InnerOrString::String(_) => unreachable!(),
806            }
807        }
808        {
809            let value: InnerOrString<DynQuantity<f64>> = serde_yaml::from_str("1.0 A").unwrap();
810            match value {
811                InnerOrString::Inner(value) => {
812                    assert_eq!(value.value, 1.0);
813                    assert_eq!(value.unit.ampere, 1);
814                }
815                InnerOrString::String(_) => unreachable!(),
816            }
817        }
818        {
819            let value: InnerOrString<DynQuantity<Complex<f64>>> =
820                serde_yaml::from_str("1.0 A").unwrap();
821            match value {
822                InnerOrString::Inner(value) => {
823                    assert_eq!(value.value.re, 1.0);
824                    assert_eq!(value.value.im, 0.0);
825                    assert_eq!(value.unit.ampere, 1);
826                }
827                InnerOrString::String(_) => unreachable!(),
828            }
829        }
830    }
831}