dwn/
lib.rs

1//! Rust implementation of a [Decentralized Web Node](https://identity.foundation/decentralized-web-node/spec/).
2//!
3//! The DWN spec is a work-in-progress and often out of date from other implementations,
4//! so it is treated more as a loose guide rather than an absolute set of rules to follow.
5//!
6//! # Example
7//!
8//! ```
9//! use dwn::{
10//!     core::{message::{descriptor::{RecordsReadBuilder, RecordsWriteBuilder}, mime::TEXT_PLAIN}, reply::Reply},
11//!     stores::NativeDbStore,
12//!     Actor,
13//!     Dwn
14//! };
15//! use xdid::methods::key::{p256::P256KeyPair, DidKeyPair, PublicKey};
16//!
17//! #[tokio::main]
18//! async fn main() {
19//!     // Create a local in-memory DWN.
20//!     let store = NativeDbStore::new_in_memory().unwrap();
21//!     let dwn = Dwn::from(store);
22//!    
23//!     // Create a new did:key.
24//!     let key = P256KeyPair::generate();
25//!     let did = key.public().to_did();
26//!    
27//!     // Create an actor to sign messages on behalf of our DID.
28//!     let mut actor = Actor::new(did, dwn);
29//!     actor.auth_key = Some(key.clone().into());
30//!     actor.sign_key = Some(key.into());
31//!    
32//!     // Write a new record to the DWN.
33//!     let data = "Hello, world!".as_bytes().to_vec();
34//!
35//!     let record_id = actor.write()
36//!         .data(TEXT_PLAIN, data.clone())
37//!         .published(true)
38//!         .process()
39//!         .await
40//!         .unwrap();
41//!
42//!     // We can now read the record using its ID.
43//!     let found = actor.read(record_id.clone())
44//!         .process()
45//!         .await
46//!         .unwrap()
47//!         .unwrap();
48//!
49//!    assert!(found.entry().record_id, record_id);
50//!    assert!(found.data().unwrap(), data);
51//! }
52//! ```
53
54use std::sync::Arc;
55
56use dwn_core::{
57    message::{Message, descriptor::Descriptor},
58    reply::Reply,
59    store::{DataStore, RecordStore},
60};
61use reqwest::StatusCode;
62use tracing::debug;
63use xdid::core::did::Did;
64
65pub use dwn_core as core;
66
67pub mod stores {
68    #[cfg(feature = "native_db")]
69    pub use dwn_native_db::*;
70}
71
72mod actor;
73mod handlers;
74
75pub use actor::*;
76
77use crate::handlers::validation::ValidationResult;
78
79#[derive(Clone)]
80pub struct Dwn {
81    pub data_store: Arc<dyn DataStore>,
82    pub record_store: Arc<dyn RecordStore>,
83}
84
85impl<T: DataStore + RecordStore + Clone + 'static> From<T> for Dwn {
86    fn from(value: T) -> Self {
87        Self::new(Arc::new(value.clone()), Arc::new(value))
88    }
89}
90
91struct ProcessContext<'a> {
92    pub rs: &'a dyn RecordStore,
93    pub ds: &'a dyn DataStore,
94    pub validation: ValidationResult,
95    pub target: &'a Did,
96    pub msg: Message,
97}
98
99impl Dwn {
100    pub fn new(data_store: Arc<dyn DataStore>, record_store: Arc<dyn RecordStore>) -> Self {
101        Self {
102            data_store,
103            record_store,
104        }
105    }
106
107    pub async fn process_message(
108        &self,
109        target: &Did,
110        msg: Message,
111    ) -> Result<Option<Reply>, StatusCode> {
112        let validation = match handlers::validation::validate_message(&msg).await {
113            Ok(a) => a,
114            Err(e) => {
115                debug!("Failed to validate message: {:?}", e);
116                return Err(StatusCode::BAD_REQUEST);
117            }
118        };
119
120        let ctx = ProcessContext {
121            rs: self.record_store.as_ref(),
122            ds: self.data_store.as_ref(),
123            validation,
124            target,
125            msg,
126        };
127
128        let res = match &ctx.msg.descriptor {
129            Descriptor::ProtocolsConfigure(_) => {
130                handlers::protocols::configure::handle(ctx).await?;
131                None
132            }
133            Descriptor::ProtocolsQuery(_) => handlers::protocols::query::handle(ctx)
134                .await
135                .map(|v| Some(Reply::ProtocolsQuery(v)))?,
136            Descriptor::RecordsDelete(_) => {
137                handlers::records::delete::handle(ctx).await?;
138                None
139            }
140            Descriptor::RecordsQuery(_) => handlers::records::query::handle(ctx)
141                .await
142                .map(|v| Some(Reply::RecordsQuery(v)))?,
143            Descriptor::RecordsRead(_) => handlers::records::read::handle(ctx)
144                .await
145                .map(|v| Some(Reply::RecordsRead(Box::new(v))))?,
146            Descriptor::RecordsSync(_) => handlers::records::sync::handle(ctx)
147                .await
148                .map(|v| Some(Reply::RecordsSync(Box::new(v))))?,
149            Descriptor::RecordsWrite(_) => {
150                handlers::records::write::handle(ctx).await?;
151                None
152            }
153        };
154
155        Ok(res)
156    }
157}