lash_core/runtime/
process_work_driver.rs1use std::sync::Arc;
2
3use super::DurableProcessWorker;
4use super::process::{
5 ProcessAttach, ProcessAwaiter, ProcessChangeHub, ProcessEvent, ProcessEventSink,
6 ProcessRegistry, watch_process_registry_with_sink,
7};
8use crate::{PluginError, ProcessAwaitOutput};
9
10#[derive(Clone)]
18pub struct ProcessWorkDriver {
19 registry: Arc<dyn ProcessRegistry>,
20 run_handle: Arc<dyn ProcessRunHandle>,
21 awaiter: ProcessAwaiter,
22 attach: Option<Arc<dyn ProcessAttach>>,
23 hub: ProcessChangeHub,
24}
25
26impl ProcessWorkDriver {
27 pub fn new(registry: Arc<dyn ProcessRegistry>, run_handle: Arc<dyn ProcessRunHandle>) -> Self {
28 Self::new_with_sink(registry, run_handle, None)
29 }
30
31 pub fn new_with_sink(
37 registry: Arc<dyn ProcessRegistry>,
38 run_handle: Arc<dyn ProcessRunHandle>,
39 sink: Option<Arc<dyn ProcessEventSink>>,
40 ) -> Self {
41 let (registry, hub) = watch_process_registry_with_sink(registry, sink);
42 Self::from_watched(registry, hub, run_handle)
43 }
44
45 pub fn from_watched(
46 registry: Arc<dyn ProcessRegistry>,
47 hub: ProcessChangeHub,
48 run_handle: Arc<dyn ProcessRunHandle>,
49 ) -> Self {
50 let awaiter = ProcessAwaiter::new(Arc::clone(®istry), hub.clone());
51 Self {
52 registry,
53 run_handle,
54 awaiter,
55 attach: None,
56 hub,
57 }
58 }
59
60 pub fn with_attach(mut self, attach: Arc<dyn ProcessAttach>) -> Self {
61 self.attach = Some(attach);
62 self
63 }
64
65 pub fn inline(registry: Arc<dyn ProcessRegistry>, worker: DurableProcessWorker) -> Self {
66 Self::new(registry, Arc::new(InlineProcessRunHandle::new(worker)))
67 }
68
69 pub fn process_registry(&self) -> Arc<dyn ProcessRegistry> {
70 Arc::clone(&self.registry)
71 }
72
73 pub fn change_hub(&self) -> ProcessChangeHub {
74 self.hub.clone()
75 }
76
77 pub fn awaiter(&self) -> ProcessAwaiter {
78 self.awaiter.clone()
79 }
80
81 pub async fn await_terminal(
82 &self,
83 process_id: &str,
84 ) -> Result<ProcessAwaitOutput, PluginError> {
85 let record = self
86 .registry
87 .get_process(process_id)
88 .await
89 .ok_or_else(|| PluginError::Session(format!("unknown process `{process_id}`")))?;
90 if let Some(output) = record.status.await_output() {
91 return Ok(output.clone());
92 }
93 if let Some(attach) = self.attach.as_ref() {
94 return attach.await_terminal(process_id).await;
95 }
96 self.awaiter.await_terminal(process_id).await
97 }
98
99 pub async fn await_event(
100 &self,
101 process_id: &str,
102 event_type: &str,
103 after_sequence: u64,
104 ) -> Result<ProcessEvent, PluginError> {
105 self.awaiter
106 .await_event(process_id, event_type, after_sequence)
107 .await
108 }
109
110 pub async fn claim_and_run_pending(&self, reason: &str) -> Result<(), PluginError> {
111 if let Err(err) = self.run_handle.claim_and_run_pending().await {
112 tracing::warn!("process work drive ({reason}) failed: {err}");
113 return Err(err);
114 }
115 Ok(())
116 }
117}
118
119#[async_trait::async_trait]
125pub trait ProcessRunHandle: Send + Sync {
126 async fn claim_and_run_pending(&self) -> Result<(), PluginError>;
129}
130
131pub struct InlineProcessRunHandle {
137 worker: DurableProcessWorker,
138}
139
140impl InlineProcessRunHandle {
141 pub fn new(worker: DurableProcessWorker) -> Self {
142 Self { worker }
143 }
144}
145
146#[async_trait::async_trait]
147impl ProcessRunHandle for InlineProcessRunHandle {
148 async fn claim_and_run_pending(&self) -> Result<(), PluginError> {
149 self.worker.drive_pending_processes().await
150 }
151}