tycho_types/cell/
lazy.rs

1use std::marker::PhantomData;
2
3use crate::cell::{
4    Cell, CellBuilder, CellContext, CellSlice, DynCell, EquivalentRepr, Load, LoadCell, Size, Store,
5};
6use crate::error::Error;
7use crate::util::{debug_tuple_field1_finish, unlikely};
8
9/// Lazy-loaded model.
10#[repr(transparent)]
11pub struct Lazy<T, const EXOTIC: bool = false> {
12    cell: Cell,
13    _marker: PhantomData<T>,
14}
15
16/// Lazy-loaded exotic cell.
17pub type LazyExotic<T> = Lazy<T, true>;
18
19impl<T, const EXOTIC: bool> crate::cell::ExactSize for Lazy<T, EXOTIC> {
20    #[inline]
21    fn exact_size(&self) -> Size {
22        Size { bits: 0, refs: 1 }
23    }
24}
25
26impl<T> std::fmt::Debug for Lazy<T, false> {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        debug_tuple_field1_finish(f, "Lazy", &self.cell)
29    }
30}
31
32impl<T> std::fmt::Debug for Lazy<T, true> {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        debug_tuple_field1_finish(f, "LazyExotic", &self.cell)
35    }
36}
37
38impl<T, const EXOTIC: bool> Eq for Lazy<T, EXOTIC> {}
39impl<T, const EXOTIC: bool> PartialEq for Lazy<T, EXOTIC> {
40    #[inline]
41    fn eq(&self, other: &Self) -> bool {
42        self.cell.as_ref().eq(other.cell.as_ref())
43    }
44}
45
46impl<T, const EXOTIC: bool> PartialEq<Cell> for Lazy<T, EXOTIC> {
47    #[inline]
48    fn eq(&self, other: &Cell) -> bool {
49        PartialEq::eq(self.inner(), other)
50    }
51}
52
53impl<T, const EXOTIC: bool> PartialEq<&Cell> for Lazy<T, EXOTIC> {
54    #[inline]
55    fn eq(&self, other: &&Cell) -> bool {
56        PartialEq::eq(self.inner(), *other)
57    }
58}
59
60impl<T, const EXOTIC: bool> PartialEq<Lazy<T, EXOTIC>> for Cell {
61    #[inline]
62    fn eq(&self, other: &Lazy<T, EXOTIC>) -> bool {
63        PartialEq::eq(self, other.inner())
64    }
65}
66
67impl<T, const EXOTIC: bool> PartialEq<&Lazy<T, EXOTIC>> for Cell {
68    #[inline]
69    fn eq(&self, other: &&Lazy<T, EXOTIC>) -> bool {
70        PartialEq::eq(self, other.inner())
71    }
72}
73
74impl<T, const EXOTIC: bool> Clone for Lazy<T, EXOTIC> {
75    #[inline]
76    fn clone(&self) -> Self {
77        Self {
78            cell: self.cell.clone(),
79            _marker: PhantomData,
80        }
81    }
82}
83
84impl<T, const EXOTIC: bool> Lazy<T, EXOTIC> {
85    /// Wraps the cell in a typed wrapper.
86    #[inline]
87    pub fn from_raw(cell: Cell) -> Result<Self, Error> {
88        if unlikely(cell.is_exotic() != EXOTIC) {
89            return Err(if EXOTIC {
90                Error::UnexpectedOrdinaryCell
91            } else {
92                Error::UnexpectedExoticCell
93            });
94        }
95
96        Ok(Self {
97            cell,
98            _marker: PhantomData,
99        })
100    }
101
102    /// Wraps the cell in a typed wrapper.
103    ///
104    /// # Safety
105    ///
106    /// The cell `is_exotic` flag must be in sync with `EXOTIC` type param.
107    #[inline]
108    pub unsafe fn from_raw_unchecked(cell: Cell) -> Self {
109        Self {
110            cell,
111            _marker: PhantomData,
112        }
113    }
114
115    /// Converts into the underlying cell.
116    #[inline]
117    pub fn into_inner(self) -> Cell {
118        self.cell
119    }
120
121    /// Returns the underlying cell.
122    #[inline]
123    pub fn inner(&self) -> &Cell {
124        &self.cell
125    }
126
127    /// Converts into a lazy loader for an equivalent type.
128    pub fn cast_into<Q>(self) -> Lazy<Q, EXOTIC>
129    where
130        Q: EquivalentRepr<T>,
131    {
132        Lazy {
133            cell: self.cell,
134            _marker: PhantomData,
135        }
136    }
137
138    /// Casts itself into a lazy loaded for an equivalent type.
139    pub fn cast_ref<Q>(&self) -> &Lazy<Q, EXOTIC>
140    where
141        Q: EquivalentRepr<T>,
142    {
143        // SAFETY: Lazy is #[repr(transparent)]
144        unsafe { &*(self as *const Self as *const Lazy<Q, EXOTIC>) }
145    }
146
147    /// Serializes only the root hash of the cell.
148    #[cfg(feature = "serde")]
149    pub fn serialize_repr_hash<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
150    where
151        S: serde::Serializer,
152    {
153        serde::Serialize::serialize(self.cell.repr_hash(), serializer)
154    }
155}
156
157impl<T, const EXOTIC: bool> AsRef<DynCell> for Lazy<T, EXOTIC> {
158    #[inline]
159    fn as_ref(&self) -> &DynCell {
160        self.cell.as_ref()
161    }
162}
163
164impl<T, const EXOTIC: bool> std::ops::Deref for Lazy<T, EXOTIC> {
165    type Target = Cell;
166
167    #[inline]
168    fn deref(&self) -> &Self::Target {
169        &self.cell
170    }
171}
172
173impl<T: Store, const EXOTIC: bool> Lazy<T, EXOTIC> {
174    /// Serializes the provided data and returns the typed wrapper around it.
175    pub fn new(data: &T) -> Result<Self, Error> {
176        Self::from_raw(ok!(CellBuilder::build_from(data)))
177    }
178
179    /// Updates the content with the provided data.
180    pub fn set(&mut self, data: &T) -> Result<(), Error> {
181        *self = ok!(Self::new(data));
182        Ok(())
183    }
184}
185
186impl<'a, T: Load<'a> + 'a> Lazy<T, false> {
187    /// Loads inner data from cell expecting an ordinary cell.
188    pub fn load(&'a self) -> Result<T, Error> {
189        self.cell.as_ref().parse::<T>()
190    }
191}
192
193impl<'a, T: LoadCell<'a> + 'a> Lazy<T, true> {
194    /// Loads inner data from cell expecting an exotic cell.
195    pub fn load(&'a self) -> Result<T, Error> {
196        self.cell.as_ref().parse_exotic::<T>()
197    }
198}
199
200impl<T, const EXOTIC: bool> Store for Lazy<T, EXOTIC> {
201    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
202        builder.store_reference(self.cell.clone())
203    }
204}
205
206impl<'a, T, const EXOTIC: bool> Load<'a> for Lazy<T, EXOTIC> {
207    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
208        match slice.load_reference_cloned() {
209            Ok(cell) => Self::from_raw(cell),
210            Err(e) => Err(e),
211        }
212    }
213}
214
215#[cfg(feature = "serde")]
216impl<T> serde::Serialize for Lazy<T, false>
217where
218    for<'a> T: serde::Serialize + Load<'a>,
219{
220    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
221    where
222        S: serde::Serializer,
223    {
224        if serializer.is_human_readable() {
225            let value = ok!(self.load().map_err(serde::ser::Error::custom));
226            value.serialize(serializer)
227        } else {
228            crate::boc::Boc::serialize(&self.cell, serializer)
229        }
230    }
231}
232
233#[cfg(feature = "serde")]
234impl<T> serde::Serialize for Lazy<T, true>
235where
236    for<'a> T: serde::Serialize + LoadCell<'a>,
237{
238    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
239    where
240        S: serde::Serializer,
241    {
242        if serializer.is_human_readable() {
243            let value = ok!(self.load().map_err(serde::ser::Error::custom));
244            value.serialize(serializer)
245        } else {
246            crate::boc::Boc::serialize(&self.cell, serializer)
247        }
248    }
249}
250
251#[cfg(feature = "serde")]
252impl<'de, T, const EXOTIC: bool> serde::Deserialize<'de> for Lazy<T, EXOTIC>
253where
254    T: serde::Deserialize<'de> + Store,
255{
256    fn deserialize<D>(deserializer: D) -> Result<Lazy<T, EXOTIC>, D::Error>
257    where
258        D: serde::Deserializer<'de>,
259    {
260        if deserializer.is_human_readable() {
261            let value = T::deserialize(deserializer)?;
262            Lazy::new(&value)
263        } else {
264            let cell = crate::boc::Boc::deserialize(deserializer)?;
265            Lazy::from_raw(cell)
266        }
267        .map_err(serde::de::Error::custom)
268    }
269}
270
271#[cfg(feature = "arbitrary")]
272impl<'a, T: Store + arbitrary::Arbitrary<'a>> arbitrary::Arbitrary<'a> for Lazy<T, false> {
273    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
274        let inner = u.arbitrary::<T>()?;
275        Lazy::new(&inner).map_err(|_| arbitrary::Error::IncorrectFormat)
276    }
277
278    #[inline]
279    fn size_hint(depth: usize) -> (usize, Option<usize>) {
280        Self::try_size_hint(depth).unwrap_or_default()
281    }
282
283    #[inline]
284    fn try_size_hint(
285        depth: usize,
286    ) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
287        <T as arbitrary::Arbitrary>::try_size_hint(depth)
288    }
289}