use std::{sync::Arc, time::Duration};
use tokio::sync::broadcast;
use crate::core::SupervisorCore;
use crate::error::RuntimeError;
use crate::events::EventKind;
use crate::identity::TaskId;
use crate::tasks::TaskSpec;
use super::outcome::TaskWaiter;
#[derive(Clone)]
pub struct SupervisorHandle {
core: Arc<SupervisorCore>,
#[cfg(feature = "controller")]
controller: Option<Arc<crate::controller::Controller>>,
}
impl std::fmt::Debug for SupervisorHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SupervisorHandle")
.field("core", &self.core)
.finish_non_exhaustive()
}
}
impl SupervisorHandle {
pub(crate) fn new(core: Arc<SupervisorCore>) -> Self {
Self {
core,
#[cfg(feature = "controller")]
controller: None,
}
}
#[cfg(feature = "controller")]
pub(crate) fn with_controller(
mut self,
controller: Option<Arc<crate::controller::Controller>>,
) -> Self {
self.controller = controller;
self
}
pub fn add(&self, spec: TaskSpec) -> Result<TaskId, RuntimeError> {
self.core.add_task(spec)
}
pub async fn add_and_wait(
&self,
spec: TaskSpec,
timeout: Duration,
) -> Result<TaskId, RuntimeError> {
let target: Arc<str> = Arc::from(spec.task().name());
let mut rx = self.core.subscribe_bus();
let id = self.core.add_task(spec)?;
self.wait_registered(&mut rx, id, target, timeout).await
}
pub async fn add_and_watch(
&self,
spec: TaskSpec,
timeout: Duration,
) -> Result<(TaskId, TaskWaiter), RuntimeError> {
let target: Arc<str> = Arc::from(spec.task().name());
let mut rx = self.core.subscribe_bus();
let (id, done_rx) = self.core.add_task_watched(spec)?;
self.wait_registered(&mut rx, id, target, timeout).await?;
Ok((id, TaskWaiter::new(id, done_rx)))
}
async fn wait_registered(
&self,
rx: &mut broadcast::Receiver<Arc<crate::Event>>,
id: TaskId,
target: Arc<str>,
timeout: Duration,
) -> Result<TaskId, RuntimeError> {
let target2 = Arc::clone(&target);
let wait = async move {
loop {
match rx.recv().await {
Ok(ev) if ev.id == Some(id) && ev.kind == EventKind::TaskAdded => {
return Ok(id);
}
Ok(ev) if ev.id == Some(id) && ev.kind == EventKind::TaskAddFailed => {
return Err(RuntimeError::TaskAlreadyExists { name: target2 });
}
Ok(_) => continue,
Err(broadcast::error::RecvError::Lagged(_)) => {
if self.core.contains_id(id).await {
return Ok(id);
}
}
Err(broadcast::error::RecvError::Closed) => {
break;
}
}
}
if self.core.contains_id(id).await {
Ok(id)
} else {
Err(RuntimeError::TaskAddTimeout {
name: target2,
timeout,
})
}
};
match tokio::time::timeout(timeout, wait).await {
Ok(result) => result,
Err(_) => Err(RuntimeError::TaskAddTimeout {
name: target,
timeout,
}),
}
}
pub fn remove(&self, id: TaskId) -> Result<(), RuntimeError> {
self.core.remove(id)
}
pub async fn remove_by_label(&self, name: &str) -> Result<bool, RuntimeError> {
match self.core.id_for_label(name).await {
Some(id) => {
self.core.remove(id)?;
Ok(true)
}
None => Ok(false),
}
}
pub async fn list(&self) -> Vec<(TaskId, Arc<str>)> {
self.core.list_tasks().await
}
pub async fn snapshot(&self) -> Vec<Arc<str>> {
self.core.snapshot().await
}
pub async fn is_alive(&self, name: &str) -> bool {
self.core.is_alive(name).await
}
pub async fn cancel(&self, id: TaskId) -> Result<bool, RuntimeError> {
self.core.cancel(id).await
}
pub async fn cancel_by_label(&self, name: &str) -> Result<bool, RuntimeError> {
match self.core.id_for_label(name).await {
Some(id) => self.core.cancel(id).await,
None => Ok(false),
}
}
pub async fn cancel_with_timeout(
&self,
id: TaskId,
wait_for: Duration,
) -> Result<bool, RuntimeError> {
self.core.cancel_with_timeout(id, wait_for).await
}
pub async fn shutdown(self) -> Result<(), RuntimeError> {
self.core.shutdown().await
}
#[cfg(feature = "controller")]
pub async fn submit(
&self,
spec: crate::controller::ControllerSpec,
) -> Result<TaskId, crate::controller::ControllerError> {
match &self.controller {
Some(ctrl) => ctrl.handle().submit(spec).await,
None => Err(crate::controller::ControllerError::NotConfigured),
}
}
#[cfg(feature = "controller")]
pub fn try_submit(
&self,
spec: crate::controller::ControllerSpec,
) -> Result<TaskId, crate::controller::ControllerError> {
match &self.controller {
Some(ctrl) => ctrl.handle().try_submit(spec),
None => Err(crate::controller::ControllerError::NotConfigured),
}
}
#[cfg(feature = "controller")]
pub async fn submit_and_watch(
&self,
spec: crate::controller::ControllerSpec,
) -> Result<(TaskId, TaskWaiter), crate::controller::ControllerError> {
match &self.controller {
Some(ctrl) => {
let (id, rx) = ctrl.handle().submit_and_watch(spec).await?;
Ok((id, TaskWaiter::new(id, rx)))
}
None => Err(crate::controller::ControllerError::NotConfigured),
}
}
#[cfg(feature = "controller")]
pub async fn controller_snapshot(&self) -> Option<crate::controller::ControllerSnapshot> {
match &self.controller {
Some(ctrl) => Some(ctrl.snapshot().await),
None => None,
}
}
}