affinidi_did_resolver_traits/lib.rs
1/*!
2 * Pluggable DID resolution traits.
3 *
4 * Provides [`Resolver`] (sync) and [`AsyncResolver`] (async) traits for
5 * decoupling DID resolution from concrete types. External consumers implement
6 * these traits for custom DID methods; the SDK composes them with built-in
7 * resolvers.
8 *
9 * Every [`Resolver`] is automatically an [`AsyncResolver`] via blanket impl,
10 * so the SDK only needs `Box<dyn AsyncResolver>` for composition.
11 *
12 * # Return Convention
13 *
14 * Resolvers return `Option<Result<Document, ResolverError>>`:
15 * - `None` — "not my DID, pass to next resolver"
16 * - `Some(Ok(doc))` — resolved successfully
17 * - `Some(Err(e))` — recognized the DID but resolution failed
18 */
19
20use std::future::Future;
21use std::pin::Pin;
22
23mod error;
24mod resolvers;
25
26pub use error::ResolverError;
27pub use resolvers::{KeyResolver, PeerResolver};
28
29use affinidi_did_common::{DID, DIDMethod, Document};
30
31/// Result type alias for resolver return values.
32///
33/// - `None`: this resolver does not handle the given DID method
34/// - `Some(Ok(doc))`: resolved successfully
35/// - `Some(Err(e))`: this resolver handles the method but resolution failed
36pub type Resolution = Option<Result<Document, ResolverError>>;
37
38/// Discriminant for DID method types — used as HashMap key for resolver dispatch.
39///
40/// Unlike `DIDMethod` (which carries parsed data), this is a pure tag type
41/// suitable for `Hash + Eq` keying. Derived from `DIDMethod` via `From` impl
42/// in the SDK crate.
43#[derive(Debug, Clone, Hash, PartialEq, Eq)]
44pub enum MethodName {
45 Key,
46 Peer,
47 Web,
48 Jwk,
49 Ethr,
50 Pkh,
51 Webvh,
52 Cheqd,
53 Scid,
54 Ebsi,
55 /// Catch-all for methods not explicitly modeled.
56 Other(String),
57}
58
59impl std::fmt::Display for MethodName {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 match self {
62 MethodName::Key => write!(f, "key"),
63 MethodName::Peer => write!(f, "peer"),
64 MethodName::Web => write!(f, "web"),
65 MethodName::Jwk => write!(f, "jwk"),
66 MethodName::Ethr => write!(f, "ethr"),
67 MethodName::Pkh => write!(f, "pkh"),
68 MethodName::Webvh => write!(f, "webvh"),
69 MethodName::Cheqd => write!(f, "cheqd"),
70 MethodName::Scid => write!(f, "scid"),
71 MethodName::Ebsi => write!(f, "ebsi"),
72 MethodName::Other(s) => write!(f, "{s}"),
73 }
74 }
75}
76
77/// Convert `DIDMethod` (with data) to `MethodName` (pure discriminant).
78impl From<&DIDMethod> for MethodName {
79 fn from(method: &DIDMethod) -> Self {
80 match method.name() {
81 "key" => MethodName::Key,
82 "peer" => MethodName::Peer,
83 "web" => MethodName::Web,
84 "jwk" => MethodName::Jwk,
85 "ethr" => MethodName::Ethr,
86 "pkh" => MethodName::Pkh,
87 "webvh" => MethodName::Webvh,
88 "cheqd" => MethodName::Cheqd,
89 "scid" => MethodName::Scid,
90 "ebsi" => MethodName::Ebsi,
91 method => MethodName::Other(method.to_string()),
92 }
93 }
94}
95
96/// Synchronous DID resolver for methods that require no IO.
97///
98/// Implement this for methods where resolution is pure computation
99/// (e.g., `did:key`, `did:peer`). Every `Resolver` is automatically
100/// an [`AsyncResolver`] via blanket impl.
101pub trait Resolver: Send + Sync {
102 /// Human-readable name for this resolver (e.g., `"KeyResolver"`).
103 ///
104 /// Must be unique within a method's resolver chain. Used by
105 /// `find_resolver()` to locate resolvers by name.
106 fn name(&self) -> &str;
107
108 /// Attempt to resolve the given DID to a Document.
109 fn resolve(&self, did: &DID) -> Resolution;
110}
111
112/// Asynchronous DID resolver for methods that require IO.
113///
114/// Implement this directly for methods that need network access,
115/// database lookups, or other async operations (e.g., `did:web`, `did:ethr`).
116///
117/// Sync resolvers get this for free via the blanket impl.
118///
119/// This trait is dyn-compatible: the SDK stores resolvers as
120/// `Box<dyn AsyncResolver>` for composition.
121pub trait AsyncResolver: Send + Sync {
122 /// Human-readable name for this resolver (e.g., `"EthrResolver"`).
123 ///
124 /// Must be unique within a method's resolver chain. Used by
125 /// `find_resolver()` to locate resolvers by name.
126 fn name(&self) -> &str;
127
128 /// Attempt to resolve the given DID to a Document.
129 fn resolve<'a>(&'a self, did: &'a DID)
130 -> Pin<Box<dyn Future<Output = Resolution> + Send + 'a>>;
131}
132
133/// Every sync [`Resolver`] is automatically an [`AsyncResolver`].
134impl<T: Resolver> AsyncResolver for T {
135 fn name(&self) -> &str {
136 Resolver::name(self)
137 }
138
139 fn resolve<'a>(
140 &'a self,
141 did: &'a DID,
142 ) -> Pin<Box<dyn Future<Output = Resolution> + Send + 'a>> {
143 Box::pin(std::future::ready(Resolver::resolve(self, did)))
144 }
145}