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 Ok(Self::new())
33 }
34 }
35
36 pub fn save(&self) -> Result<()> {
37 let config_path = Self::config_file_path()?;
38
39 if let Some(parent) = config_path.parent() {
41 if !parent.exists() {
43 fs::create_dir_all(parent)?;
44 }
45 }
46
47 let content = toml::to_string_pretty(self)?;
48 fs::write(&config_path, content)?;
49 Ok(())
50 }
51
52 pub fn add_provider(
53 &mut self,
54 name: String,
55 url: String,
56 provider_type: SearchProviderType,
57 ) -> Result<()> {
58 let provider_config = SearchProviderConfig {
59 url,
60 provider_type,
61 headers: HashMap::new(),
62 };
63
64 self.providers.insert(name.clone(), provider_config);
65
66 if self.default_provider.is_none() {
68 self.default_provider = Some(name);
69 }
70
71 Ok(())
72 }
73
74 pub fn add_provider_auto(&mut self, name: String, url: String) -> Result<()> {
76 let provider_type = SearchProviderType::detect_from_url(&url)?;
77 self.add_provider(name, url, provider_type)
78 }
79
80 pub fn delete_provider(&mut self, name: &str) -> Result<()> {
81 if self.providers.remove(name).is_none() {
82 anyhow::bail!("Search provider '{}' not found", name);
83 }
84
85 if self.default_provider.as_ref() == Some(&name.to_string()) {
87 self.default_provider = None;
88 }
89
90 Ok(())
91 }
92
93 pub fn set_header(
94 &mut self,
95 provider: &str,
96 header_name: String,
97 header_value: String,
98 ) -> Result<()> {
99 if let Some(provider_config) = self.providers.get_mut(provider) {
100 provider_config.headers.insert(header_name, header_value);
101 Ok(())
102 } else {
103 anyhow::bail!("Search provider '{}' not found", provider);
104 }
105 }
106
107 pub fn get_provider(&self, name: &str) -> Result<&SearchProviderConfig> {
108 self.providers
109 .get(name)
110 .ok_or_else(|| anyhow::anyhow!("Search provider '{}' not found", name))
111 }
112
113 pub fn has_provider(&self, name: &str) -> bool {
114 self.providers.contains_key(name)
115 }
116
117 pub fn list_providers(&self) -> &HashMap<String, SearchProviderConfig> {
118 &self.providers
119 }
120
121 pub fn set_default_provider(&mut self, name: String) -> Result<()> {
122 if name.is_empty() {
123 self.default_provider = None;
124 } else if self.has_provider(&name) {
125 self.default_provider = Some(name);
126 } else {
127 anyhow::bail!("Search provider '{}' not found", name);
128 }
129 Ok(())
130 }
131
132 pub fn get_default_provider(&self) -> Option<&String> {
133 self.default_provider.as_ref()
134 }
135
136 fn config_file_path() -> Result<PathBuf> {
137 let config_dir = crate::config::Config::config_dir()?;
138 Ok(config_dir.join("search_config.toml"))
139 }
140}