Skip to main content

muster/domain/config/
spec.rs

1use std::path::PathBuf;
2
3use getset::{Getters, WithSetters};
4use serde::{Deserialize, Serialize};
5use typed_builder::TypedBuilder;
6
7use crate::domain::{
8    process::{Process, ProcessKind, RestartPolicy, StopPolicy},
9    value::{CommandLine, Description, PaneId, ProcessName},
10};
11
12/// Declarative definition of one process within a workspace section.
13#[derive(Clone, Debug, Serialize, Deserialize, Getters, WithSetters, TypedBuilder)]
14#[set_with]
15pub struct ProcessSpec {
16    #[getset(get = "pub", set_with = "pub")]
17    name: ProcessName,
18    #[getset(get = "pub", set_with = "pub")]
19    #[builder(default)]
20    command: Option<CommandLine>,
21    #[getset(get = "pub", set_with = "pub")]
22    #[builder(default)]
23    working_dir: Option<PathBuf>,
24    #[getset(get = "pub", set_with = "pub")]
25    #[builder(default)]
26    description: Option<Description>,
27    #[getset(get = "pub", set_with = "pub")]
28    #[builder(default)]
29    restart: Option<RestartPolicy>,
30    /// Optional graceful shutdown policy. Only command specs may configure it.
31    #[getset(get = "pub", set_with = "pub")]
32    #[builder(default)]
33    stop: Option<StopPolicy>,
34    /// Whether to launch this process automatically on load. Absent means the
35    /// per-kind default; `true`/`false` overrides it.
36    #[getset(get = "pub", set_with = "pub")]
37    #[builder(default)]
38    autostart: Option<bool>,
39}
40
41impl ProcessSpec {
42    /// Builds the corresponding `Process` entity in its initial `Pending` state.
43    pub fn to_process(&self, id: PaneId, kind: ProcessKind) -> Process {
44        Process::builder()
45            .id(id)
46            .name(self.name.clone())
47            .kind(kind)
48            .command(self.command.clone())
49            .working_dir(self.working_dir.clone())
50            .description(self.description.clone())
51            .restart(self.restart_policy())
52            .stop(if kind == ProcessKind::Command {
53                self.stop.clone()
54            } else {
55                None
56            })
57            .autostart(self.should_autostart(kind))
58            .build()
59    }
60
61    /// The effective restart policy, treating an absent policy as `Never`.
62    pub fn restart_policy(&self) -> RestartPolicy {
63        self.restart.unwrap_or(RestartPolicy::Never)
64    }
65
66    /// Whether this process auto-starts. Defaults to false for commands (which
67    /// wait for an explicit start) and true for agents and terminals.
68    pub fn should_autostart(&self, kind: ProcessKind) -> bool {
69        self.autostart.unwrap_or(kind != ProcessKind::Command)
70    }
71
72    /// The effective graceful shutdown policy for `kind`. Commands use the
73    /// domain default when the config omits `stop`; other kinds have no policy.
74    pub fn effective_stop_policy(&self, kind: ProcessKind) -> Option<StopPolicy> {
75        if kind == ProcessKind::Command {
76            Some(self.stop.clone().unwrap_or_default())
77        } else {
78            None
79        }
80    }
81}