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}