Skip to main content

lilo_rm_core/
version.rs

1use std::fmt::{Display, Formatter};
2use std::str::FromStr;
3
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6pub const RUNTIME_PROTOCOL_VERSION: &str = "0.3";
7
8pub const RUNTIME_PROTOCOL_CAPABILITIES: &[RuntimeCapability] = &[
9    RuntimeCapability::StructuredProtocolErrors,
10    RuntimeCapability::HeadlessStdioLogPaths,
11    RuntimeCapability::StatusSessionSetFilter,
12    RuntimeCapability::StatusUpdatedSinceFilter,
13    RuntimeCapability::TypedNudgeOutcomes,
14    RuntimeCapability::ValidateTargetPreflight,
15    RuntimeCapability::EventsCursor,
16    RuntimeCapability::EventsLongPoll,
17    RuntimeCapability::TmuxPaneSnapshot,
18];
19
20#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
21pub struct VersionInfo {
22    pub version: String,
23    pub git_sha: String,
24    pub protocol_version: String,
25    pub capabilities: Vec<RuntimeCapability>,
26}
27
28impl VersionInfo {
29    pub fn new(version: impl Into<String>, git_sha: impl Into<String>) -> Self {
30        Self {
31            version: version.into(),
32            git_sha: git_sha.into(),
33            protocol_version: RUNTIME_PROTOCOL_VERSION.to_owned(),
34            capabilities: RUNTIME_PROTOCOL_CAPABILITIES.to_vec(),
35        }
36    }
37}
38
39pub fn version_info() -> VersionInfo {
40    VersionInfo::new(env!("CARGO_PKG_VERSION"), env!("RTM_GIT_SHA"))
41}
42
43#[derive(Clone, Copy, Debug, Eq, PartialEq)]
44pub enum RuntimeCapability {
45    /// Error responses expose stable machine readable codes.
46    StructuredProtocolErrors,
47    /// Headless spawn responses include stdout and stderr log paths.
48    HeadlessStdioLogPaths,
49    /// Status requests accept a set of session ids.
50    StatusSessionSetFilter,
51    /// Status requests accept an updated time lower bound.
52    StatusUpdatedSinceFilter,
53    /// Nudge responses expose typed delivery outcomes.
54    TypedNudgeOutcomes,
55    /// ValidateTarget checks a target string without spawning.
56    ValidateTargetPreflight,
57    /// Events support durable cursor replay.
58    EventsCursor,
59    /// Events requests accept a bounded long poll wait window.
60    EventsLongPoll,
61    /// Tmux targets support on demand pane snapshot capture.
62    TmuxPaneSnapshot,
63}
64
65impl RuntimeCapability {
66    pub const fn as_str(self) -> &'static str {
67        match self {
68            Self::StructuredProtocolErrors => "structured_protocol_errors",
69            Self::HeadlessStdioLogPaths => "headless_stdio_log_paths",
70            Self::StatusSessionSetFilter => "status_session_set_filter",
71            Self::StatusUpdatedSinceFilter => "status_updated_since_filter",
72            Self::TypedNudgeOutcomes => "typed_nudge_outcomes",
73            Self::ValidateTargetPreflight => "validate_target_preflight",
74            Self::EventsCursor => "events_cursor",
75            Self::EventsLongPoll => "events_long_poll",
76            Self::TmuxPaneSnapshot => "tmux_pane_snapshot",
77        }
78    }
79}
80
81impl Display for RuntimeCapability {
82    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
83        formatter.write_str(self.as_str())
84    }
85}
86
87impl FromStr for RuntimeCapability {
88    type Err = String;
89
90    fn from_str(value: &str) -> Result<Self, Self::Err> {
91        match value {
92            "structured_protocol_errors" => Ok(Self::StructuredProtocolErrors),
93            "headless_stdio_log_paths" => Ok(Self::HeadlessStdioLogPaths),
94            "status_session_set_filter" => Ok(Self::StatusSessionSetFilter),
95            "status_updated_since_filter" => Ok(Self::StatusUpdatedSinceFilter),
96            "typed_nudge_outcomes" => Ok(Self::TypedNudgeOutcomes),
97            "validate_target_preflight" => Ok(Self::ValidateTargetPreflight),
98            "events_cursor" => Ok(Self::EventsCursor),
99            "events_long_poll" => Ok(Self::EventsLongPoll),
100            "tmux_pane_snapshot" => Ok(Self::TmuxPaneSnapshot),
101            other => Err(format!("unknown runtime capability {other}")),
102        }
103    }
104}
105
106impl Serialize for RuntimeCapability {
107    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
108    where
109        S: Serializer,
110    {
111        serializer.serialize_str(self.as_str())
112    }
113}
114
115impl<'de> Deserialize<'de> for RuntimeCapability {
116    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117    where
118        D: Deserializer<'de>,
119    {
120        String::deserialize(deserializer)?
121            .parse()
122            .map_err(serde::de::Error::custom)
123    }
124}