payjp_types/
expandable.rs

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