Skip to main content

parallax_core/
runtime.rs

1//! Runtime identity and metadata.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// Supported runtime kinds. Extensible without coupling core to adapters.
7#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum RuntimeKind {
10    /// CPython (or compatible) interpreter.
11    Python,
12    /// JavaScript via Node.js (or compatible engine).
13    #[serde(rename = "javascript")]
14    JavaScript,
15    /// WebAssembly via a sandboxed engine.
16    Wasm,
17    /// Reserved for future adapters.
18    Other(String),
19}
20
21impl RuntimeKind {
22    /// Canonical short name used by the CLI.
23    pub fn as_str(&self) -> &str {
24        match self {
25            Self::Python => "python",
26            Self::JavaScript => "javascript",
27            Self::Wasm => "wasm",
28            Self::Other(name) => name.as_str(),
29        }
30    }
31
32    /// Parse a runtime name from CLI / config input.
33    pub fn parse(s: &str) -> Option<Self> {
34        match s.to_ascii_lowercase().as_str() {
35            "python" | "py" | "cpython" => Some(Self::Python),
36            "javascript" | "js" | "node" | "nodejs" => Some(Self::JavaScript),
37            "wasm" | "webassembly" | "wat" => Some(Self::Wasm),
38            other if !other.is_empty() => Some(Self::Other(other.to_string())),
39            _ => None,
40        }
41    }
42
43    /// File extensions typically associated with this runtime.
44    pub fn extensions(&self) -> &'static [&'static str] {
45        match self {
46            Self::Python => &["py"],
47            Self::JavaScript => &["js", "mjs", "cjs"],
48            Self::Wasm => &["wasm", "wat"],
49            Self::Other(_) => &[],
50        }
51    }
52
53    /// Infer runtime from a file path extension.
54    ///
55    /// First-class production runtimes map to named variants; everything else
56    /// becomes [`RuntimeKind::Other`] with a canonical connector id when known.
57    pub fn from_path(path: &std::path::Path) -> Option<Self> {
58        let ext = path.extension()?.to_str()?.to_ascii_lowercase();
59        match ext.as_str() {
60            "py" => Some(Self::Python),
61            "js" | "mjs" | "cjs" | "jsx" => Some(Self::JavaScript),
62            "wasm" | "wat" => Some(Self::Wasm),
63            // Broad connector map (canonical ids used by parallax-connectors).
64            "ts" | "tsx" => Some(Self::Other("typescript".into())),
65            "go" => Some(Self::Other("go".into())),
66            "rs" => Some(Self::Other("rust".into())),
67            "java" => Some(Self::Other("java".into())),
68            "kt" | "kts" => Some(Self::Other("kotlin".into())),
69            "scala" | "sc" => Some(Self::Other("scala".into())),
70            "cs" | "csx" => Some(Self::Other("csharp".into())),
71            "fs" | "fsx" => Some(Self::Other("fsharp".into())),
72            "rb" => Some(Self::Other("ruby".into())),
73            "php" => Some(Self::Other("php".into())),
74            "pl" | "pm" => Some(Self::Other("perl".into())),
75            "lua" => Some(Self::Other("lua".into())),
76            "r" => Some(Self::Other("r".into())),
77            "jl" => Some(Self::Other("julia".into())),
78            "swift" => Some(Self::Other("swift".into())),
79            "dart" => Some(Self::Other("dart".into())),
80            "ex" | "exs" => Some(Self::Other("elixir".into())),
81            "erl" | "hrl" => Some(Self::Other("erlang".into())),
82            "hs" | "lhs" => Some(Self::Other("haskell".into())),
83            "ml" | "mli" => Some(Self::Other("ocaml".into())),
84            "clj" | "cljs" | "cljc" => Some(Self::Other("clojure".into())),
85            "c" | "h" => Some(Self::Other("c".into())),
86            "cpp" | "cc" | "cxx" | "hpp" | "hh" => Some(Self::Other("cpp".into())),
87            "zig" => Some(Self::Other("zig".into())),
88            "nim" => Some(Self::Other("nim".into())),
89            "cr" => Some(Self::Other("crystal".into())),
90            "sol" => Some(Self::Other("solidity".into())),
91            "ps1" | "psm1" => Some(Self::Other("powershell".into())),
92            "sh" | "bash" | "zsh" => Some(Self::Other("shell".into())),
93            "sql" => Some(Self::Other("sql".into())),
94            _ => None,
95        }
96    }
97}
98
99impl fmt::Display for RuntimeKind {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        write!(f, "{}", self.as_str())
102    }
103}
104
105/// High-level availability of a runtime on the host.
106#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
107#[serde(rename_all = "snake_case")]
108pub enum RuntimeStatus {
109    /// Runtime binary/engine is present and responsive.
110    Ready,
111    /// Runtime is installed but failed a health check.
112    Degraded {
113        /// Human-readable reason.
114        reason: String,
115    },
116    /// Runtime is not available on this host.
117    Unavailable {
118        /// Human-readable reason.
119        reason: String,
120    },
121}
122
123/// Static metadata describing a runtime adapter.
124#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
125pub struct RuntimeMetadata {
126    /// Runtime kind.
127    pub kind: RuntimeKind,
128    /// Human-readable display name.
129    pub name: String,
130    /// Adapter implementation version.
131    pub adapter_version: String,
132    /// Detected host runtime version, if known.
133    pub host_version: Option<String>,
134    /// Adapter interface version this implementation targets.
135    pub interface_version: u32,
136    /// Short description.
137    pub description: String,
138}
139
140impl RuntimeMetadata {
141    /// Construct metadata for a built-in adapter.
142    pub fn builtin(
143        kind: RuntimeKind,
144        name: impl Into<String>,
145        description: impl Into<String>,
146    ) -> Self {
147        Self {
148            kind,
149            name: name.into(),
150            adapter_version: crate::PARALLAX_VERSION.to_string(),
151            host_version: None,
152            interface_version: crate::ADAPTER_INTERFACE_VERSION,
153            description: description.into(),
154        }
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn parse_aliases() {
164        assert_eq!(RuntimeKind::parse("py"), Some(RuntimeKind::Python));
165        assert_eq!(RuntimeKind::parse("node"), Some(RuntimeKind::JavaScript));
166        assert_eq!(RuntimeKind::parse("wat"), Some(RuntimeKind::Wasm));
167    }
168
169    #[test]
170    fn from_path() {
171        assert_eq!(
172            RuntimeKind::from_path(std::path::Path::new("demo.py")),
173            Some(RuntimeKind::Python)
174        );
175    }
176}