use crate::utils::{config, dirs}; use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_yaml::{Mapping, Value}; #[derive(Default, Debug, Clone)] pub struct IClashTemp(pub Mapping); impl IClashTemp { pub fn new() -> Self { Self(config::read_merge_mapping(dirs::clash_path())) } pub fn patch_config(&mut self, patch: Mapping) { for (key, value) in patch.into_iter() { self.0.insert(key, value); } } pub fn save_config(&self) -> Result<()> { config::save_yaml( dirs::clash_path(), &self.0, Some("# Default Config For ClashN Core\n\n"), ) } pub fn get_info(&self) -> Result { Ok(ClashInfoN::from(&self.0)) } } #[derive(Default, Debug, Clone, Deserialize, Serialize)] pub struct ClashInfoN { /// clash sidecar status pub status: String, /// clash core port pub port: Option, /// same as `external-controller` pub server: Option, /// clash secret pub secret: Option, } impl ClashInfoN { /// parse the clash's config.yaml /// get some information pub fn from(config: &Mapping) -> ClashInfoN { let key_port_1 = Value::from("mixed-port"); let key_port_2 = Value::from("port"); let key_server = Value::from("external-controller"); let key_secret = Value::from("secret"); let mut status: u32 = 0; let port = match config.get(&key_port_1) { Some(value) => match value { Value::String(val_str) => Some(val_str.clone()), Value::Number(val_num) => Some(val_num.to_string()), _ => { status |= 0b1; None } }, _ => { status |= 0b10; None } }; let port = match port { Some(_) => port, None => match config.get(&key_port_2) { Some(value) => match value { Value::String(val_str) => Some(val_str.clone()), Value::Number(val_num) => Some(val_num.to_string()), _ => { status |= 0b100; None } }, _ => { status |= 0b1000; None } }, }; // `external-controller` could be // "127.0.0.1:9090" or ":9090" let server = match config.get(&key_server) { Some(value) => match value.as_str() { Some(val_str) => { if val_str.starts_with(":") { Some(format!("127.0.0.1{val_str}")) } else if val_str.starts_with("0.0.0.0:") { Some(format!("127.0.0.1:{}", &val_str[8..])) } else if val_str.starts_with("[::]:") { Some(format!("127.0.0.1:{}", &val_str[5..])) } else { Some(val_str.into()) } } None => { status |= 0b10000; None } }, None => { status |= 0b100000; None } }; let secret = match config.get(&key_secret) { Some(value) => match value { Value::String(val_str) => Some(val_str.clone()), Value::Bool(val_bool) => Some(val_bool.to_string()), Value::Number(val_num) => Some(val_num.to_string()), _ => None, }, _ => None, }; ClashInfoN { status: format!("{status}"), port, server, secret, } } } #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub struct IClash { pub mixed_port: Option, pub allow_lan: Option, pub log_level: Option, pub ipv6: Option, pub mode: Option, pub external_controller: Option, pub secret: Option, pub dns: Option, pub tun: Option, pub interface_name: Option, } #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub struct IClashTUN { pub enable: Option, pub stack: Option, pub auto_route: Option, pub auto_detect_interface: Option, pub dns_hijack: Option>, } #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub struct IClashDNS { pub enable: Option, pub listen: Option, pub default_nameserver: Option>, pub enhanced_mode: Option, pub fake_ip_range: Option, pub use_hosts: Option, pub fake_ip_filter: Option>, pub nameserver: Option>, pub fallback: Option>, pub fallback_filter: Option, pub nameserver_policy: Option>, } #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub struct IClashFallbackFilter { pub geoip: Option, pub geoip_code: Option, pub ipcidr: Option>, pub domain: Option>, }