stripe_types/
expandable.rs

1use serde::Serialize;
2
3use crate::Object;
4
5/// An id or object.
6///
7/// By default stripe will return an id for most fields, but if more detail is
8/// necessary the `expand` parameter can be provided to ask for the id to be
9/// loaded as an object instead.
10///
11/// For more details see <https://stripe.com/docs/api/expanding_objects>.
12#[derive(Clone, Debug, Serialize)] // TODO: Implement deserialize by hand for better error messages with `serde` enabled
13#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
14#[serde(untagged)]
15pub enum Expandable<T: Object> {
16    /// Just the object id.
17    Id(T::Id),
18    /// The entire object.
19    Object(Box<T>),
20}
21
22impl<T: Object> Default for Expandable<T>
23where
24    T::Id: Default,
25{
26    fn default() -> Self {
27        Expandable::Id(Default::default())
28    }
29}
30
31impl<T: Object> Expandable<T> {
32    /// Returns true if we have the object.
33    pub fn is_object(&self) -> bool {
34        match self {
35            Expandable::Id(_) => false,
36            Expandable::Object(_) => true,
37        }
38    }
39
40    /// If the `Expandable` is an object, return that, otherwise return None.
41    pub fn as_object(&self) -> Option<&T> {
42        match self {
43            Expandable::Id(_) => None,
44            Expandable::Object(obj) => Some(obj),
45        }
46    }
47
48    /// If the `Expandable` is an object, return that by taking ownership, otherwise return None.
49    pub fn into_object(self) -> Option<T> {
50        match self {
51            Expandable::Id(_) => None,
52            Expandable::Object(obj) => Some(*obj),
53        }
54    }
55
56    /// Take ownership of the object's id.
57    pub fn into_id(self) -> T::Id {
58        match self {
59            Expandable::Id(id) => id,
60            Expandable::Object(obj) => obj.into_id(),
61        }
62    }
63
64    /// Extract the object's id.
65    pub fn id(&self) -> &T::Id {
66        match self {
67            Expandable::Id(id) => id,
68            Expandable::Object(obj) => obj.id(),
69        }
70    }
71}
72
73#[doc(hidden)]
74mod miniserde {
75    use miniserde::de::{Map, Visitor};
76    use miniserde::json::Value;
77    use miniserde::{Deserialize, make_place};
78
79    use crate::miniserde_helpers::FromValueOpt;
80    use crate::{Expandable, FromCursor, Object};
81    make_place!(Place);
82
83    /// Internal implementation details
84    pub trait MapBuilder {
85        type Out;
86
87        /// Initial state for the builder. Note that this does _not_ match the `Default` trait, it
88        /// matches `miniserde::Deserialize::default` -> specifically we need `Option<Option<>>` to
89        /// default to `Some(None)`
90        fn deser_default() -> Self;
91
92        fn key(&mut self, k: &str) -> miniserde::Result<&mut dyn Visitor>;
93
94        fn take_out(&mut self) -> Option<Self::Out>
95        where
96            Self::Out: Sized;
97    }
98
99    /// Internal implementation details
100    pub trait ObjectDeser
101    where
102        Self: Sized,
103    {
104        type Builder: MapBuilder<Out = Self>;
105    }
106
107    impl<T> Deserialize for Expandable<T>
108    where
109        T: Object + Deserialize + ObjectDeser,
110    {
111        fn begin(out: &mut Option<Self>) -> &mut dyn Visitor {
112            Place::new(out)
113        }
114    }
115
116    struct ExpandableBuilder<'a, T: ObjectDeser + Object> {
117        out: &'a mut Option<Expandable<T>>,
118        builder: T::Builder,
119    }
120
121    impl<T> Map for ExpandableBuilder<'_, T>
122    where
123        T: ObjectDeser + Object,
124    {
125        fn key(&mut self, k: &str) -> miniserde::Result<&mut dyn Visitor> {
126            self.builder.key(k)
127        }
128
129        fn finish(&mut self) -> miniserde::Result<()> {
130            let finalized = self.builder.take_out().ok_or(miniserde::Error)?;
131            *self.out = Some(Expandable::Object(Box::new(finalized)));
132            Ok(())
133        }
134    }
135
136    impl<T> Visitor for Place<Expandable<T>>
137    where
138        T: Object + ObjectDeser,
139    {
140        fn string(&mut self, s: &str) -> miniserde::Result<()> {
141            let val = T::Id::from_cursor(s).ok_or(miniserde::Error)?;
142            self.out = Some(Expandable::Id(val));
143            Ok(())
144        }
145
146        fn map(&mut self) -> miniserde::Result<Box<dyn Map + '_>> {
147            Ok(Box::new(ExpandableBuilder {
148                out: &mut self.out,
149                builder: T::Builder::deser_default(),
150            }))
151        }
152    }
153
154    impl<T: FromValueOpt + Object> FromValueOpt for Expandable<T> {
155        fn from_value(v: Value) -> Option<Self> {
156            match v {
157                Value::String(id) => Some(Self::Id(T::Id::from_cursor(&id)?)),
158                Value::Object(obj) => {
159                    Some(Self::Object(Box::new(T::from_value(Value::Object(obj))?)))
160                }
161                _ => None,
162            }
163        }
164    }
165}
166
167pub use miniserde::{MapBuilder, ObjectDeser};