opencode_cloud_core/docker/
state.rs1use chrono::Utc;
7use serde::{Deserialize, Serialize};
8use std::path::PathBuf;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct ImageState {
13 pub version: String,
15 pub source: String,
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub registry: Option<String>,
20 pub acquired_at: String,
22}
23
24impl ImageState {
25 pub fn prebuilt(version: &str, registry: &str) -> Self {
27 Self {
28 version: version.to_string(),
29 source: "prebuilt".to_string(),
30 registry: Some(registry.to_string()),
31 acquired_at: Utc::now().to_rfc3339(),
32 }
33 }
34
35 pub fn built(version: &str) -> Self {
37 Self {
38 version: version.to_string(),
39 source: "build".to_string(),
40 registry: None,
41 acquired_at: Utc::now().to_rfc3339(),
42 }
43 }
44}
45
46pub fn get_state_path() -> Option<PathBuf> {
48 crate::config::paths::get_data_dir().map(|p| p.join("image-state.json"))
49}
50
51pub fn save_state(state: &ImageState) -> anyhow::Result<()> {
53 let path = get_state_path().ok_or_else(|| anyhow::anyhow!("Could not determine state path"))?;
54
55 if let Some(parent) = path.parent() {
57 std::fs::create_dir_all(parent)?;
58 }
59
60 let json = serde_json::to_string_pretty(state)?;
61 std::fs::write(&path, json)?;
62 Ok(())
63}
64
65pub fn load_state() -> Option<ImageState> {
67 let path = get_state_path()?;
68 let content = std::fs::read_to_string(&path).ok()?;
69 serde_json::from_str(&content).ok()
70}
71
72pub fn clear_state() -> anyhow::Result<()> {
74 if let Some(path) = get_state_path() {
75 if path.exists() {
76 std::fs::remove_file(&path)?;
77 }
78 }
79 Ok(())
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn test_image_state_prebuilt() {
88 let state = ImageState::prebuilt("1.0.12", "ghcr.io");
89 assert_eq!(state.version, "1.0.12");
90 assert_eq!(state.source, "prebuilt");
91 assert_eq!(state.registry, Some("ghcr.io".to_string()));
92 assert!(!state.acquired_at.is_empty());
93 }
94
95 #[test]
96 fn test_image_state_built() {
97 let state = ImageState::built("1.0.12");
98 assert_eq!(state.version, "1.0.12");
99 assert_eq!(state.source, "build");
100 assert!(state.registry.is_none());
101 }
102
103 #[test]
104 fn test_image_state_serialize_deserialize() {
105 let state = ImageState::prebuilt("1.0.12", "docker.io");
106 let json = serde_json::to_string(&state).unwrap();
107 let parsed: ImageState = serde_json::from_str(&json).unwrap();
108 assert_eq!(state.version, parsed.version);
109 assert_eq!(state.source, parsed.source);
110 assert_eq!(state.registry, parsed.registry);
111 }
112
113 #[test]
114 fn test_get_state_path() {
115 let path = get_state_path();
116 assert!(path.is_some());
117 let p = path.unwrap();
118 assert!(p.to_string_lossy().contains("image-state.json"));
119 }
120}