hashtree_resolver/traits.rs
1//! Core traits for root resolvers
2
3use async_trait::async_trait;
4use hashtree_core::Cid;
5use thiserror::Error;
6use tokio::sync::mpsc;
7
8/// Errors that can occur during resolution
9#[derive(Error, Debug)]
10pub enum ResolverError {
11 #[error("Invalid key format: {0}")]
12 InvalidKey(String),
13
14 #[error("Network error: {0}")]
15 Network(String),
16
17 #[error("Not authorized to publish")]
18 NotAuthorized,
19
20 #[error("Resolver stopped")]
21 Stopped,
22
23 #[error("Other error: {0}")]
24 Other(String),
25}
26
27/// Entry in a resolver list
28#[derive(Debug, Clone)]
29pub struct ResolverEntry {
30 pub key: String,
31 pub cid: Cid,
32}
33
34/// RootResolver - Maps human-readable keys to content identifiers (Cid)
35///
36/// This abstraction allows different backends (Nostr, DNS, HTTP, local storage)
37/// to provide mutable pointers to immutable content-addressed data.
38///
39/// The Cid contains:
40/// - hash: content hash (always present)
41/// - key: optional decryption key (for CHK encrypted content)
42/// - size: content size
43///
44/// Unlike the TypeScript version which uses callbacks, this Rust version uses
45/// channels which are more idiomatic for async Rust.
46#[async_trait]
47pub trait RootResolver: Send + Sync {
48 /// Resolve a key to its current Cid (one-shot)
49 ///
50 /// Returns None if the key doesn't exist or can't be resolved.
51 /// For shared content, pass the share_secret to decrypt the encrypted_key.
52 async fn resolve(&self, key: &str) -> Result<Option<Cid>, ResolverError>;
53
54 /// Resolve with a share secret (for encrypted_key decryption)
55 async fn resolve_shared(
56 &self,
57 key: &str,
58 share_secret: &[u8; 32],
59 ) -> Result<Option<Cid>, ResolverError> {
60 let _ = share_secret;
61 self.resolve(key).await
62 }
63
64 /// Subscribe to Cid changes for a key.
65 ///
66 /// Returns a channel receiver that will receive the current value immediately,
67 /// then subsequent updates. The channel is closed when the subscription ends.
68 ///
69 /// To unsubscribe, simply drop the receiver.
70 async fn subscribe(&self, key: &str) -> Result<mpsc::Receiver<Option<Cid>>, ResolverError>;
71
72 /// Publish/update a Cid (optional - only for writable backends)
73 ///
74 /// Returns true if published successfully.
75 async fn publish(&self, key: &str, cid: &Cid) -> Result<bool, ResolverError> {
76 let _ = (key, cid);
77 Err(ResolverError::NotAuthorized)
78 }
79
80 /// Publish with encrypted key for sharing
81 ///
82 /// The key is encrypted with share_secret, allowing anyone with the secret to decrypt.
83 async fn publish_shared(
84 &self,
85 key: &str,
86 cid: &Cid,
87 share_secret: &[u8; 32],
88 ) -> Result<bool, ResolverError> {
89 let _ = (key, cid, share_secret);
90 Err(ResolverError::NotAuthorized)
91 }
92
93 /// List all keys matching a prefix (one-shot)
94 ///
95 /// Returns array of matching keys with their current Cids.
96 async fn list(&self, prefix: &str) -> Result<Vec<ResolverEntry>, ResolverError> {
97 let _ = prefix;
98 Ok(vec![])
99 }
100
101 /// Subscribe to list changes for a prefix.
102 ///
103 /// Returns a channel receiver that will receive the current list immediately,
104 /// then the full updated list on each add/remove/update.
105 async fn subscribe_list(
106 &self,
107 prefix: &str,
108 ) -> Result<mpsc::Receiver<Vec<ResolverEntry>>, ResolverError> {
109 let _ = prefix;
110 Err(ResolverError::Other("Not implemented".into()))
111 }
112
113 /// Stop the resolver and clean up resources
114 async fn stop(&self) -> Result<(), ResolverError> {
115 Ok(())
116 }
117}