use std::time::Duration; use anyhow::Result; use chainfire_client::{Client, EventType}; use tracing::{info, warn}; #[derive(Debug, Clone, Copy)] pub enum WatchScope { Key, Prefix, } #[derive(Debug, Clone)] pub struct WatchChange { pub event_type: EventType, pub key: Vec, pub value: Vec, #[allow(dead_code)] pub revision: u64, } pub struct ChainfireWatcher { endpoint: String, scope: WatchScope, target: Vec, reconnect_backoff: Duration, } impl ChainfireWatcher { pub fn key(endpoint: String, key: Vec) -> Self { Self { endpoint, scope: WatchScope::Key, target: key, reconnect_backoff: Duration::from_secs(1), } } pub fn prefix(endpoint: String, prefix: Vec) -> Self { Self { endpoint, scope: WatchScope::Prefix, target: prefix, reconnect_backoff: Duration::from_secs(1), } } pub async fn watch_with_ready(&self, mut on_connected: G, mut callback: F) -> Result<()> where F: FnMut(WatchChange) -> Result<()>, G: FnMut() -> Result<()>, { loop { match Client::connect(self.endpoint.clone()).await { Ok(mut client) => { let watch_result = match self.scope { WatchScope::Key => client.watch(&self.target).await, WatchScope::Prefix => client.watch_prefix(&self.target).await, }; match watch_result { Ok(mut handle) => { info!( scope = ?self.scope, target = %String::from_utf8_lossy(&self.target), watch_id = handle.id(), "connected ChainFire watch" ); on_connected()?; while let Some(event) = handle.recv().await { if let Err(error) = callback(WatchChange { event_type: event.event_type, key: event.key, value: event.value, revision: event.revision, }) { warn!(error = %error, "watch callback failed"); } } warn!( scope = ?self.scope, target = %String::from_utf8_lossy(&self.target), "ChainFire watch stream ended; reconnecting" ); } Err(error) => { warn!( error = %error, scope = ?self.scope, target = %String::from_utf8_lossy(&self.target), "failed to create ChainFire watch" ); } } } Err(error) => { warn!( error = %error, scope = ?self.scope, target = %String::from_utf8_lossy(&self.target), "failed to connect ChainFire watch" ); } } tokio::time::sleep(self.reconnect_backoff).await; } } }