audb-cli 0.1.11

Command-line interface for AuDB database application framework
//! Deployment state management
//!
//! This module handles tracking deployment state in `.audb/deploy.toml`.

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;

use super::config::DeploymentTarget;

/// Deployment state stored in .audb/deploy.toml
///
/// This is minimal metadata only. For runtime status (running? PID? etc.),
/// query the actual service manager (Docker, systemd, launchd).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentState {
    /// Deployment target type
    pub target: DeploymentTarget,

    /// When the deployment was created
    pub deployed_at: chrono::DateTime<chrono::Utc>,

    /// Service label/name (e.g., "com.audb.myapp" for launchd, container name for Docker)
    pub service_label: String,

    /// Project name
    pub project_name: String,
}

impl DeploymentState {
    /// Load deployment state from .audb/deploy.toml
    pub fn load(project_root: &std::path::Path) -> Result<Self> {
        let path = Self::state_file_path(project_root);

        if !path.exists() {
            anyhow::bail!("No deployment state found. Has the project been deployed?");
        }

        let content = fs::read_to_string(&path)
            .with_context(|| format!("Failed to read deployment state from {}", path.display()))?;

        toml::from_str(&content).context("Failed to parse deployment state")
    }

    /// Save deployment state to .audb/deploy.toml
    pub fn save(&self, project_root: &std::path::Path) -> Result<()> {
        let path = Self::state_file_path(project_root);

        // Create .audb directory if it doesn't exist
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent).context("Failed to create .audb directory")?;
        }

        let content = toml::to_string_pretty(self).context("Failed to serialize state")?;

        fs::write(&path, content)
            .with_context(|| format!("Failed to write deployment state to {}", path.display()))?;

        Ok(())
    }

    /// Get the state file path
    fn state_file_path(project_root: &std::path::Path) -> PathBuf {
        project_root.join(".audb").join("deploy.toml")
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::PathBuf;

    #[test]
    fn test_state_file_path() {
        let project_root = PathBuf::from("/tmp/test-project");
        let path = DeploymentState::state_file_path(&project_root);
        assert!(path.ends_with(".audb/deploy.toml"));
    }
}