lc/search/
config.rs

1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::fs;
5use std::path::PathBuf;
6
7use super::providers::{SearchProviderConfig, SearchProviderType};
8
9#[derive(Debug, Serialize, Deserialize, Clone)]
10pub struct SearchConfig {
11    pub providers: HashMap<String, SearchProviderConfig>,
12    pub default_provider: Option<String>,
13}
14
15impl SearchConfig {
16    pub fn new() -> Self {
17        Self {
18            providers: HashMap::new(),
19            default_provider: None,
20        }
21    }
22
23    pub fn load() -> Result<Self> {
24        let config_path = Self::config_file_path()?;
25        
26        if config_path.exists() {
27            let content = fs::read_to_string(&config_path)?;
28            let config: SearchConfig = toml::from_str(&content)?;
29            Ok(config)
30        } else {
31            let config = Self::new();
32            config.save()?;
33            Ok(config)
34        }
35    }
36
37    pub fn save(&self) -> Result<()> {
38        let config_path = Self::config_file_path()?;
39
40        // Ensure parent directory exists
41        if let Some(parent) = config_path.parent() {
42            fs::create_dir_all(parent)?;
43        }
44
45        let content = toml::to_string_pretty(self)?;
46        fs::write(&config_path, content)?;
47        Ok(())
48    }
49
50    pub fn add_provider(
51        &mut self,
52        name: String,
53        url: String,
54        provider_type: SearchProviderType,
55    ) -> Result<()> {
56        let provider_config = SearchProviderConfig {
57            url,
58            provider_type,
59            headers: HashMap::new(),
60        };
61
62        self.providers.insert(name.clone(), provider_config);
63
64        // Set as default if it's the first provider
65        if self.default_provider.is_none() {
66            self.default_provider = Some(name);
67        }
68
69        Ok(())
70    }
71
72    /// Add provider with auto-detected type from URL
73    pub fn add_provider_auto(&mut self, name: String, url: String) -> Result<()> {
74        let provider_type = SearchProviderType::detect_from_url(&url)?;
75        self.add_provider(name, url, provider_type)
76    }
77
78    pub fn delete_provider(&mut self, name: &str) -> Result<()> {
79        if self.providers.remove(name).is_none() {
80            anyhow::bail!("Search provider '{}' not found", name);
81        }
82
83        // Clear default if it was the deleted provider
84        if self.default_provider.as_ref() == Some(&name.to_string()) {
85            self.default_provider = None;
86        }
87
88        Ok(())
89    }
90
91    pub fn set_header(
92        &mut self,
93        provider: &str,
94        header_name: String,
95        header_value: String,
96    ) -> Result<()> {
97        if let Some(provider_config) = self.providers.get_mut(provider) {
98            provider_config.headers.insert(header_name, header_value);
99            Ok(())
100        } else {
101            anyhow::bail!("Search provider '{}' not found", provider);
102        }
103    }
104
105    pub fn get_provider(&self, name: &str) -> Result<&SearchProviderConfig> {
106        self.providers
107            .get(name)
108            .ok_or_else(|| anyhow::anyhow!("Search provider '{}' not found", name))
109    }
110
111    pub fn has_provider(&self, name: &str) -> bool {
112        self.providers.contains_key(name)
113    }
114
115    pub fn list_providers(&self) -> &HashMap<String, SearchProviderConfig> {
116        &self.providers
117    }
118
119    pub fn set_default_provider(&mut self, name: String) -> Result<()> {
120        if name.is_empty() {
121            self.default_provider = None;
122        } else if self.has_provider(&name) {
123            self.default_provider = Some(name);
124        } else {
125            anyhow::bail!("Search provider '{}' not found", name);
126        }
127        Ok(())
128    }
129
130    pub fn get_default_provider(&self) -> Option<&String> {
131        self.default_provider.as_ref()
132    }
133
134    fn config_file_path() -> Result<PathBuf> {
135        let config_dir = crate::config::Config::config_dir()?;
136        Ok(config_dir.join("search_config.toml"))
137    }
138}