use crate::StatusResult;
use kaspa_core::trace;
use kaspa_grpc_core::protowire::KaspadResponse;
use kaspa_notify::{connection::Connection, error::Error as NotificationError, listener::ListenerId, notifier::Notifier};
use kaspa_rpc_core::Notification;
use std::{
collections::HashMap,
net::SocketAddr,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
use tokio::sync::mpsc::Sender;
pub type GrpcSender = Sender<StatusResult<KaspadResponse>>;
#[derive(Debug)]
struct Inner {
pub address: SocketAddr, pub sender: GrpcSender,
pub closed: AtomicBool,
}
#[derive(Clone, Debug)]
pub struct GrpcConnection {
inner: Arc<Inner>,
}
impl GrpcConnection {
pub fn new(address: SocketAddr, sender: GrpcSender) -> Self {
Self { inner: Arc::new(Inner { address, sender, closed: AtomicBool::new(false) }) }
}
}
#[derive(Clone, Debug, Hash, Eq, PartialEq, Default)]
pub enum GrpcEncoding {
#[default]
ProtowireResponse = 0,
}
impl Connection for GrpcConnection {
type Notification = Notification;
type Message = Arc<StatusResult<KaspadResponse>>;
type Encoding = GrpcEncoding;
type Error = super::error::Error;
fn encoding(&self) -> Self::Encoding {
GrpcEncoding::ProtowireResponse
}
fn into_message(notification: &kaspa_rpc_core::Notification, _: &Self::Encoding) -> Self::Message {
Arc::new(Ok((notification).into()))
}
fn send(&self, message: Self::Message) -> Result<(), Self::Error> {
match !self.is_closed() {
true => Ok(self.inner.sender.try_send((*message).clone())?),
false => Err(NotificationError::ConnectionClosed.into()),
}
}
fn close(&self) -> bool {
self.inner.closed.store(true, Ordering::SeqCst);
true
}
fn is_closed(&self) -> bool {
self.inner.sender.is_closed() || self.inner.closed.load(Ordering::SeqCst)
}
}
pub(crate) struct GrpcConnectionManager {
connections: HashMap<SocketAddr, GrpcConnection>,
notifier: Arc<Notifier<Notification, GrpcConnection>>,
}
impl GrpcConnectionManager {
pub fn new(notifier: Arc<Notifier<Notification, GrpcConnection>>) -> Self {
Self { connections: HashMap::new(), notifier }
}
pub fn register(&mut self, address: SocketAddr, sender: GrpcSender) -> ListenerId {
let connection = GrpcConnection::new(address, sender);
let id = self.notifier.clone().register_new_listener(connection.clone());
trace!("registering a new gRPC connection from: {address} with listener id {id}");
self.connections.insert(address, connection);
id
}
pub fn unregister(&mut self, address: SocketAddr) {
if let Some(connection) = self.connections.remove(&address) {
trace!("dismiss a gRPC connection from: {}", connection.inner.address);
connection.close();
}
}
}