mihomo-rs 2.0.0

A Rust SDK and CLI tool for mihomo proxy management with service lifecycle management, configuration handling, and real-time monitoring
Documentation
use crate::core::Result;
use std::path::PathBuf;

#[derive(Debug, Clone)]
pub struct Profile {
    pub name: String,
    pub path: PathBuf,
    pub active: bool,
}

impl Profile {
    pub fn new(name: String, path: PathBuf, active: bool) -> Self {
        Self { name, path, active }
    }

    pub async fn validate(&self) -> Result<()> {
        if !self.path.exists() {
            return Err(crate::core::MihomoError::config(format!(
                "Profile file does not exist: {}",
                self.path.display()
            )));
        }

        let content = tokio::fs::read_to_string(&self.path).await?;
        serde_yaml::from_str::<serde_yaml::Value>(&content)?;

        Ok(())
    }

    pub async fn backup(&self) -> Result<PathBuf> {
        let backup_path = self.path.with_extension("yaml.bak");
        tokio::fs::copy(&self.path, &backup_path).await?;
        Ok(backup_path)
    }
}

#[cfg(test)]
mod tests {
    use super::Profile;
    use tempfile::tempdir;
    use tokio::fs;

    #[tokio::test]
    async fn validate_and_backup_success() {
        let temp = tempdir().expect("create temp dir");
        let path = temp.path().join("profile.yaml");
        fs::write(
            &path,
            "port: 7890\nsocks-port: 7891\nexternal-controller: 127.0.0.1:9090\n",
        )
        .await
        .expect("write config");

        let profile = Profile::new("default".to_string(), path.clone(), true);
        profile.validate().await.expect("validate profile");

        let backup = profile.backup().await.expect("backup profile");
        assert!(backup.exists());

        let content = fs::read_to_string(backup).await.expect("read backup");
        assert!(content.contains("external-controller"));
    }

    #[tokio::test]
    async fn validate_errors_for_missing_and_invalid_yaml() {
        let temp = tempdir().expect("create temp dir");
        let missing = temp.path().join("missing.yaml");
        let missing_profile = Profile::new("missing".to_string(), missing, false);
        assert!(missing_profile.validate().await.is_err());

        let invalid = temp.path().join("invalid.yaml");
        fs::write(&invalid, "invalid: yaml: [")
            .await
            .expect("write invalid yaml");
        let invalid_profile = Profile::new("invalid".to_string(), invalid, false);
        assert!(invalid_profile.validate().await.is_err());
    }
}