1use crate::{BlockStore, Storable, utils::CondSync};
2use anyhow::Result;
3use async_once_cell::OnceCell;
4use cid::Cid;
5use std::fmt::{self, Debug, Formatter};
6
7pub enum Link<T> {
17 Encoded { cid: Cid, value_cache: OnceCell<T> },
21 Decoded { value: T },
26}
27
28impl<T: Storable + CondSync> Link<T> {
33 pub fn from_cid(cid: Cid) -> Self {
35 Self::Encoded {
36 cid,
37 value_cache: OnceCell::new(),
38 }
39 }
40
41 pub async fn resolve_cid(&self, store: &impl BlockStore) -> Result<Cid> {
43 match self {
44 Self::Encoded { cid, .. } => Ok(*cid),
45 Self::Decoded { value } => value.store(store).await,
46 }
47 }
48
49 pub async fn resolve_value(&self, store: &impl BlockStore) -> Result<&T> {
51 match self {
52 Self::Encoded { cid, value_cache } => {
53 value_cache.get_or_try_init(T::load(cid, store)).await
54 }
55 Self::Decoded { value, .. } => Ok(value),
56 }
57 }
58
59 pub async fn resolve_value_mut(&mut self, store: &impl BlockStore) -> Result<&mut T> {
61 match self {
62 Self::Encoded { cid, value_cache } => {
63 let value = match value_cache.take() {
64 Some(v) => v,
65 None => T::load(cid, store).await?,
66 };
67
68 *self = Self::Decoded { value };
69
70 Ok(match self {
71 Self::Decoded { value } => value,
72 _ => unreachable!(),
73 })
74 }
75 Self::Decoded { value, .. } => Ok(value),
76 }
77 }
78
79 pub fn get_cid(&self) -> Option<&Cid> {
83 match self {
84 Self::Encoded { cid, .. } => Some(cid),
85 Self::Decoded { value } => value.persisted_as().and_then(OnceCell::get),
86 }
87 }
88
89 pub fn get_value(&self) -> Option<&T> {
93 match self {
94 Self::Encoded { value_cache, .. } => value_cache.get(),
95 Self::Decoded { value } => Some(value),
96 }
97 }
98
99 pub async fn resolve_owned_value(self, store: &impl BlockStore) -> Result<T>
101 where
102 T: Storable,
103 {
104 match self {
105 Self::Encoded { cid, value_cache } => match value_cache.into_inner() {
106 Some(cached) => Ok(cached),
107 None => Ok(T::load(&cid, store).await?),
108 },
109 Self::Decoded { value, .. } => Ok(value),
110 }
111 }
112
113 pub fn has_cid(&self) -> bool {
115 self.get_cid().is_some()
116 }
117
118 pub fn has_value(&self) -> bool {
120 match self {
121 Self::Encoded { value_cache, .. } => value_cache.get().is_some(),
122 _ => true,
123 }
124 }
125
126 pub async fn deep_eq(&self, other: &Link<T>, store: &impl BlockStore) -> Result<bool>
128 where
129 T: PartialEq + Storable,
130 {
131 if self == other {
132 return Ok(true);
133 }
134
135 Ok(self.resolve_cid(store).await? == other.resolve_cid(store).await?)
136 }
137}
138
139impl<T: Storable> From<T> for Link<T> {
140 fn from(value: T) -> Self {
141 Self::Decoded { value }
142 }
143}
144
145impl<T> Clone for Link<T>
146where
147 T: Clone,
148{
149 fn clone(&self) -> Self {
150 match self {
151 Self::Encoded { cid, value_cache } => Self::Encoded {
152 cid: *cid,
153 value_cache: value_cache
154 .get()
155 .cloned()
156 .map(OnceCell::new_with)
157 .unwrap_or_default(),
158 },
159 Self::Decoded { value } => Self::Decoded {
160 value: value.clone(),
161 },
162 }
163 }
164}
165
166impl<T: Storable + CondSync> PartialEq for Link<T>
167where
168 T: PartialEq,
169{
170 fn eq(&self, other: &Self) -> bool {
171 match (self, other) {
172 (Self::Encoded { cid, .. }, Self::Encoded { cid: cid2, .. }) => cid == cid2,
173 (Self::Decoded { value, .. }, Self::Decoded { value: value2, .. }) => value == value2,
174 (Self::Encoded { cid, .. }, Self::Decoded { value: value2, .. }) => {
175 if let Some(cid2) = other.get_cid() {
176 cid == cid2
177 } else if let Some(value) = self.get_value() {
178 value == value2
179 } else {
180 false
181 }
182 }
183 (Self::Decoded { value, .. }, Self::Encoded { cid: cid2, .. }) => {
184 if let Some(cid) = self.get_cid() {
185 cid == cid2
186 } else if let Some(value2) = other.get_value() {
187 value == value2
188 } else {
189 false
190 }
191 }
192 }
193 }
194}
195
196impl<T> Debug for Link<T>
197where
198 T: Debug,
199{
200 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
201 match self {
202 Self::Encoded { cid, value_cache } => f
203 .debug_struct("Link::Encoded")
204 .field("cid", &format!("{cid}"))
205 .field("value_cache", &value_cache.get())
206 .finish(),
207 Self::Decoded { value } => f.debug_tuple("Link::Decoded").field(value).finish(),
208 }
209 }
210}
211
212#[cfg(test)]
217mod tests {
218 use crate::{BlockStore, Link, MemoryBlockStore, Storable};
219 use anyhow::Result;
220 use async_once_cell::OnceCell;
221 use cid::Cid;
222 use serde::{Deserialize, Serialize};
223
224 #[derive(Debug, Serialize, Deserialize)]
225 struct Example {
226 price: u64,
227 #[serde(skip, default = "OnceCell::new")]
228 persisted_as: OnceCell<Cid>,
229 }
230
231 impl Storable for Example {
232 type Serializable = Example;
233
234 async fn to_serializable(&self, _store: &impl BlockStore) -> Result<Self::Serializable> {
235 Ok(self.clone())
236 }
237
238 async fn from_serializable(
239 cid: Option<&Cid>,
240 mut serializable: Self::Serializable,
241 ) -> Result<Self> {
242 serializable.persisted_as = cid.cloned().map(OnceCell::new_with).unwrap_or_default();
243 Ok(serializable)
244 }
245
246 fn persisted_as(&self) -> Option<&OnceCell<Cid>> {
247 Some(&self.persisted_as)
248 }
249 }
250
251 impl Clone for Example {
252 fn clone(&self) -> Self {
253 Self {
254 price: self.price,
255 persisted_as: self
256 .persisted_as
257 .get()
258 .cloned()
259 .map(OnceCell::new_with)
260 .unwrap_or_default(),
261 }
262 }
263 }
264
265 impl PartialEq for Example {
266 fn eq(&self, other: &Self) -> bool {
267 self.price == other.price
268 }
269 }
270
271 impl Example {
272 fn new(price: u64) -> Self {
273 Self {
274 price,
275 persisted_as: OnceCell::new(),
276 }
277 }
278 }
279
280 #[async_std::test]
281 async fn link_value_can_be_resolved() {
282 let store = &MemoryBlockStore::default();
283 let example = Example::new(256);
284 let cid = example.store(store).await.unwrap();
285 let link = Link::<Example>::from_cid(cid);
286
287 let value = link.resolve_value(store).await.unwrap();
288 assert_eq!(value, &example);
289 assert!(link.has_value());
290 }
291
292 #[async_std::test]
293 async fn link_cid_can_be_resolved() {
294 let example = Example::new(12_000_500);
295 let store = &MemoryBlockStore::default();
296 let link = Link::<Example>::from(example.clone());
297
298 let cid = link.resolve_cid(store).await.unwrap();
299 let value = Example::load(&cid, store).await.unwrap();
300
301 assert_eq!(value, example);
302 }
303}