rskit_process/persistent/
config.rs1use std::time::Duration;
2
3use crate::{ProcessSpec, command::DEFAULT_MAX_OUTPUT_BYTES, runner::OutputBytesCallback};
4
5#[derive(Debug, Clone, Eq, PartialEq)]
7#[non_exhaustive]
8pub enum PersistentReadiness {
9 Started,
11 OutputContains(String),
13 Command(ProcessSpec),
15}
16
17#[derive(Debug, Clone)]
19pub struct PersistentConfig {
20 pub readiness: PersistentReadiness,
22 pub readiness_timeout: Duration,
24 pub shutdown_grace_period: Duration,
26 pub max_capture_bytes: Option<usize>,
28 pub output: PersistentOutput,
30 pub output_observer: Option<PersistentOutputObserver>,
32}
33
34impl Default for PersistentConfig {
35 fn default() -> Self {
36 Self {
37 readiness: PersistentReadiness::Started,
38 readiness_timeout: Duration::from_secs(30),
39 shutdown_grace_period: Duration::from_secs(5),
40 max_capture_bytes: Some(DEFAULT_MAX_OUTPUT_BYTES),
41 output: PersistentOutput::capture_only(),
42 output_observer: None,
43 }
44 }
45}
46
47impl PersistentConfig {
48 #[must_use]
50 pub fn with_readiness(mut self, readiness: PersistentReadiness) -> Self {
51 self.readiness = readiness;
52 self
53 }
54
55 #[must_use]
57 pub fn with_readiness_timeout(mut self, timeout: Duration) -> Self {
58 self.readiness_timeout = timeout;
59 self
60 }
61
62 #[must_use]
64 pub fn with_shutdown_grace_period(mut self, grace_period: Duration) -> Self {
65 self.shutdown_grace_period = grace_period;
66 self
67 }
68
69 #[must_use]
71 pub fn with_max_capture_bytes(mut self, bytes: usize) -> Self {
72 self.max_capture_bytes = Some(bytes);
73 self
74 }
75
76 #[must_use]
78 pub fn with_unbounded_capture(mut self) -> Self {
79 self.max_capture_bytes = None;
80 self
81 }
82
83 #[must_use]
85 pub fn with_output(mut self, output: PersistentOutput) -> Self {
86 self.output = output;
87 self
88 }
89
90 #[must_use]
92 pub fn with_output_observer(mut self, observer: PersistentOutputObserver) -> Self {
93 self.output_observer = Some(observer);
94 self
95 }
96}
97
98#[derive(Debug, Clone, Copy)]
100pub struct PersistentOutput {
101 stdout: Option<PersistentOutputStream>,
102 stderr: Option<PersistentOutputStream>,
103}
104
105impl PersistentOutput {
106 #[must_use]
108 pub const fn capture_only() -> Self {
109 Self {
110 stdout: None,
111 stderr: None,
112 }
113 }
114
115 #[must_use]
117 pub const fn forward(stdout: PersistentOutputStream, stderr: PersistentOutputStream) -> Self {
118 Self {
119 stdout: Some(stdout),
120 stderr: Some(stderr),
121 }
122 }
123
124 pub(in crate::persistent) const fn stdout_stream(&self) -> Option<PersistentOutputStream> {
125 self.stdout
126 }
127
128 pub(in crate::persistent) const fn stderr_stream(&self) -> Option<PersistentOutputStream> {
129 self.stderr
130 }
131}
132
133#[derive(Clone, Default)]
140pub struct PersistentOutputObserver {
141 stdout_bytes: Option<OutputBytesCallback>,
142 stderr_bytes: Option<OutputBytesCallback>,
143}
144
145impl std::fmt::Debug for PersistentOutputObserver {
146 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 formatter
148 .debug_struct("PersistentOutputObserver")
149 .field("stdout_bytes", &self.stdout_bytes.is_some())
150 .field("stderr_bytes", &self.stderr_bytes.is_some())
151 .finish()
152 }
153}
154
155impl PersistentOutputObserver {
156 #[must_use]
158 pub fn new() -> Self {
159 Self::default()
160 }
161
162 #[must_use]
166 pub fn with_stdout_bytes(mut self, callback: impl Fn(&[u8]) + Send + Sync + 'static) -> Self {
167 self.stdout_bytes = Some(std::sync::Arc::new(callback));
168 self
169 }
170
171 #[must_use]
175 pub fn with_stderr_bytes(mut self, callback: impl Fn(&[u8]) + Send + Sync + 'static) -> Self {
176 self.stderr_bytes = Some(std::sync::Arc::new(callback));
177 self
178 }
179
180 pub(in crate::persistent) fn stdout_bytes_callback(&self) -> Option<OutputBytesCallback> {
181 self.stdout_bytes.clone()
182 }
183
184 pub(in crate::persistent) fn stderr_bytes_callback(&self) -> Option<OutputBytesCallback> {
185 self.stderr_bytes.clone()
186 }
187}
188
189#[derive(Debug, Clone, Copy, Eq, PartialEq)]
191#[non_exhaustive]
192pub enum PersistentOutputStream {
193 Stdout,
195 Stderr,
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn persistent_config_builders_update_nested_policy() {
205 let readiness = PersistentReadiness::OutputContains("ready".to_string());
206 let output = PersistentOutput::forward(
207 PersistentOutputStream::Stdout,
208 PersistentOutputStream::Stderr,
209 );
210 let config = PersistentConfig::default()
211 .with_readiness(readiness.clone())
212 .with_readiness_timeout(Duration::from_secs(2))
213 .with_shutdown_grace_period(Duration::from_secs(3))
214 .with_max_capture_bytes(64)
215 .with_output(output);
216
217 assert_eq!(config.readiness, readiness);
218 assert_eq!(config.readiness_timeout, Duration::from_secs(2));
219 assert_eq!(config.shutdown_grace_period, Duration::from_secs(3));
220 assert_eq!(config.max_capture_bytes, Some(64));
221 assert_eq!(
222 config.output.stdout_stream(),
223 Some(PersistentOutputStream::Stdout)
224 );
225 assert_eq!(
226 config.output.stderr_stream(),
227 Some(PersistentOutputStream::Stderr)
228 );
229 assert!(config.output_observer.is_none());
230
231 assert_eq!(config.with_unbounded_capture().max_capture_bytes, None);
232 }
233
234 #[test]
235 fn persistent_output_remains_copy_forwarding_policy() {
236 let output = PersistentOutput::forward(
237 PersistentOutputStream::Stdout,
238 PersistentOutputStream::Stderr,
239 );
240 let copied = output;
241
242 assert_eq!(copied.stdout_stream(), Some(PersistentOutputStream::Stdout));
243 assert_eq!(output.stderr_stream(), Some(PersistentOutputStream::Stderr));
244 }
245
246 #[test]
247 fn persistent_output_observer_stores_byte_callbacks() {
248 let observer = PersistentOutputObserver::new()
249 .with_stdout_bytes(|_| {})
250 .with_stderr_bytes(|_| {});
251 let config = PersistentConfig::default().with_output_observer(observer);
252
253 let observer = config.output_observer.as_ref().expect("observer is set");
254 assert!(observer.stdout_bytes_callback().is_some());
255 assert!(observer.stderr_bytes_callback().is_some());
256 }
257
258 #[test]
259 fn default_and_observer_debug_are_stable() {
260 let config = PersistentConfig::default();
261 assert_eq!(config.readiness, PersistentReadiness::Started);
262 assert_eq!(config.readiness_timeout, Duration::from_secs(30));
263 assert_eq!(config.shutdown_grace_period, Duration::from_secs(5));
264
265 let observer = PersistentOutputObserver::new().with_stdout_bytes(|_| {});
266 let rendered = format!("{observer:?}");
267 assert!(rendered.contains("stdout_bytes: true"));
268 assert!(rendered.contains("stderr_bytes: false"));
269 }
270}