1use std::collections::HashMap;
7use std::path::PathBuf;
8
9use anyhow::{Context, Result};
10use chrono::{DateTime, Utc};
11use serde::{Deserialize, Serialize};
12
13use super::backend::DeployedService;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct DeploymentState {
18 pub agent_name: String,
19 pub project_name: String,
20 pub target: String,
21 pub project_id: String,
22 pub environment_id: String,
23 pub services: HashMap<String, DeployedService>,
24 pub deployed_at: DateTime<Utc>,
25 pub template_name: String,
26}
27
28impl DeploymentState {
29 fn state_dir() -> Result<PathBuf> {
31 let home = dirs::home_dir().context("cannot determine home directory")?;
32 let dir = home.join(".life").join("deployments");
33 std::fs::create_dir_all(&dir)
34 .with_context(|| format!("failed to create {}", dir.display()))?;
35 Ok(dir)
36 }
37
38 fn state_path(agent_name: &str) -> Result<PathBuf> {
40 Ok(Self::state_dir()?.join(format!("{agent_name}.json")))
41 }
42
43 pub fn save(&self) -> Result<()> {
45 let path = Self::state_path(&self.agent_name)?;
46 let json = serde_json::to_string_pretty(self)?;
47 std::fs::write(&path, json)
48 .with_context(|| format!("failed to write {}", path.display()))?;
49 Ok(())
50 }
51
52 pub fn load(agent_name: &str) -> Result<Self> {
54 let path = Self::state_path(agent_name)?;
55 let json = std::fs::read_to_string(&path)
56 .with_context(|| format!("no deployment state found at {}", path.display()))?;
57 serde_json::from_str(&json).context("failed to parse deployment state")
58 }
59
60 pub fn remove(&self) -> Result<()> {
62 let path = Self::state_path(&self.agent_name)?;
63 if path.exists() {
64 std::fs::remove_file(&path)?;
65 }
66 Ok(())
67 }
68
69 #[allow(dead_code)]
71 pub fn list_all() -> Result<Vec<Self>> {
72 let dir = Self::state_dir()?;
73 let mut states = Vec::new();
74
75 if let Ok(entries) = std::fs::read_dir(dir) {
76 for entry in entries.flatten() {
77 let path = entry.path();
78 if path.extension().is_some_and(|e| e == "json") {
79 if let Ok(json) = std::fs::read_to_string(&path) {
80 if let Ok(state) = serde_json::from_str::<DeploymentState>(&json) {
81 states.push(state);
82 }
83 }
84 }
85 }
86 }
87
88 Ok(states)
89 }
90}