lazy_borink/
lib.rs

1//! # `lazy-borink`
2//! `lazy-borink` exposes one struct, [Lazy], and a trait, [UnwrapLazy]. See [Lazy] for additional information.
3
4use base64::{engine::general_purpose as b64, Engine as _};
5#[cfg(feature = "derive")]
6pub use lazy_borink_derive::UnwrapLazy;
7pub use rmp_serde::decode::Error;
8use serde::de::{self, DeserializeOwned, Visitor};
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10use std::fmt::{self, Debug};
11use std::marker::PhantomData;
12
13/// `Lazy` is a lazy (de)serialization type. It only serializes its data when the entire struct its contained in is serialized and only deserializes into
14/// its inner type when specifically asked to do so.
15///
16/// Consider some information about a client. You might want to send this to the client for storage and retrieve it at a later date. However, the struct contains some data only
17/// useful to the server. An example:
18/// ```
19/// # struct ServerData {}
20/// struct ClientInfo {
21///     client_id: String,
22///     server_data: ServerData
23/// }
24/// ```
25///
26/// You don't want to have to expose the inner structure of ServerData in your public API. But if you serialize it to e.g. JSON, the structure is easily visible to the client.
27/// The client might also need to define the structure if they want to conveniently deserialize it. Instead, wrap ServerData in `Lazy`:
28/// ```
29/// # use serde::{Deserialize, Serialize};
30/// # #[derive(Serialize, Deserialize)]
31/// # struct ServerData {}
32/// use lazy_borink::Lazy;
33///
34/// #[derive(Serialize, Deserialize)]
35/// struct ClientInfo {
36///     client_id: String,
37///     server_data: Lazy<ServerData>
38/// }
39/// ```
40///
41/// `Lazy` has custom implementations for [serde::Serialize] and [serde::Deserialize] to make this all possible. It is compatible with both JSON and MessagePack and has tests for both.
42///
43/// Example serialization:
44///
45/// ```
46/// # use serde::{Deserialize, Serialize};
47/// # #[derive(Serialize, Deserialize)]
48/// # struct ServerData { data: String }
49/// # use lazy_borink::Lazy;
50/// # #[derive(Serialize, Deserialize)]
51/// # struct ClientInfo {
52/// #     client_id: String,
53/// #     server_data: Lazy<ServerData>
54/// # }
55/// // ServerData can be anything, as long as it implements Serialize
56/// let server_data = ServerData { data: "some_data".to_owned() };
57/// let client_info = ClientInfo { client_id: "some_id".to_owned(), server_data: Lazy::from_inner(server_data) };
58/// let client_json = serde_json::to_string(&client_info).unwrap();
59///
60/// assert_eq!(client_json, "{\"client_id\":\"some_id\",\"server_data\":\"gaRkYXRhqXNvbWVfZGF0YQ\"}");
61/// ```
62///
63/// Instantiating a Lazy type is free, it doesn't yet do any serialization. But if we then serialize and deserialize, it will still have the original bytes.
64/// However, `Lazy<T>` implements the trait [UnwrapLazy] if T implements UnwrapLazy, allowing us to call [UnwrapLazy::unwrap_lazy] to get the structured data.
65///
66/// ```
67/// use lazy_borink::UnwrapLazy;
68/// # use lazy_borink::Lazy;
69/// # use serde::{Deserialize, Serialize};
70/// # #[derive(Serialize, Deserialize, Debug, UnwrapLazy)]
71/// # struct ServerData { data: String }
72/// # let server_data = ServerData { data: "some_data".to_owned() };
73///
74/// let lazy = Lazy::from_inner(server_data);
75/// assert_eq!(format!("{:?}", lazy), "ServerData { data: \"some_data\" }");
76/// let bytes = rmp_serde::encode::to_vec_named(&lazy).unwrap();
77/// let decoded: Lazy<ServerData> = rmp_serde::decode::from_slice(&bytes).unwrap();
78/// assert_eq!(format!("{:?}", decoded), "Lazy { bytes: [129, 164, 100, 97, 116, 97, 169, 115, 111, 109, 101, 95, 100, 97, 116, 97] }");
79/// let structured = decoded.unwrap_lazy();
80/// assert_eq!(format!("{:?}", structured), "ServerData { data: \"some_data\" }");
81/// ```
82///
83/// You can also automatically derive [UnwrapLazy] (this is gated behind the "derive" feature, which is enabled by default).
84///
85/// Other convenience methods are also available on the `Lazy` type, in case you only want the serialized binary data or just the inner structured type.
86/// See [Lazy::inner], [Lazy::take], [Lazy::bytes], [Lazy::take_bytes].
87///
88/// Note that `Lazy`` assumes a correct implementation of Deserialize and Serialize for the inner structs. If one has to deal with possible incorrect data,
89/// there is also [Lazy::try_inner], [Lazy::try_take] and [UnwrapLazy::try_unwrap_lazy], which return a Result with [rmp_serde::decode::Error].
90/// Furthermore, it is always true that only one of bytes or inner ever holds a value, never both or neither.
91#[derive(Clone, PartialEq, Eq)]
92pub struct Lazy<T> {
93    bytes: Option<Vec<u8>>,
94    inner: Option<T>,
95}
96
97impl<T> Debug for Lazy<T>
98where
99    T: Debug,
100{
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        if let Some(inner) = &self.inner {
103            write!(f, "{:?}", inner)?;
104        } else if let Some(bytes) = &self.bytes {
105            f.debug_struct("Lazy").field("bytes", bytes).finish()?;
106        }
107
108        Ok(())
109    }
110}
111
112fn deserialize_lazy<T: DeserializeOwned>(bytes: &[u8]) -> Result<T, Error> {
113    rmp_serde::decode::from_slice(bytes)
114}
115
116fn serialize_lazy<T: Serialize>(inner: &T) -> Vec<u8> {
117    rmp_serde::encode::to_vec_named(inner).unwrap()
118}
119
120impl<T> Lazy<T> {
121    /// Instantiates a Lazy type with bytes. Does not do any (de)serialization.
122    pub fn from_bytes(bytes: Vec<u8>) -> Self {
123        Lazy {
124            bytes: Some(bytes),
125            inner: None,
126        }
127    }
128
129    /// Instantiates a Lazy type with the inner type. Does not do any (de)serialization.
130    pub fn from_inner(inner: T) -> Self {
131        Lazy {
132            bytes: None,
133            inner: Some(inner),
134        }
135    }
136
137    /// Returns `true` if the inner type is already deserialized. If this is the case, [Lazy::inner] and [Lazy::take] can be safely called.
138    pub fn is_deserialized(&self) -> bool {
139        self.inner.is_some()
140    }
141}
142
143impl<T> From<T> for Lazy<T> {
144    fn from(value: T) -> Self {
145        Lazy::from_inner(value)
146    }
147}
148
149impl<T> Lazy<T>
150where
151    T: DeserializeOwned,
152{
153    /// Try to get a reference to the inner value. If it is already in its deserialized state, it will simply return a reference. Otherwise,
154    /// it will try to deserialize the binary data to type `T`, discarding the binary data in the process. If it is unable to deserialize, it
155    /// returns the [rmp_serde::decode::Error].
156    pub fn try_inner(&mut self) -> Result<&T, Error> {
157        if let Some(bytes) = self.bytes.take() {
158            let inner = deserialize_lazy(&bytes)?;
159            self.inner = Some(inner);
160
161            self.try_inner()
162        } else if let Some(inner) = &self.inner {
163            Ok(inner)
164        } else {
165            panic!("Lazy structure is invalid, it contains no data!")
166        }
167    }
168
169    /// Similar to [Lazy::try_inner], but moves the data out of the original Lazy type and returns the owned `T`.
170    pub fn try_take(self) -> Result<T, Error> {
171        if let Some(bytes) = self.bytes {
172            deserialize_lazy(&bytes)
173        } else if let Some(inner) = self.inner {
174            Ok(inner)
175        } else {
176            panic!("Lazy structure is invalid, it contains no data!")
177        }
178    }
179
180    /// Convenience function that unwraps the result from [Lazy::try_inner]. Panics if deserialization failed. Can be safely called if already in a deserialized state.
181    pub fn inner(&mut self) -> &T {
182        self.try_inner().unwrap()
183    }
184
185    /// Convenience function that unwraps the result from [Lazy::try_take]. Panics if deserialization failed. Can be safely called if already in a deserialized state.
186    pub fn take(self) -> T {
187        self.try_take().unwrap()
188    }
189}
190
191impl<T> Lazy<T>
192where
193    T: Serialize,
194{
195    /// If in a deserialized state, it serializes the inner data using MessagePack and discards the inner T. It stores the serialized data in itself and returns a reference.
196    /// It assumes the Serialize implementation is correct and does not fail. If it is not in a deserialized state, it will simply return a reference to the inner bytes.
197    pub fn bytes(&mut self) -> &[u8] {
198        if let Some(inner) = self.inner.take() {
199            let bytes = serialize_lazy(&inner);
200            self.bytes = Some(bytes);
201
202            self.bytes()
203        } else if let Some(bytes) = &self.bytes {
204            bytes
205        } else {
206            panic!("Lazy structure is invalid, it contains no data!")
207        }
208    }
209
210    /// Similar to [Lazy::take_bytes], but moves the data out of the `Lazy` and returns the owned bytes.
211    pub fn take_bytes(self) -> Vec<u8> {
212        if let Some(inner) = self.inner {
213            serialize_lazy(&inner)
214        } else if let Some(bytes) = self.bytes {
215            bytes
216        } else {
217            panic!("Lazy structure is invalid, it contains no data!")
218        }
219    }
220}
221
222impl<'de, T> Deserialize<'de> for Lazy<T>
223where
224    T: DeserializeOwned,
225{
226    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
227    where
228        D: Deserializer<'de>,
229    {
230        struct LazyVisitor<T>
231        where
232            T: DeserializeOwned,
233        {
234            _marker: PhantomData<T>,
235        }
236
237        impl<'de, T> Visitor<'de> for LazyVisitor<T>
238        where
239            T: DeserializeOwned,
240        {
241            type Value = Lazy<T>;
242
243            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
244                formatter.write_str("a byte array or str")
245            }
246
247            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
248            where
249                E: de::Error,
250            {
251                match b64::URL_SAFE_NO_PAD.decode(v) {
252                    Ok(data) => Ok(Lazy::from_bytes(data)),
253                    Err(_) => Err(E::custom("not valid base64url without padding")),
254                }
255            }
256
257            fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
258            where
259                E: de::Error,
260            {
261                Ok(Lazy::from_bytes(v))
262            }
263
264            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
265            where
266                E: de::Error,
267            {
268                Ok(Lazy::from_bytes(v.to_vec()))
269            }
270        }
271
272        if deserializer.is_human_readable() {
273            deserializer.deserialize_str(LazyVisitor {
274                _marker: PhantomData,
275            })
276        } else {
277            deserializer.deserialize_byte_buf(LazyVisitor {
278                _marker: PhantomData,
279            })
280        }
281    }
282}
283
284impl<T> Serialize for Lazy<T>
285where
286    T: Serialize,
287{
288    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
289    where
290        S: Serializer,
291    {
292        if let Some(data) = &self.bytes {
293            if serializer.is_human_readable() {
294                let s = b64::URL_SAFE_NO_PAD.encode(data);
295                serializer.serialize_str(&s)
296            } else {
297                serializer.serialize_bytes(data)
298            }
299        } else if let Some(inner) = &self.inner {
300            let data = rmp_serde::encode::to_vec_named(inner).unwrap();
301
302            if serializer.is_human_readable() {
303                let s = b64::URL_SAFE_NO_PAD.encode(data);
304                serializer.serialize_str(&s)
305            } else {
306                serializer.serialize_bytes(&data)
307            }
308        } else {
309            panic!("Lazy structure is invalid, it contains no data!")
310        }
311    }
312}
313
314/// `unwrap_lazy()` should fully deserialize any [Lazy] child fields by calling `unwrap_lazy()` on all fields. After calling this once, calling this on subfields should be cheap.
315/// This crate provides a procedural macro to automatically derive `UnwrapLazy`. It has also been implemented for many primitive types.
316///
317/// An example:
318/// ```
319/// # use serde::{Deserialize, Serialize};
320/// use lazy_borink::{Lazy, UnwrapLazy};
321///
322/// #[derive(Serialize, Deserialize, UnwrapLazy)]
323/// struct Claims {
324///     my_claim: String,
325/// }
326/// #[derive(Deserialize, Serialize, UnwrapLazy)]
327/// struct User {
328///     user_id: String,
329///     password_file: String,
330///     claims: Lazy<Claims>,
331/// }
332/// #[derive(Deserialize, Serialize, UnwrapLazy)]
333/// struct UserMeta {
334///     user: User,
335/// }
336/// ```
337///
338/// The tests provide some additional usage examples.
339pub trait UnwrapLazy {
340    fn unwrap_lazy(self) -> Self
341    where
342        Self: Sized,
343    {
344        self.try_unwrap_lazy().unwrap()
345    }
346
347    fn try_unwrap_lazy(self) -> Result<Self, Error>
348    where
349        Self: Sized;
350}
351
352impl<T> UnwrapLazy for Lazy<T>
353where
354    T: DeserializeOwned + UnwrapLazy,
355{
356    fn unwrap_lazy(self) -> Self {
357        Self::from_inner(self.take().unwrap_lazy())
358    }
359
360    fn try_unwrap_lazy(self) -> Result<Self, Error> {
361        Ok(Self::from_inner(self.try_take()?))
362    }
363}
364
365impl<'a, T> UnwrapLazy for &'a T
366where
367    T: UnwrapLazy,
368{
369    fn try_unwrap_lazy(self) -> Result<Self, Error> {
370        Ok(self)
371    }
372}
373
374impl<'a, T> UnwrapLazy for &'a mut T
375where
376    T: UnwrapLazy,
377{
378    fn try_unwrap_lazy(self) -> Result<Self, Error> {
379        Ok(self)
380    }
381}
382
383impl<'a, T> UnwrapLazy for &'a [T]
384where
385    T: UnwrapLazy,
386{
387    fn try_unwrap_lazy(self) -> Result<Self, Error> {
388        Ok(self)
389    }
390}
391
392impl<'a, T> UnwrapLazy for &'a mut [T]
393where
394    T: UnwrapLazy,
395{
396    fn try_unwrap_lazy(self) -> Result<Self, Error> {
397        Ok(self)
398    }
399}
400
401impl<T> UnwrapLazy for Vec<T>
402where
403    T: UnwrapLazy,
404{
405    fn try_unwrap_lazy(self) -> Result<Self, Error> {
406        Ok(self)
407    }
408}
409
410impl<T> UnwrapLazy for Box<T>
411where
412    T: UnwrapLazy,
413{
414    fn try_unwrap_lazy(self) -> Result<Self, Error> {
415        Ok(self)
416    }
417}
418
419macro_rules! impl_unwrap_lazy_for_primitives {
420    ($($t:ty),*) => {
421        $(
422            impl UnwrapLazy for $t {
423                fn try_unwrap_lazy(self) -> Result<Self, Error> {
424                    Ok(self)
425                }
426            }
427        )*
428    };
429}
430
431impl_unwrap_lazy_for_primitives!(
432    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, bool, char, String
433);
434
435#[cfg(test)]
436mod test {
437    use super::*;
438
439    // Assuming Claims is another struct you want to deserialize lazily.
440    #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, UnwrapLazy)]
441    struct Claims {
442        my_claim: String,
443    }
444
445    #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, UnwrapLazy)]
446    struct User {
447        user_id: String,
448        password_file: String,
449        claims: Lazy<Claims>,
450    }
451
452    #[derive(Deserialize, Serialize, Debug, PartialEq, UnwrapLazy)]
453    struct UserMeta {
454        user: User,
455    }
456
457    #[test]
458    fn lazy_unwrap() {
459        let claims = Claims {
460            my_claim: "hi".to_owned(),
461        };
462
463        let lazy_claims = Lazy::from_inner(claims.clone());
464        let user = User {
465            user_id: "abc".to_owned(),
466            password_file: "hi".to_owned(),
467            claims: lazy_claims,
468        };
469
470        assert_eq!(
471            user.claims,
472            Lazy {
473                inner: Some(claims.clone()),
474                bytes: None
475            }
476        );
477        //println!("user:\n{:?}", user);
478
479        let user_json = serde_json::to_string(&user).unwrap();
480
481        assert_eq!(
482            user_json,
483            "{\"user_id\":\"abc\",\"password_file\":\"hi\",\"claims\":\"gahteV9jbGFpbaJoaQ\"}"
484        );
485        //println!("json repr\n{}", user_json);
486
487        let claims_bytes = serialize_lazy(&claims);
488        let user: User = serde_json::from_str(&user_json).unwrap();
489        assert_eq!(
490            user.claims,
491            Lazy {
492                inner: None,
493                bytes: Some(claims_bytes.clone())
494            }
495        );
496        //println!("user:\n{:?}", user);
497
498        assert_eq!(
499            user.clone().claims.unwrap_lazy(),
500            Lazy {
501                inner: Some(claims.clone()),
502                bytes: None
503            }
504        );
505        assert_eq!(user.clone().claims.take(), claims);
506        let mut user_mut = user.clone();
507        assert_eq!(user_mut.claims.inner(), &claims);
508        assert_eq!(
509            user_mut.claims,
510            Lazy {
511                inner: Some(claims.clone()),
512                bytes: None
513            }
514        );
515
516        let user_m = UserMeta { user };
517        assert_eq!(
518            user_m.user.claims,
519            Lazy {
520                inner: None,
521                bytes: Some(claims_bytes.clone())
522            }
523        );
524
525        let user_m = user_m.unwrap_lazy();
526        assert_eq!(
527            user_m.user.unwrap_lazy().claims,
528            Lazy {
529                inner: Some(claims.clone()),
530                bytes: None
531            }
532        );
533
534        //println!("user_m:\n{:?}", user_m);
535    }
536
537    #[test]
538    fn lazy_trait() {
539        let claims = Claims {
540            my_claim: "hi".to_owned(),
541        };
542        let claims_bytes = serialize_lazy(&claims);
543        let lazy_claims: Lazy<Claims> = claims.clone().into();
544        assert_eq!(
545            lazy_claims,
546            Lazy {
547                inner: Some(claims.clone()),
548                bytes: None
549            }
550        );
551        let mut lazy_mut = lazy_claims.clone();
552        assert_eq!(claims_bytes, lazy_mut.bytes());
553        assert_eq!(
554            lazy_mut,
555            Lazy {
556                inner: None,
557                bytes: Some(claims_bytes.clone())
558            }
559        );
560        assert_eq!(claims_bytes, lazy_claims.clone().take_bytes());
561        let lazy_serial = lazy_mut.clone();
562        assert_eq!(&claims, lazy_mut.inner());
563        assert_eq!(
564            lazy_mut,
565            Lazy {
566                inner: Some(claims.clone()),
567                bytes: None
568            }
569        );
570        assert_eq!(claims, lazy_serial.take());
571    }
572
573    #[test]
574    fn msgpack_round_trip() {
575        let claims = Claims {
576            my_claim: "hi".to_owned(),
577        };
578        let claims_bytes = serialize_lazy(&claims);
579        let lazy_claims = Lazy::from(claims.clone());
580        let user = User {
581            user_id: "abc".to_owned(),
582            password_file: "hi".to_owned(),
583            claims: lazy_claims,
584        };
585
586        let user_lazy = User {
587            user_id: "abc".to_owned(),
588            password_file: "hi".to_owned(),
589            claims: Lazy::from_bytes(claims_bytes),
590        };
591
592        let bytes = rmp_serde::encode::to_vec_named(&user).unwrap();
593
594        let decoded: User = rmp_serde::decode::from_slice(&bytes).unwrap();
595
596        assert_eq!(user_lazy, decoded);
597        assert_eq!(user, decoded.unwrap_lazy())
598    }
599}