use crate::server::channel::Channel;
use crate::server::connection::ConnectionLike;
use parking_lot::Mutex;
use serde_json::Value;
use std::any::Any;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Weak};
type ChildrenRegistry = HashMap<Arc<str>, Arc<dyn ChannelOwner>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DisposeReason {
Closed,
GarbageCollected,
Protocol,
}
#[derive(Clone)]
pub enum ParentOrConnection {
Parent(Arc<dyn ChannelOwner>),
Connection(Arc<dyn ConnectionLike>),
}
pub trait ChannelOwner: Send + Sync {
fn guid(&self) -> &str;
fn type_name(&self) -> &str;
fn parent(&self) -> Option<Arc<dyn ChannelOwner>>;
fn connection(&self) -> Arc<dyn ConnectionLike>;
fn initializer(&self) -> &Value;
fn channel(&self) -> &Channel;
fn dispose(&self, reason: DisposeReason);
fn adopt(&self, child: Arc<dyn ChannelOwner>);
fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>);
fn remove_child(&self, guid: &str);
fn on_event(&self, method: &str, params: Value);
fn was_collected(&self) -> bool;
fn as_any(&self) -> &dyn Any;
}
pub struct ChannelOwnerImpl {
guid: Arc<str>,
type_name: String,
parent: Option<Weak<dyn ChannelOwner>>,
connection: Arc<dyn ConnectionLike>,
children: Arc<Mutex<ChildrenRegistry>>,
channel: Channel,
initializer: Value,
was_collected: AtomicBool,
}
impl Clone for ChannelOwnerImpl {
fn clone(&self) -> Self {
Self {
guid: self.guid.clone(),
type_name: self.type_name.clone(),
parent: self.parent.clone(),
connection: Arc::clone(&self.connection),
children: Arc::clone(&self.children),
channel: self.channel.clone(),
initializer: self.initializer.clone(),
was_collected: AtomicBool::new(
self.was_collected.load(std::sync::atomic::Ordering::SeqCst),
),
}
}
}
impl ChannelOwnerImpl {
pub fn new(
parent: ParentOrConnection,
type_name: String,
guid: Arc<str>,
initializer: Value,
) -> Self {
let (connection, parent_opt) = match parent {
ParentOrConnection::Parent(p) => {
let conn = p.connection();
(conn, Some(Arc::downgrade(&p)))
}
ParentOrConnection::Connection(c) => (c, None),
};
let channel = Channel::new(Arc::clone(&guid), connection.clone());
Self {
guid,
type_name,
parent: parent_opt,
connection,
children: Arc::new(Mutex::new(HashMap::new())),
channel,
initializer,
was_collected: AtomicBool::new(false),
}
}
pub fn guid(&self) -> &str {
&self.guid
}
pub fn type_name(&self) -> &str {
&self.type_name
}
pub fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
self.parent.as_ref().and_then(|p| p.upgrade())
}
pub fn connection(&self) -> Arc<dyn ConnectionLike> {
self.connection.clone()
}
pub fn initializer(&self) -> &Value {
&self.initializer
}
pub fn channel(&self) -> &Channel {
&self.channel
}
pub fn dispose(&self, reason: DisposeReason) {
if reason == DisposeReason::GarbageCollected {
self.was_collected.store(true, Ordering::SeqCst);
}
if let Some(parent) = self.parent() {
parent.remove_child(&self.guid);
}
let connection = self.connection.clone();
let guid = Arc::clone(&self.guid);
tokio::spawn(async move {
connection.unregister_object(&guid).await;
});
let children: Vec<_> = {
let guard = self.children.lock();
guard.values().cloned().collect()
};
for child in children {
child.dispose(reason);
}
self.children.lock().clear();
}
pub fn adopt(&self, child: Arc<dyn ChannelOwner>) {
if let Some(old_parent) = child.parent() {
old_parent.remove_child(child.guid());
}
self.add_child(Arc::from(child.guid()), child);
}
pub fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
self.children.lock().insert(guid, child);
}
pub fn remove_child(&self, guid: &str) {
let guid_arc: Arc<str> = Arc::from(guid);
self.children.lock().remove(&guid_arc);
}
pub fn on_event(&self, method: &str, params: Value) {
tracing::debug!(
"Event on {} ({}): {} -> {:?}",
self.guid,
self.type_name,
method,
params
);
}
pub fn was_collected(&self) -> bool {
self.was_collected.load(Ordering::SeqCst)
}
}