use std::sync::Arc;
use super::DurableProcessWorker;
use super::process::{
ProcessAttach, ProcessAwaiter, ProcessChangeHub, ProcessEvent, ProcessEventSink,
ProcessRegistry, watch_process_registry_with_sink,
};
use crate::{PluginError, ProcessAwaitOutput};
#[derive(Clone)]
pub struct ProcessWorkDriver {
registry: Arc<dyn ProcessRegistry>,
run_handle: Arc<dyn ProcessRunHandle>,
awaiter: ProcessAwaiter,
attach: Option<Arc<dyn ProcessAttach>>,
hub: ProcessChangeHub,
}
impl ProcessWorkDriver {
pub fn new(registry: Arc<dyn ProcessRegistry>, run_handle: Arc<dyn ProcessRunHandle>) -> Self {
Self::new_with_sink(registry, run_handle, None)
}
pub fn new_with_sink(
registry: Arc<dyn ProcessRegistry>,
run_handle: Arc<dyn ProcessRunHandle>,
sink: Option<Arc<dyn ProcessEventSink>>,
) -> Self {
let (registry, hub) = watch_process_registry_with_sink(registry, sink);
Self::from_watched(registry, hub, run_handle)
}
pub fn from_watched(
registry: Arc<dyn ProcessRegistry>,
hub: ProcessChangeHub,
run_handle: Arc<dyn ProcessRunHandle>,
) -> Self {
let awaiter = ProcessAwaiter::new(Arc::clone(®istry), hub.clone());
Self {
registry,
run_handle,
awaiter,
attach: None,
hub,
}
}
pub fn with_attach(mut self, attach: Arc<dyn ProcessAttach>) -> Self {
self.attach = Some(attach);
self
}
pub fn inline(registry: Arc<dyn ProcessRegistry>, worker: DurableProcessWorker) -> Self {
Self::new(registry, Arc::new(InlineProcessRunHandle::new(worker)))
}
pub fn process_registry(&self) -> Arc<dyn ProcessRegistry> {
Arc::clone(&self.registry)
}
pub fn change_hub(&self) -> ProcessChangeHub {
self.hub.clone()
}
pub fn awaiter(&self) -> ProcessAwaiter {
self.awaiter.clone()
}
pub async fn await_terminal(
&self,
process_id: &str,
) -> Result<ProcessAwaitOutput, PluginError> {
let record = self
.registry
.get_process(process_id)
.await
.ok_or_else(|| PluginError::Session(format!("unknown process `{process_id}`")))?;
if let Some(output) = record.status.await_output() {
return Ok(output.clone());
}
if let Some(attach) = self.attach.as_ref() {
return attach.await_terminal(process_id).await;
}
self.awaiter.await_terminal(process_id).await
}
pub async fn await_event(
&self,
process_id: &str,
event_type: &str,
after_sequence: u64,
) -> Result<ProcessEvent, PluginError> {
self.awaiter
.await_event(process_id, event_type, after_sequence)
.await
}
pub async fn claim_and_run_pending(&self, reason: &str) -> Result<(), PluginError> {
if let Err(err) = self.run_handle.claim_and_run_pending().await {
tracing::warn!("process work drive ({reason}) failed: {err}");
return Err(err);
}
Ok(())
}
}
#[async_trait::async_trait]
pub trait ProcessRunHandle: Send + Sync {
async fn claim_and_run_pending(&self) -> Result<(), PluginError>;
}
pub struct InlineProcessRunHandle {
worker: DurableProcessWorker,
}
impl InlineProcessRunHandle {
pub fn new(worker: DurableProcessWorker) -> Self {
Self { worker }
}
}
#[async_trait::async_trait]
impl ProcessRunHandle for InlineProcessRunHandle {
async fn claim_and_run_pending(&self) -> Result<(), PluginError> {
self.worker.drive_pending_processes().await
}
}