use std::path::PathBuf;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PortBinding {
pub host_port: u16,
pub guest_port: u16,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FileMount {
pub host_path: PathBuf,
pub guest_path: String,
pub read_only: bool,
}
impl FileMount {
pub fn new(host_path: impl Into<PathBuf>, guest_path: impl Into<String>) -> Self {
Self {
host_path: host_path.into(),
guest_path: guest_path.into(),
read_only: true,
}
}
pub fn read_write(mut self) -> Self {
self.read_only = false;
self
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ExecResult {
pub exit_code: i32,
pub stdout: String,
pub stderr: String,
}
#[derive(Clone, Debug)]
pub struct ContainerSpec {
pub name: String,
pub image: String,
pub env: Vec<(String, String)>,
pub command: Option<Vec<String>>,
pub ports: Vec<PortBinding>,
pub mounts: Vec<FileMount>,
pub network_id: Option<String>,
pub aliases: Vec<String>,
pub run_id: String,
pub memory_limit_mb: Option<u64>,
pub keep_alive: bool,
pub checkpoint_ref: Option<String>,
}
impl ContainerSpec {
pub fn new(
name: impl Into<String>,
image: impl Into<String>,
run_id: impl Into<String>,
) -> Self {
Self {
name: name.into(),
image: image.into(),
env: Vec::new(),
command: None,
ports: Vec::new(),
mounts: Vec::new(),
network_id: None,
aliases: Vec::new(),
run_id: run_id.into(),
memory_limit_mb: None,
keep_alive: false,
checkpoint_ref: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn container_spec_new_defaults_every_optional_field() {
let spec = ContainerSpec::new("rz-deadbeef-0", "redis:8.6-alpine", "deadbeef");
assert_eq!(spec.name, "rz-deadbeef-0");
assert_eq!(spec.image, "redis:8.6-alpine");
assert_eq!(spec.run_id, "deadbeef");
assert!(spec.env.is_empty());
assert!(spec.command.is_none());
assert!(spec.ports.is_empty());
assert!(spec.mounts.is_empty());
assert!(spec.network_id.is_none());
assert!(spec.aliases.is_empty());
assert_eq!(spec.memory_limit_mb, None);
assert!(!spec.keep_alive);
assert!(spec.checkpoint_ref.is_none());
}
#[test]
fn file_mount_defaults_to_read_only() {
let m = FileMount::new("/host/f.txt", "/guest/f.txt");
assert!(m.read_only);
assert_eq!(m.host_path, PathBuf::from("/host/f.txt"));
assert_eq!(m.guest_path, "/guest/f.txt");
}
#[test]
fn file_mount_read_write_flips_the_flag() {
let m = FileMount::new("/host/f.txt", "/guest/f.txt").read_write();
assert!(!m.read_only);
}
#[test]
fn port_binding_and_exec_result_are_plain_value_types() {
let a = PortBinding {
host_port: 32768,
guest_port: 6379,
};
let b = a.clone();
assert_eq!(a, b);
let r = ExecResult {
exit_code: 0,
stdout: "ok".into(),
stderr: String::new(),
};
assert_eq!(r.exit_code, 0);
assert_eq!(r.stdout, "ok");
}
}