mihomo-rs 2.2.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::{MihomoClient, ProxyGroup, ProxyInfo, ProxyNode, Result};
use std::collections::HashMap;

pub struct ProxyManager {
    client: MihomoClient,
}

impl ProxyManager {
    pub fn new(client: MihomoClient) -> Self {
        Self { client }
    }

    pub async fn list_proxies(&self) -> Result<Vec<ProxyNode>> {
        let proxies = self.client.get_proxies().await?;
        let mut nodes = vec![];

        for (name, info) in proxies {
            let is_group = Self::is_group_type(&info.proxy_type);

            if !is_group {
                let delay = info.history.first().map(|h| h.delay);
                nodes.push(ProxyNode {
                    name,
                    proxy_type: info.proxy_type,
                    delay,
                    alive: delay.is_some(),
                });
            }
        }

        log::debug!("Filtered {} proxy nodes from all proxies", nodes.len());
        nodes.sort_by(|a, b| a.name.cmp(&b.name));
        Ok(nodes)
    }

    pub async fn list_groups(&self) -> Result<Vec<ProxyGroup>> {
        let proxies = self.client.get_proxies().await?;
        let mut groups = vec![];

        for (name, info) in proxies {
            let is_group = Self::is_group_type(&info.proxy_type);

            if is_group {
                groups.push(ProxyGroup {
                    name,
                    group_type: info.proxy_type,
                    now: info.now.unwrap_or_default(),
                    all: info.all.unwrap_or_default(),
                });
            }
        }

        groups.sort_by(|a, b| a.name.cmp(&b.name));
        Ok(groups)
    }

    pub async fn switch(&self, group: &str, proxy: &str) -> Result<()> {
        self.client.switch_proxy(group, proxy).await
    }

    pub async fn get_current(&self, group: &str) -> Result<String> {
        let info = self.client.get_proxy(group).await?;
        Ok(info.now.unwrap_or_default())
    }

    pub async fn get_all_proxies(&self) -> Result<HashMap<String, ProxyInfo>> {
        self.client.get_proxies().await
    }

    fn is_group_type(proxy_type: &str) -> bool {
        matches!(
            proxy_type,
            "Selector" | "URLTest" | "Fallback" | "LoadBalance" | "Relay"
        )
    }
}

#[cfg(test)]
mod tests {
    use super::ProxyManager;

    #[test]
    fn test_group_type_classification() {
        assert!(ProxyManager::is_group_type("Selector"));
        assert!(ProxyManager::is_group_type("URLTest"));
        assert!(!ProxyManager::is_group_type("Direct"));
        assert!(!ProxyManager::is_group_type("Reject"));
        assert!(!ProxyManager::is_group_type("Pass"));
    }
}