Skip to main content

rustyclaw_core/runtime/
mod.rs

1//! Runtime subsystem for platform abstraction.
2//!
3//! This module provides the [`RuntimeAdapter`] trait and implementations for
4//! different execution environments. The runtime abstraction allows RustyClaw
5//! to run on native systems, Docker containers, and (in future) serverless
6//! platforms with appropriate capability detection.
7//!
8//! Adapted from ZeroClaw (MIT OR Apache-2.0 licensed).
9
10pub mod docker;
11pub mod native;
12pub mod traits;
13
14pub use docker::{DockerRuntime, DockerRuntimeConfig};
15pub use native::NativeRuntime;
16pub use traits::RuntimeAdapter;
17
18use serde::{Deserialize, Serialize};
19
20/// Runtime configuration.
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct RuntimeConfig {
23    /// Runtime kind: "native", "docker", etc.
24    #[serde(default = "default_runtime_kind")]
25    pub kind: String,
26    /// Docker-specific configuration.
27    #[serde(default)]
28    pub docker: DockerRuntimeConfig,
29}
30
31fn default_runtime_kind() -> String {
32    "native".to_string()
33}
34
35impl Default for RuntimeConfig {
36    fn default() -> Self {
37        Self {
38            kind: default_runtime_kind(),
39            docker: DockerRuntimeConfig::default(),
40        }
41    }
42}
43
44/// Factory: create the right runtime from config
45pub fn create_runtime(config: &RuntimeConfig) -> anyhow::Result<Box<dyn RuntimeAdapter>> {
46    match config.kind.as_str() {
47        "native" => Ok(Box::new(NativeRuntime::new())),
48        "docker" => Ok(Box::new(DockerRuntime::new(config.docker.clone()))),
49        other if other.trim().is_empty() => {
50            anyhow::bail!("runtime.kind cannot be empty. Supported values: native, docker")
51        }
52        other => anyhow::bail!("Unknown runtime kind '{other}'. Supported values: native, docker"),
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn factory_native() {
62        let cfg = RuntimeConfig {
63            kind: "native".into(),
64            ..RuntimeConfig::default()
65        };
66        let rt = create_runtime(&cfg).unwrap();
67        assert_eq!(rt.name(), "native");
68        assert!(rt.has_shell_access());
69    }
70
71    #[test]
72    fn factory_docker() {
73        let cfg = RuntimeConfig {
74            kind: "docker".into(),
75            ..RuntimeConfig::default()
76        };
77        let rt = create_runtime(&cfg).unwrap();
78        assert_eq!(rt.name(), "docker");
79        assert!(rt.has_shell_access());
80    }
81
82    #[test]
83    fn factory_unknown_errors() {
84        let cfg = RuntimeConfig {
85            kind: "wasm-edge-unknown".into(),
86            ..RuntimeConfig::default()
87        };
88        match create_runtime(&cfg) {
89            Err(err) => assert!(err.to_string().contains("Unknown runtime kind")),
90            Ok(_) => panic!("unknown runtime should error"),
91        }
92    }
93
94    #[test]
95    fn factory_empty_errors() {
96        let cfg = RuntimeConfig {
97            kind: String::new(),
98            ..RuntimeConfig::default()
99        };
100        match create_runtime(&cfg) {
101            Err(err) => assert!(err.to_string().contains("cannot be empty")),
102            Ok(_) => panic!("empty runtime should error"),
103        }
104    }
105}