Skip to main content

rskit_process/persistent/
config.rs

1use std::time::Duration;
2
3use crate::{ProcessSpec, command::DEFAULT_MAX_OUTPUT_BYTES};
4
5/// Persistent process readiness policy.
6#[derive(Debug, Clone, Eq, PartialEq)]
7#[non_exhaustive]
8pub enum PersistentReadiness {
9    /// The process is ready immediately after it is spawned.
10    Started,
11    /// The process is ready when either output stream contains the text.
12    OutputContains(String),
13    /// The process is ready when a command exits successfully.
14    Command(ProcessSpec),
15}
16
17/// Configuration for a persistent process.
18#[derive(Debug, Clone)]
19pub struct PersistentConfig {
20    /// Readiness policy used before returning the running process.
21    pub readiness: PersistentReadiness,
22    /// Maximum time to wait for readiness.
23    pub readiness_timeout: Duration,
24    /// Maximum time to wait after a graceful shutdown request before killing.
25    pub shutdown_grace_period: Duration,
26    /// Maximum retained bytes for each captured output stream.
27    pub max_capture_bytes: Option<usize>,
28    /// Output forwarding policy.
29    pub output: PersistentOutput,
30}
31
32impl Default for PersistentConfig {
33    fn default() -> Self {
34        Self {
35            readiness: PersistentReadiness::Started,
36            readiness_timeout: Duration::from_secs(30),
37            shutdown_grace_period: Duration::from_secs(5),
38            max_capture_bytes: Some(DEFAULT_MAX_OUTPUT_BYTES),
39            output: PersistentOutput::capture_only(),
40        }
41    }
42}
43
44impl PersistentConfig {
45    /// Set the readiness policy.
46    #[must_use]
47    pub fn with_readiness(mut self, readiness: PersistentReadiness) -> Self {
48        self.readiness = readiness;
49        self
50    }
51
52    /// Set the readiness timeout.
53    #[must_use]
54    pub fn with_readiness_timeout(mut self, timeout: Duration) -> Self {
55        self.readiness_timeout = timeout;
56        self
57    }
58
59    /// Set the shutdown grace period.
60    #[must_use]
61    pub fn with_shutdown_grace_period(mut self, grace_period: Duration) -> Self {
62        self.shutdown_grace_period = grace_period;
63        self
64    }
65
66    /// Set the maximum retained bytes for each captured output stream.
67    #[must_use]
68    pub fn with_max_capture_bytes(mut self, bytes: usize) -> Self {
69        self.max_capture_bytes = Some(bytes);
70        self
71    }
72
73    /// Disable capture bounds.
74    #[must_use]
75    pub fn with_unbounded_capture(mut self) -> Self {
76        self.max_capture_bytes = None;
77        self
78    }
79
80    /// Set the output forwarding policy.
81    #[must_use]
82    pub fn with_output(mut self, output: PersistentOutput) -> Self {
83        self.output = output;
84        self
85    }
86}
87
88/// Persistent process output forwarding policy.
89#[derive(Debug, Clone, Copy)]
90pub struct PersistentOutput {
91    stdout: Option<PersistentOutputStream>,
92    stderr: Option<PersistentOutputStream>,
93}
94
95impl PersistentOutput {
96    /// Capture output without forwarding it to the parent process streams.
97    #[must_use]
98    pub const fn capture_only() -> Self {
99        Self {
100            stdout: None,
101            stderr: None,
102        }
103    }
104
105    /// Capture and forward stdout/stderr to the selected parent streams.
106    #[must_use]
107    pub const fn forward(stdout: PersistentOutputStream, stderr: PersistentOutputStream) -> Self {
108        Self {
109            stdout: Some(stdout),
110            stderr: Some(stderr),
111        }
112    }
113
114    pub(in crate::persistent) const fn stdout_stream(self) -> Option<PersistentOutputStream> {
115        self.stdout
116    }
117
118    pub(in crate::persistent) const fn stderr_stream(self) -> Option<PersistentOutputStream> {
119        self.stderr
120    }
121}
122
123/// Parent stream used for forwarding persistent output.
124#[derive(Debug, Clone, Copy, Eq, PartialEq)]
125#[non_exhaustive]
126pub enum PersistentOutputStream {
127    /// Forward bytes to parent stdout.
128    Stdout,
129    /// Forward bytes to parent stderr.
130    Stderr,
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn persistent_config_builders_update_nested_policy() {
139        let readiness = PersistentReadiness::OutputContains("ready".to_string());
140        let output = PersistentOutput::forward(
141            PersistentOutputStream::Stdout,
142            PersistentOutputStream::Stderr,
143        );
144        let config = PersistentConfig::default()
145            .with_readiness(readiness.clone())
146            .with_readiness_timeout(Duration::from_secs(2))
147            .with_shutdown_grace_period(Duration::from_secs(3))
148            .with_max_capture_bytes(64)
149            .with_output(output);
150
151        assert_eq!(config.readiness, readiness);
152        assert_eq!(config.readiness_timeout, Duration::from_secs(2));
153        assert_eq!(config.shutdown_grace_period, Duration::from_secs(3));
154        assert_eq!(config.max_capture_bytes, Some(64));
155        assert_eq!(
156            config.output.stdout_stream(),
157            Some(PersistentOutputStream::Stdout)
158        );
159        assert_eq!(
160            config.output.stderr_stream(),
161            Some(PersistentOutputStream::Stderr)
162        );
163
164        assert_eq!(config.with_unbounded_capture().max_capture_bytes, None);
165    }
166}