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 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 if self.default_provider.is_none() {
66 self.default_provider = Some(name);
67 }
68
69 Ok(())
70 }
71
72 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 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}