Skip to main content

arcbox_container/
state.rs

1//! Container state management.
2
3use crate::config::ContainerConfig;
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7
8/// Container identifier.
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct ContainerId(String);
11
12impl ContainerId {
13    /// Creates a new random container ID.
14    #[must_use]
15    pub fn new() -> Self {
16        Self(Uuid::new_v4().to_string().replace('-', "")[..12].to_string())
17    }
18
19    /// Creates a container ID from a string.
20    #[must_use]
21    pub fn from_string(s: impl Into<String>) -> Self {
22        Self(s.into())
23    }
24
25    /// Returns the ID as a string.
26    #[must_use]
27    pub fn as_str(&self) -> &str {
28        &self.0
29    }
30}
31
32impl Default for ContainerId {
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38impl std::fmt::Display for ContainerId {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        write!(f, "{}", self.0)
41    }
42}
43
44/// Container state.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(rename_all = "lowercase")]
47pub enum ContainerState {
48    /// Container created but not started.
49    Created,
50    /// Container start requested but not yet running.
51    Starting,
52    /// Container is running.
53    Running,
54    /// Container is paused.
55    Paused,
56    /// Container is restarting.
57    Restarting,
58    /// Container has exited.
59    Exited,
60    /// Container is being removed.
61    Removing,
62    /// Container is dead (error state).
63    Dead,
64}
65
66impl std::fmt::Display for ContainerState {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        match self {
69            Self::Created => write!(f, "created"),
70            Self::Starting => write!(f, "starting"),
71            Self::Running => write!(f, "running"),
72            Self::Paused => write!(f, "paused"),
73            Self::Restarting => write!(f, "restarting"),
74            Self::Exited => write!(f, "exited"),
75            Self::Removing => write!(f, "removing"),
76            Self::Dead => write!(f, "dead"),
77        }
78    }
79}
80
81/// Container information.
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct Container {
84    /// Container ID.
85    pub id: ContainerId,
86    /// Container name.
87    pub name: String,
88    /// Image name.
89    pub image: String,
90    /// Machine name (VM where container runs).
91    pub machine_name: Option<String>,
92    /// Current state.
93    pub state: ContainerState,
94    /// Creation time.
95    pub created: DateTime<Utc>,
96    /// Start time (if running).
97    pub started_at: Option<DateTime<Utc>>,
98    /// Finish time (if exited).
99    pub finished_at: Option<DateTime<Utc>>,
100    /// Exit code (if exited).
101    pub exit_code: Option<i32>,
102    /// Container configuration (cmd, env, mounts, etc.).
103    pub config: Option<ContainerConfig>,
104}
105
106impl Container {
107    /// Creates a new container.
108    #[must_use]
109    pub fn new(name: impl Into<String>, image: impl Into<String>) -> Self {
110        Self {
111            id: ContainerId::new(),
112            name: name.into(),
113            image: image.into(),
114            machine_name: None,
115            state: ContainerState::Created,
116            created: Utc::now(),
117            started_at: None,
118            finished_at: None,
119            exit_code: None,
120            config: None,
121        }
122    }
123
124    /// Creates a new container with configuration.
125    #[must_use]
126    pub fn with_config(name: impl Into<String>, config: ContainerConfig) -> Self {
127        Self {
128            id: ContainerId::new(),
129            name: name.into(),
130            image: config.image.clone(),
131            machine_name: None,
132            state: ContainerState::Created,
133            created: Utc::now(),
134            started_at: None,
135            finished_at: None,
136            exit_code: None,
137            config: Some(config),
138        }
139    }
140
141    /// Creates a new container for a specific machine.
142    #[must_use]
143    pub fn new_for_machine(
144        name: impl Into<String>,
145        image: impl Into<String>,
146        machine: impl Into<String>,
147    ) -> Self {
148        Self {
149            id: ContainerId::new(),
150            name: name.into(),
151            image: image.into(),
152            machine_name: Some(machine.into()),
153            state: ContainerState::Created,
154            created: Utc::now(),
155            started_at: None,
156            finished_at: None,
157            exit_code: None,
158            config: None,
159        }
160    }
161
162    /// Creates a new container for a specific machine with configuration.
163    #[must_use]
164    pub fn with_config_for_machine(
165        name: impl Into<String>,
166        config: ContainerConfig,
167        machine: impl Into<String>,
168    ) -> Self {
169        Self {
170            id: ContainerId::new(),
171            name: name.into(),
172            image: config.image.clone(),
173            machine_name: Some(machine.into()),
174            state: ContainerState::Created,
175            created: Utc::now(),
176            started_at: None,
177            finished_at: None,
178            exit_code: None,
179            config: Some(config),
180        }
181    }
182
183    /// Returns whether the container is running.
184    #[must_use]
185    pub fn is_running(&self) -> bool {
186        self.state == ContainerState::Running
187    }
188}