Skip to main content

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/// Implement this trait to provide mutable pointers to immutable
37/// content-addressed data. The optional `nostr` feature supplies a Nostr backend.
38///
39/// The Cid contains:
40/// - hash: content hash (always present)
41/// - key: optional decryption key (for CHK encrypted content)
42///
43/// Unlike the TypeScript version which uses callbacks, this Rust version uses
44/// channels which are more idiomatic for async Rust.
45#[async_trait]
46pub trait RootResolver: Send + Sync {
47    /// Resolve a key to its current Cid (one-shot)
48    ///
49    /// Returns `None` if no usable root was found in this lookup. This is not
50    /// proof of absence on a network. For shared content, use `resolve_shared`.
51    async fn resolve(&self, key: &str) -> Result<Option<Cid>, ResolverError>;
52
53    /// Resolve with a share secret (for encrypted_key decryption)
54    async fn resolve_shared(
55        &self,
56        key: &str,
57        share_secret: &[u8; 32],
58    ) -> Result<Option<Cid>, ResolverError> {
59        let _ = share_secret;
60        self.resolve(key).await
61    }
62
63    /// Subscribe to Cid changes for a key.
64    ///
65    /// Returns a channel receiver that will receive the current value immediately,
66    /// then subsequent updates. The channel is closed when the subscription ends.
67    ///
68    /// To unsubscribe, simply drop the receiver.
69    async fn subscribe(&self, key: &str) -> Result<mpsc::Receiver<Option<Cid>>, ResolverError>;
70
71    /// Publish/update a Cid (optional - only for writable backends)
72    ///
73    /// Returns true if published successfully.
74    async fn publish(&self, key: &str, cid: &Cid) -> Result<bool, ResolverError> {
75        let _ = (key, cid);
76        Err(ResolverError::NotAuthorized)
77    }
78
79    /// Publish with encrypted key for sharing
80    ///
81    /// The key is encrypted with share_secret, allowing anyone with the secret to decrypt.
82    async fn publish_shared(
83        &self,
84        key: &str,
85        cid: &Cid,
86        share_secret: &[u8; 32],
87    ) -> Result<bool, ResolverError> {
88        let _ = (key, cid, share_secret);
89        Err(ResolverError::NotAuthorized)
90    }
91
92    /// List all keys matching a prefix (one-shot)
93    ///
94    /// Returns array of matching keys with their current Cids.
95    async fn list(&self, prefix: &str) -> Result<Vec<ResolverEntry>, ResolverError> {
96        let _ = prefix;
97        Ok(vec![])
98    }
99
100    /// Subscribe to list changes for a prefix.
101    ///
102    /// Returns a channel receiver that will receive the current list immediately,
103    /// then the full updated list on each add/remove/update.
104    async fn subscribe_list(
105        &self,
106        prefix: &str,
107    ) -> Result<mpsc::Receiver<Vec<ResolverEntry>>, ResolverError> {
108        let _ = prefix;
109        Err(ResolverError::Other("Not implemented".into()))
110    }
111
112    /// Stop the resolver and clean up resources
113    async fn stop(&self) -> Result<(), ResolverError> {
114        Ok(())
115    }
116}