1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::{collections::HashMap, io::Cursor, sync::Arc};
use anyhow::{anyhow, Result};
use async_std::sync::Mutex;
use async_trait::async_trait;
use cid::{
multihash::{Code, MultihashDigest},
Cid,
};
use libipld_core::{
codec::{Codec, Decode, Encode},
ipld::Ipld,
raw::RawCodec,
};
#[cfg(not(target_arch = "wasm32"))]
pub trait UcanStoreConditionalSend: Send {}
#[cfg(not(target_arch = "wasm32"))]
impl<U> UcanStoreConditionalSend for U where U: Send {}
#[cfg(target_arch = "wasm32")]
pub trait UcanStoreConditionalSend {}
#[cfg(target_arch = "wasm32")]
impl<U> UcanStoreConditionalSend for U {}
#[cfg(not(target_arch = "wasm32"))]
pub trait UcanStoreConditionalSendSync: Send + Sync {}
#[cfg(not(target_arch = "wasm32"))]
impl<U> UcanStoreConditionalSendSync for U where U: Send + Sync {}
#[cfg(target_arch = "wasm32")]
pub trait UcanStoreConditionalSendSync {}
#[cfg(target_arch = "wasm32")]
impl<U> UcanStoreConditionalSendSync for U {}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait UcanStore<C: Codec + Default = RawCodec>: UcanStoreConditionalSendSync {
async fn read<T: Decode<C>>(&self, cid: &Cid) -> Result<Option<T>>;
async fn write<T: Encode<C> + UcanStoreConditionalSend + core::fmt::Debug>(
&mut self,
token: T,
) -> Result<Cid>;
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait UcanJwtStore: UcanStore<RawCodec> {
async fn require_token(&self, cid: &Cid) -> Result<String> {
match self.read_token(cid).await? {
Some(token) => Ok(token),
None => Err(anyhow!("No token found for CID {}", cid.to_string())),
}
}
async fn read_token(&self, cid: &Cid) -> Result<Option<String>> {
let codec = RawCodec::default();
if cid.codec() != u64::from(codec) {
return Err(anyhow!(
"Only 'raw' codec supported, but CID refers to {:#x}",
cid.codec()
));
}
match self.read::<Ipld>(cid).await? {
Some(Ipld::Bytes(bytes)) => Ok(Some(std::str::from_utf8(&bytes)?.to_string())),
_ => Err(anyhow!("No UCAN was found for CID {:?}", cid)),
}
}
async fn write_token(&mut self, token: &str) -> Result<Cid> {
self.write(Ipld::Bytes(token.as_bytes().to_vec())).await
}
}
impl<U> UcanJwtStore for U where U: UcanStore<RawCodec> {}
#[derive(Clone, Default, Debug)]
pub struct MemoryStore {
dags: Arc<Mutex<HashMap<Cid, Vec<u8>>>>,
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl UcanStore<RawCodec> for MemoryStore {
async fn read<T: Decode<RawCodec>>(&self, cid: &Cid) -> Result<Option<T>> {
let codec = RawCodec::default();
let dags = self.dags.lock().await;
Ok(match dags.get(cid) {
Some(bytes) => Some(T::decode(codec, &mut Cursor::new(bytes))?),
None => None,
})
}
async fn write<T: Encode<RawCodec> + UcanStoreConditionalSend + core::fmt::Debug>(
&mut self,
token: T,
) -> Result<Cid> {
let codec = RawCodec::default();
let block = codec.encode(&token)?;
let cid = Cid::new_v1(codec.into(), Code::Blake2b256.digest(&block));
let mut dags = self.dags.lock().await;
dags.insert(cid.clone(), block);
Ok(cid)
}
}