use std::collections::HashMap;
use std::sync::Arc;
use serde::{Deserialize, Serialize};
use tokio::sync::{mpsc, RwLock};
use crate::channels::{ComponentStatus, ComponentType};
#[derive(Debug, Clone)]
pub enum ComponentUpdate {
Status {
component_id: String,
status: ComponentStatus,
message: Option<String>,
},
}
pub type ComponentUpdateSender = mpsc::Sender<ComponentUpdate>;
pub type ComponentUpdateReceiver = mpsc::Receiver<ComponentUpdate>;
#[derive(Clone)]
pub struct ComponentStatusHandle {
component_id: String,
status: Arc<RwLock<ComponentStatus>>,
update_tx: Arc<tokio::sync::OnceCell<ComponentUpdateSender>>,
}
impl ComponentStatusHandle {
pub fn new(component_id: impl Into<String>) -> Self {
Self {
component_id: component_id.into(),
status: Arc::new(RwLock::new(ComponentStatus::Stopped)),
update_tx: Arc::new(tokio::sync::OnceCell::new()),
}
}
pub fn new_wired(component_id: impl Into<String>, update_tx: ComponentUpdateSender) -> Self {
let cell = tokio::sync::OnceCell::new();
let _ = cell.set(update_tx);
Self {
component_id: component_id.into(),
status: Arc::new(RwLock::new(ComponentStatus::Stopped)),
update_tx: Arc::new(cell),
}
}
pub async fn wire(&self, update_tx: ComponentUpdateSender) {
let _ = self.update_tx.set(update_tx);
}
pub async fn set_status(&self, status: ComponentStatus, message: Option<String>) {
{
*self.status.write().await = status;
}
if let Some(tx) = self.update_tx.get() {
if let Err(e) = tx
.send(ComponentUpdate::Status {
component_id: self.component_id.clone(),
status,
message,
})
.await
{
log::warn!(
"Status update for '{}' dropped (channel closed): {e}",
self.component_id
);
}
}
}
pub async fn get_status(&self) -> ComponentStatus {
*self.status.read().await
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum ComponentKind {
Instance,
Source,
Query,
Reaction,
BootstrapProvider,
IdentityProvider,
}
impl std::fmt::Display for ComponentKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ComponentKind::Instance => write!(f, "instance"),
ComponentKind::Source => write!(f, "source"),
ComponentKind::Query => write!(f, "query"),
ComponentKind::Reaction => write!(f, "reaction"),
ComponentKind::BootstrapProvider => write!(f, "bootstrap_provider"),
ComponentKind::IdentityProvider => write!(f, "identity_provider"),
}
}
}
impl ComponentKind {
pub fn to_component_type(&self) -> Option<ComponentType> {
match self {
ComponentKind::Source => Some(ComponentType::Source),
ComponentKind::Query => Some(ComponentType::Query),
ComponentKind::Reaction => Some(ComponentType::Reaction),
ComponentKind::BootstrapProvider => Some(ComponentType::BootstrapProvider),
ComponentKind::IdentityProvider => Some(ComponentType::IdentityProvider),
ComponentKind::Instance => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum RelationshipKind {
Owns,
OwnedBy,
Feeds,
SubscribesTo,
Bootstraps,
BootstrappedBy,
Authenticates,
AuthenticatedBy,
}
impl RelationshipKind {
pub fn reverse(&self) -> Self {
match self {
RelationshipKind::Owns => RelationshipKind::OwnedBy,
RelationshipKind::OwnedBy => RelationshipKind::Owns,
RelationshipKind::Feeds => RelationshipKind::SubscribesTo,
RelationshipKind::SubscribesTo => RelationshipKind::Feeds,
RelationshipKind::Bootstraps => RelationshipKind::BootstrappedBy,
RelationshipKind::BootstrappedBy => RelationshipKind::Bootstraps,
RelationshipKind::Authenticates => RelationshipKind::AuthenticatedBy,
RelationshipKind::AuthenticatedBy => RelationshipKind::Authenticates,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentNode {
pub id: String,
pub kind: ComponentKind,
pub status: ComponentStatus,
#[serde(default)]
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphSnapshot {
pub instance_id: String,
pub nodes: Vec<ComponentNode>,
pub edges: Vec<GraphEdge>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphEdge {
pub from: String,
pub to: String,
pub relationship: RelationshipKind,
}