use std::{
io,
sync::{Arc, OnceLock},
};
use dashmap::{DashMap, Entry};
use qbase::{net::addr::BindUri, util::UniqueIdGenerator};
use super::RwInterface;
use crate::{
factory::ProductQuicIO,
iface::{
BindInterface, Interface, QuicInterface, context::InterfaceContext,
physical::PhysicalInterfaces,
},
local::Locations,
};
#[derive(Default, Debug)]
pub struct QuicInterfaces {
interfaces: DashMap<BindUri, InterfaceContext>,
pub(super) bind_id_generator: UniqueIdGenerator,
}
impl QuicInterfaces {
#[inline]
pub fn global() -> &'static Arc<Self> {
static GLOBAL: OnceLock<Arc<QuicInterfaces>> = OnceLock::new();
GLOBAL.get_or_init(QuicInterfaces::new)
}
#[inline]
pub fn new() -> Arc<Self> {
Arc::new(Self::default())
}
pub fn bind(
self: &Arc<Self>,
bind_uri: BindUri,
factory: Arc<dyn ProductQuicIO>,
) -> BindInterface {
let entry = self.interfaces.entry(bind_uri.clone());
if let Entry::Occupied(entry) = &entry
&& let Some(iface) = entry.get().iface().upgrade()
{
return iface.binding();
}
let iface = Arc::new(RwInterface::new(bind_uri, factory, self.clone()));
let context =
InterfaceContext::new(iface.clone(), PhysicalInterfaces::global().event_receiver());
entry.insert(context);
iface.publish_address();
iface.binding()
}
#[inline]
pub fn borrow(&self, bind_uri: &BindUri) -> Option<QuicInterface> {
self.interfaces
.get(bind_uri)
.and_then(|ctx| ctx.iface().upgrade()?.borrow().ok())
}
#[inline]
pub fn get(&self, bind_uri: &BindUri) -> Option<BindInterface> {
self.interfaces
.get(bind_uri)
.and_then(|ctx| Some(ctx.iface().upgrade()?.binding()))
}
#[inline]
pub fn remove(&self, bind_uri: &BindUri) {
self.interfaces.remove(bind_uri);
}
#[inline]
pub fn clear(&self) {
self.interfaces.retain(|_bind_uri, iface| {
if let Some(iface) = iface.iface().upgrade() {
iface.write().io = Err(io::ErrorKind::NotConnected.into());
}
false
});
}
}
impl Interface {
pub(super) fn close(&mut self) {
self.io = Err(io::ErrorKind::NotConnected.into());
if let Entry::Occupied(entry) = self.ifaces.interfaces.entry(self.bind_uri.clone())
&& entry.get().iface().upgrade().is_none()
{
Locations::global().remove_all(&self.bind_uri);
entry.remove();
}
}
}