Skip to main content

compose_yml/v2/
true_or_struct.rs

1//! Tools for working with fields that might contain true, or might
2//! contain a struct.
3
4use serde::de::{self, Deserialize, Deserializer};
5use serde::ser::{Serialize, Serializer};
6use std::fmt;
7use std::marker::PhantomData;
8
9/// Handle a value which may either a struct that deserializes as type `T`,
10/// or a bare value `true` indicating that we should construct a type `T`
11/// using `Default::default()`.  We do this in a clever way that allows us
12/// to be generic over multiple such types, and which allows us to use the
13/// structure deserealization code automatically generated by serde.
14pub fn deserialize_true_or_struct<'de, T, D>(d: D) -> Result<T, D::Error>
15where
16    T: Deserialize<'de> + Default,
17    D: Deserializer<'de>,
18{
19    /// Declare an internal visitor type to handle our input.
20    struct TrueOrStruct<T>(PhantomData<T>);
21
22    impl<'de, T> de::Visitor<'de> for TrueOrStruct<T>
23    where
24        T: Deserialize<'de> + Default,
25    {
26        type Value = T;
27
28        fn visit_bool<E>(self, value: bool) -> Result<T, E>
29        where
30            E: de::Error,
31        {
32            if value {
33                Ok(Default::default())
34            } else {
35                Err(de::Error::custom(
36                    "expected true or struct, got false".to_owned(),
37                ))
38            }
39        }
40
41        fn visit_map<M>(self, visitor: M) -> Result<T, M::Error>
42        where
43            M: de::MapAccess<'de>,
44        {
45            let mvd = de::value::MapAccessDeserializer::new(visitor);
46            Deserialize::deserialize(mvd)
47        }
48
49        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50            write!(formatter, "a map or a boolean")
51        }
52    }
53
54    d.deserialize_any(TrueOrStruct(PhantomData))
55}
56
57/// Like `opt_true_or_struct`, but it also handles the case where the
58/// value is optional.
59///
60/// TODO MED: Deduplicate with `deserialize_opt_string_or_struct` (not as
61/// easy as it looks).
62pub fn deserialize_opt_true_or_struct<'de, T, D>(d: D) -> Result<Option<T>, D::Error>
63where
64    T: Deserialize<'de> + Default,
65    D: Deserializer<'de>,
66{
67    /// Declare an internal visitor type to handle our input.
68    struct OptTrueOrStruct<T>(PhantomData<T>);
69
70    impl<'de, T> de::Visitor<'de> for OptTrueOrStruct<T>
71    where
72        T: Deserialize<'de> + Default,
73    {
74        type Value = Option<T>;
75
76        fn visit_none<E>(self) -> Result<Self::Value, E>
77        where
78            E: de::Error,
79        {
80            Ok(None)
81        }
82
83        fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
84        where
85            D: Deserializer<'de>,
86        {
87            deserialize_true_or_struct(deserializer).map(Some)
88        }
89
90        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
91            write!(formatter, "a map or a list of strings")
92        }
93    }
94
95    d.deserialize_option(OptTrueOrStruct(PhantomData))
96}
97
98/// Serialize the specified value as `true` if it is equal to
99/// `Default::default()`, and a struct otherwise.
100pub fn serialize_true_or_struct<T, S>(
101    value: &T,
102    serializer: S,
103) -> Result<S::Ok, S::Error>
104where
105    T: Serialize + Default + Eq,
106    S: Serializer,
107{
108    if value == &Default::default() {
109        serializer.serialize_bool(true)
110    } else {
111        value.serialize(serializer)
112    }
113}
114
115/// Like `serialize_true_or_struct`, but can also handle missing values.
116///
117/// TODO MED: Deduplicate with `serialize_opt_string_or_struct` (not as
118/// easy as it looks).
119pub fn serialize_opt_true_or_struct<T, S>(
120    value: &Option<T>,
121    serializer: S,
122) -> Result<S::Ok, S::Error>
123where
124    T: Serialize + Default + Eq,
125    S: Serializer,
126{
127    /// A fun little trick: We need to pass `value` to to `serialize_some`,
128    /// but we don't want `serialize_some` to call the normal `serialize`
129    /// method on it.  So we define a local wrapper type that overrides the
130    /// serialization.  This is one of the more subtle tricks of generic
131    /// programming in Rust: using a "newtype" wrapper struct to override
132    /// how a trait is applied to a class.
133    struct Wrap<'a, T>(&'a T)
134    where
135        T: Serialize + Default + Eq;
136
137    impl<'a, T> Serialize for Wrap<'a, T>
138    where
139        T: 'a + Serialize + Default + Eq,
140    {
141        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
142        where
143            S: Serializer,
144        {
145            match *self {
146                Wrap(v) => serialize_true_or_struct(v, serializer),
147            }
148        }
149    }
150
151    match *value {
152        None => serializer.serialize_none(),
153        Some(ref v) => serializer.serialize_some(&Wrap(v)),
154    }
155}