hm_vm/types.rs
1use std::path::PathBuf;
2use std::time::Duration;
3
4/// Where to boot the VM from.
5#[derive(Clone, Debug)]
6pub enum ImageSource {
7 /// OCI image reference (e.g., "alpine:latest").
8 Image(String),
9 /// Fork from a previous snapshot.
10 Snapshot(SnapshotId),
11}
12
13/// What to execute inside a VM.
14#[derive(Clone, Debug)]
15pub struct Action {
16 pub source: ImageSource,
17 pub cmd: String,
18 pub env: Vec<(String, String)>,
19 pub working_dir: String,
20 pub timeout: Option<Duration>,
21 /// Host directory to copy into `working_dir` before execution.
22 /// Skipped on cache hits (snapshot already contains prior state).
23 pub inject: Option<PathBuf>,
24}
25
26/// How to cache the result.
27#[derive(Clone, Debug)]
28pub enum CachingPolicy {
29 /// Do not cache.
30 None,
31 /// Cache the resulting snapshot under this key.
32 Cache { key: String },
33}
34
35/// Typed instruction for `Vm::snapshot`, describing how the committed
36/// snapshot should be tagged.
37///
38/// This replaces a previously stringly-encoded convention where a bare label
39/// meant "ephemeral" and a `repo:tag` label meant "cached", a contract that
40/// the producer (`vm.rs`) and consumer (the backend) had to agree on
41/// out-of-band. Encoding the distinction as an enum makes it compiler-checked.
42#[derive(Clone, Debug, PartialEq, Eq)]
43pub enum SnapshotLabel {
44 /// Uncached snapshot. The backend chooses a unique tag (e.g. the
45 /// container id) so concurrent sibling steps do not race to write the
46 /// same image reference.
47 Ephemeral,
48 /// Cached snapshot tagged from this cache key (parsed as `repo:tag`).
49 Cached(String),
50}
51
52/// Opaque snapshot handle. Backend-specific contents.
53///
54/// The inner representation is private so a snapshot id can only be minted
55/// through [`SnapshotId::new`]; read access goes through the `AsRef<str>` /
56/// `Deref<Target = str>` impls or the `Display` impl. This keeps the handle a
57/// distinct domain newtype rather than an interchangeable `String`.
58#[derive(Clone, Debug, Hash, PartialEq, Eq, derive_more::Display)]
59#[display("{_0}")]
60pub struct SnapshotId(String);
61
62impl SnapshotId {
63 /// Construct a snapshot handle from a backend-specific id.
64 pub fn new(id: impl Into<String>) -> Self {
65 Self(id.into())
66 }
67}
68
69impl AsRef<str> for SnapshotId {
70 fn as_ref(&self) -> &str {
71 &self.0
72 }
73}
74
75impl std::ops::Deref for SnapshotId {
76 type Target = str;
77
78 fn deref(&self) -> &str {
79 &self.0
80 }
81}
82
83/// Result of executing an action.
84#[derive(Clone, Debug)]
85pub struct ExecutionResult {
86 pub exit_code: i32,
87 pub snapshot: Option<SnapshotId>,
88 pub cached: bool,
89}
90
91/// VM resource configuration.
92#[derive(Clone, Debug, Default)]
93pub struct VmConfig {
94 pub cpus: Option<u32>,
95 pub memory_mib: Option<u64>,
96 pub disk_size_gb: Option<u64>,
97}
98
99/// Receives stdout/stderr lines during execution.
100pub trait OutputSink: Send + Sync {
101 fn on_stdout(&self, line: &str);
102 fn on_stderr(&self, line: &str);
103}
104
105/// No-op sink for when output is not needed.
106#[derive(Debug)]
107pub struct NullSink;
108
109impl OutputSink for NullSink {
110 fn on_stdout(&self, _line: &str) {}
111 fn on_stderr(&self, _line: &str) {}
112}