1#![allow(missing_docs)]
2
3use crate::{ensure, Error, Unit};
12use const_format::formatcp;
13#[cfg(feature = "diesel")]
14use diesel::{
15 backend::Backend,
16 deserialize::{self, FromSql, FromSqlRow},
17 expression::AsExpression,
18 serialize::{self, IsNull, Output, ToSql},
19 sql_types::Text,
20 sqlite::Sqlite,
21};
22use enum_assoc::Assoc;
23use libipld::{cid::Cid, serde::from_ipld, Ipld, Link};
24use schemars::{
25 gen::SchemaGenerator,
26 schema::{InstanceType, Metadata, ObjectValidation, Schema, SchemaObject, SingleOrVec},
27 JsonSchema,
28};
29use serde::{Deserialize, Serialize};
30#[cfg(feature = "diesel")]
31use std::str::FromStr;
32use std::{borrow::Cow, collections::btree_map::BTreeMap, fmt, module_path};
33
34pub const OK_BRANCH: &str = "await/ok";
36pub const ERR_BRANCH: &str = "await/error";
38pub const PTR_BRANCH: &str = "await/*";
40
41#[derive(Clone, Debug, PartialEq, Eq, Assoc, Deserialize, Serialize)]
56#[func(pub const fn branch(&self) -> &'static str)]
57#[func(pub fn result(s: &str) -> Option<Self>)]
58pub enum AwaitResult {
59 #[assoc(branch = OK_BRANCH)]
61 #[assoc(result = OK_BRANCH)]
62 Ok,
63 #[assoc(branch = ERR_BRANCH)]
65 #[assoc(result = ERR_BRANCH)]
66 Error,
67 #[assoc(branch = PTR_BRANCH)]
69 #[assoc(result = PTR_BRANCH)]
70 Ptr,
71}
72
73impl fmt::Display for AwaitResult {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 match self {
76 AwaitResult::Error => write!(f, "await/error"),
77 AwaitResult::Ok => write!(f, "await/ok"),
78 AwaitResult::Ptr => write!(f, "await/*"),
79 }
80 }
81}
82
83impl JsonSchema for AwaitResult {
84 fn schema_name() -> String {
85 "await_result".to_owned()
86 }
87
88 fn schema_id() -> Cow<'static, str> {
89 Cow::Borrowed(formatcp!("{}::AwaitResult", module_path!()))
90 }
91
92 fn json_schema(gen: &mut SchemaGenerator) -> Schema {
93 let mut schema = SchemaObject {
94 instance_type: None,
95 metadata: Some(Box::new(Metadata {
96 title: Some("Await result".to_string()),
97 description: Some("Branches of a promise that is awaited".to_string()),
98 ..Default::default()
99 })),
100 ..Default::default()
101 };
102
103 let await_ok = SchemaObject {
104 instance_type: Some(SingleOrVec::Single(InstanceType::Object.into())),
105 object: Some(Box::new(ObjectValidation {
106 properties: BTreeMap::from([(
107 OK_BRANCH.to_string(),
108 gen.subschema_for::<Pointer>(),
109 )]),
110 ..Default::default()
111 })),
112 ..Default::default()
113 };
114 let await_err = SchemaObject {
115 instance_type: Some(SingleOrVec::Single(InstanceType::Object.into())),
116 object: Some(Box::new(ObjectValidation {
117 properties: BTreeMap::from([(
118 ERR_BRANCH.to_string(),
119 gen.subschema_for::<Pointer>(),
120 )]),
121 ..Default::default()
122 })),
123 ..Default::default()
124 };
125 let await_ptr = SchemaObject {
126 instance_type: Some(SingleOrVec::Single(InstanceType::Object.into())),
127 object: Some(Box::new(ObjectValidation {
128 properties: BTreeMap::from([(
129 PTR_BRANCH.to_string(),
130 gen.subschema_for::<Pointer>(),
131 )]),
132 ..Default::default()
133 })),
134 ..Default::default()
135 };
136
137 schema.subschemas().one_of = Some(vec![
138 Schema::Object(await_ok),
139 Schema::Object(await_err),
140 Schema::Object(await_ptr),
141 ]);
142 schema.into()
143 }
144}
145
146#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
152pub struct Await {
153 instruction: Pointer,
154 result: AwaitResult,
155}
156
157impl Await {
158 pub fn new(instruction: Pointer, result: AwaitResult) -> Self {
161 Self {
162 instruction,
163 result,
164 }
165 }
166
167 pub fn instruction_cid(&self) -> Cid {
171 self.instruction.cid()
172 }
173
174 pub fn result(&self) -> &AwaitResult {
176 &self.result
177 }
178}
179
180impl From<Await> for Ipld {
181 fn from(await_promise: Await) -> Self {
182 Ipld::Map(BTreeMap::from([(
183 await_promise.result.branch().to_string(),
184 await_promise.instruction.into(),
185 )]))
186 }
187}
188
189impl From<&Await> for Ipld {
190 fn from(await_promise: &Await) -> Self {
191 From::from(await_promise.to_owned())
192 }
193}
194
195impl TryFrom<Ipld> for Await {
196 type Error = Error<Unit>;
197
198 fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
199 let map = from_ipld::<BTreeMap<String, Ipld>>(ipld)?;
200 ensure!(
201 map.len() == 1,
202 Error::ConditionNotMet(
203 "await promise must have only a single key in a map".to_string()
204 )
205 );
206
207 let (key, value) = map.into_iter().next().unwrap();
208 let instruction = Pointer::try_from(value)?;
209
210 let result = match key.as_str() {
211 OK_BRANCH => AwaitResult::Ok,
212 ERR_BRANCH => AwaitResult::Error,
213 _ => AwaitResult::Ptr,
214 };
215
216 Ok(Await {
217 instruction,
218 result,
219 })
220 }
221}
222
223impl TryFrom<&Ipld> for Await {
224 type Error = Error<Unit>;
225
226 fn try_from(ipld: &Ipld) -> Result<Self, Self::Error> {
227 TryFrom::try_from(ipld.to_owned())
228 }
229}
230
231#[cfg(feature = "diesel")]
239#[cfg_attr(docsrs, doc(cfg(feature = "diesel")))]
240#[derive(
241 Clone,
242 Debug,
243 AsExpression,
244 FromSqlRow,
245 PartialEq,
246 Eq,
247 Serialize,
248 Deserialize,
249 Hash,
250 PartialOrd,
251 Ord,
252)]
253#[diesel(sql_type = Text)]
254#[repr(transparent)]
255pub struct Pointer(Cid);
256
257#[cfg(not(feature = "diesel"))]
265#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, PartialOrd, Ord)]
266#[repr(transparent)]
267pub struct Pointer(Cid);
268
269impl fmt::Display for Pointer {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 let cid_as_string = self.0.to_string();
272 write!(f, "{cid_as_string}")
273 }
274}
275
276impl Pointer {
277 pub fn cid(&self) -> Cid {
279 self.0
280 }
281
282 pub fn new(cid: Cid) -> Self {
284 Pointer(cid)
285 }
286
287 pub fn new_from_link<T>(link: Link<T>) -> Self {
289 Pointer(*link)
290 }
291}
292
293impl From<Pointer> for Ipld {
294 fn from(ptr: Pointer) -> Self {
295 Ipld::Link(ptr.cid())
296 }
297}
298
299impl TryFrom<Ipld> for Pointer {
300 type Error = Error<Unit>;
301
302 fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
303 let s: Cid = from_ipld(ipld)?;
304 Ok(Pointer(s))
305 }
306}
307
308impl TryFrom<&Ipld> for Pointer {
309 type Error = Error<Unit>;
310
311 fn try_from(ipld: &Ipld) -> Result<Self, Self::Error> {
312 TryFrom::try_from(ipld.to_owned())
313 }
314}
315
316impl<'a> From<Pointer> for Cow<'a, Pointer> {
317 fn from(ptr: Pointer) -> Self {
318 Cow::Owned(ptr)
319 }
320}
321
322impl<'a> From<&'a Pointer> for Cow<'a, Pointer> {
323 fn from(ptr: &'a Pointer) -> Self {
324 Cow::Borrowed(ptr)
325 }
326}
327
328#[cfg(feature = "diesel")]
329#[cfg_attr(docsrs, doc(cfg(feature = "diesel")))]
330impl ToSql<Text, Sqlite> for Pointer {
331 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
332 out.set_value(self.cid().to_string());
333 Ok(IsNull::No)
334 }
335}
336
337#[cfg(feature = "diesel")]
338#[cfg_attr(docsrs, doc(cfg(feature = "diesel")))]
339impl<DB> FromSql<Text, DB> for Pointer
340where
341 DB: Backend,
342 String: FromSql<Text, DB>,
343{
344 fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
345 let s = String::from_sql(bytes)?;
346 Ok(Pointer::new(Cid::from_str(&s)?))
347 }
348}
349
350impl JsonSchema for Pointer {
351 fn schema_name() -> String {
352 "pointer".to_owned()
353 }
354
355 fn schema_id() -> Cow<'static, str> {
356 Cow::Borrowed(formatcp!("{}::Pointer", module_path!()))
357 }
358
359 fn json_schema(gen: &mut SchemaGenerator) -> Schema {
360 let schema = SchemaObject {
361 instance_type: Some(SingleOrVec::Single(InstanceType::Object.into())),
362 object: Some(Box::new(ObjectValidation {
363 properties: BTreeMap::from([('/'.to_string(), <String>::json_schema(gen))]),
364 ..Default::default()
365 })),
366 metadata: Some(Box::new(Metadata {
367 description: Some(
368 "CID reference to an invocation, task, instruction, or receipt".to_string(),
369 ),
370 ..Default::default()
371 })),
372 ..Default::default()
373 };
374 schema.into()
375 }
376}
377
378#[cfg(test)]
379mod test {
380 use super::*;
381 use crate::test_utils::cid::generate_cid;
382 use rand::thread_rng;
383
384 #[test]
385 fn ser_de_pointer() {
386 let pointer = Pointer::new(generate_cid(&mut thread_rng()));
387 let ser = serde_json::to_string(&pointer).unwrap();
388 let de = serde_json::from_str(&ser).unwrap();
389
390 assert_eq!(pointer, de);
391 }
392
393 #[test]
394 fn ser_de_await() {
395 let awaited = Await::new(
396 Pointer::new(generate_cid(&mut thread_rng())),
397 AwaitResult::Ok,
398 );
399 let ser = serde_json::to_string(&awaited).unwrap();
400 let de = serde_json::from_str(&ser).unwrap();
401
402 assert_eq!(awaited, de);
403 }
404}