use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::BTreeMap; use std::fs; use std::path::Path; /// Represents a single rule, capturing the URL, tag, and any other fields. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Rule { pub url: String, pub tag: String, // Capture any other fields in the rule object #[serde(flatten)] pub other_fields: BTreeMap, } /// Represents the "route" object in the config. #[derive(Serialize, Deserialize, Debug)] struct Route { #[serde(default, rename = "rule_set")] rule_set: Vec, } /// Represents the top-level structure of the configuration file. #[derive(Serialize, Deserialize, Debug)] pub struct Config { route: Route, // Capture any other top-level fields #[serde(flatten)] other_fields: BTreeMap, } impl Config { /// Loads and parses the config file from a given path. pub fn load(path: &Path) -> Result { let content = fs::read_to_string(path) .with_context(|| format!("Failed to read config file from '{}'", path.display()))?; let config: Config = serde_json::from_str(&content) .context("Failed to parse JSON. Please check the config file structure.")?; Ok(config) } /// Saves the current config state back to a file, prettified. pub fn save(&self, path: &Path) -> Result<()> { let new_content = serde_json::to_string_pretty(self) .context("Failed to serialize modified config to JSON.")?; fs::write(path, new_content)?; Ok(()) } /// Extracts all URLs from the rule_set. pub fn extract_urls(&self) -> Vec { self.route .rule_set .iter() .map(|rule| rule.url.clone()) .collect() } /// Rewrites the URLs in the config based on the provided domain and path. pub fn rewrite_urls(&mut self, domain: &str, rule_path: &str) -> Result<()> { let clean_domain = domain.trim_matches('/'); let clean_rule_path = format!("/{}/", rule_path.trim_matches('/')); for rule in &mut self.route.rule_set { if let Some(filename) = rule.url.split('/').last().filter(|s| !s.is_empty()) { let new_url = format!("https://{}{}{}", clean_domain, clean_rule_path, filename); println!(" '{}' -> {}", rule.tag, new_url); rule.url = new_url; } } Ok(()) } }