use crate::envelope::{e_req_type, e_to_type, Envelope, OneshotErr, ReplyErr};
use crate::error::AskError;
use crate::lifecycle::{EVENT_SRV_CHG, EVENT_SRV_CHG_PREFIX};
use crate::main_engine::{PluginInfo, PluginState};
use crate::registry::Registry;
use crate::service::{ProviderRef, ServiceDesc};
use ek_event::{Event, EventEngine, PutError};
use std::any::{Any, type_name};
use std::sync::Arc;
use std::time::Duration;
#[derive(Clone)]
pub struct PluginContext {
pub(crate) event: Arc<EventEngine>,
pub(crate) registry: Arc<Registry>,
pub(crate) name: String,
}
impl PluginContext {
pub(crate) fn new(event: Arc<EventEngine>, registry: Arc<Registry>, name: String) -> Self {
Self { event, registry, name }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn put(&self, event: Event) -> Result<(), PutError> {
self.event.put(event)
}
pub fn notify(&self, target: &str, payload: Arc<dyn Any + Send + Sync>) {
let _ = self.event.put(Event::with_data(e_to_type(target), payload));
}
pub fn plugins(&self) -> Vec<PluginInfo> {
let mut out = Vec::new();
for name in self.registry.names() {
if let Some(info) = self.registry.info_of(&name) {
out.push(info);
}
}
out
}
pub fn services_of(&self, name: &str) -> Option<Arc<Vec<ServiceDesc>>> {
self.registry.services_of(name)
}
pub fn find_service(&self, svc: &str) -> Vec<ProviderRef> {
let mut out = Vec::new();
for name in self.registry.index_providers(svc) {
let Some(list) = self.registry.services_of(&name) else { continue };
let Some(entry) = self.registry.get(&name) else { continue };
if let Some(desc) = list.iter().find(|d| d.id == svc) {
out.push(ProviderRef {
name: entry.name.clone(),
version: entry.plugin.version().into_owned(),
state: entry.state.get(),
service: desc.clone(),
});
}
}
out
}
pub fn publish_services(&self, list: Vec<ServiceDesc>) {
let name = self.name.clone();
let version = self.registry.publish(&name, list);
let _ = self
.event
.put(Event::with_data(format!("{EVENT_SRV_CHG_PREFIX}{name}"), Arc::new(version.clone())));
let _ = self
.event
.put(Event::with_data(EVENT_SRV_CHG, Arc::new(crate::service::PlugNotice { name, version })));
}
pub fn ask<Req, Res>(&self, target: &str, req: Req, timeout: Duration) -> Result<Res, AskError>
where
Req: Any + Send + Sync,
Res: Any + Send + Sync,
{
match self.registry.get(target) {
Some(entry) if entry.state.get() == PluginState::Running => {}
_ => return Err(AskError::TargetGone { target: target.to_string() }),
}
self.deliver(target, req, timeout)
}
pub fn ask_service<Req, Res>(&self, svc: &str, req: Req, timeout: Duration) -> Result<Res, AskError>
where
Req: Any + Send + Sync,
Res: Any + Send + Sync,
{
let providers = self.find_service(svc);
if providers.is_empty() {
return Err(AskError::UnknownService { service: svc.to_string() });
}
let Some(slot) = providers.iter().find(|p| p.state == PluginState::Running) else {
return Err(AskError::TargetGone { target: providers[0].name.clone() });
};
if slot.service.req_type != type_name::<Req>() {
return Err(AskError::InvalidPayload { target: slot.name.clone() });
}
self.deliver(&slot.name.clone(), req, timeout)
}
fn deliver<Req, Res>(&self, target: &str, req: Req, timeout: Duration) -> Result<Res, AskError>
where
Req: Any + Send + Sync,
Res: Any + Send + Sync,
{
let (envelope, reply_rx) = Envelope::new(Arc::new(req));
let put = self.event.put(Event::with_data(e_req_type(target), Arc::new(envelope)));
if put.is_err() {
return Err(AskError::BusStopped);
}
let boxed = reply_rx.recv_timeout(timeout).map_err(|e| match e {
OneshotErr::Timeout => AskError::Timeout { target: target.to_string() },
OneshotErr::Closed => AskError::TargetGone { target: target.to_string() },
})?;
match boxed.downcast::<Result<Res, ReplyErr>>() {
Ok(inner) => match *inner {
Ok(res) => Ok(res),
Err(_) => Err(AskError::InvalidPayload { target: target.to_string() }),
},
Err(_) => Err(AskError::InvalidPayload { target: target.to_string() }),
}
}
}