1use color_eyre::Result;
2use serde::{Deserialize, Serialize};
3use std::fs;
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Config {
8 pub version: String,
9 pub providers: Vec<SearchProvider>,
10}
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct SearchProvider {
14 pub name: String,
15 pub aliases: Vec<String>,
16 pub url: String,
17 pub browser: Option<String>,
18}
19
20impl Config {
21 pub fn load() -> Result<Option<Self>> {
23 let config_path = Self::default_config_path();
24
25 if !config_path.exists() {
26 return Ok(None);
27 }
28
29 let content = fs::read_to_string(&config_path)?;
30 let config: Config = serde_yaml::from_str(&content)?;
31 Ok(Some(config))
32 }
33
34 fn default_config_path() -> PathBuf {
36 dirs::home_dir()
37 .unwrap_or_else(|| PathBuf::from("."))
38 .join(".config")
39 .join("search")
40 .join("config.yaml")
41 }
42}
43
44impl SearchProvider {
45 pub fn matches_alias(&self, query: &str) -> bool {
47 self.aliases.iter().any(|alias| query.starts_with(alias))
48 }
49
50 pub fn get_matching_alias(&self, query: &str) -> Option<&str> {
52 let first_word = query.split_whitespace().next()?;
54
55 let mut best_match = "";
57 for alias in &self.aliases {
58 if first_word == alias && alias.len() > best_match.len() {
59 best_match = alias;
60 }
61 }
62
63 if best_match.is_empty() {
64 None
65 } else {
66 Some(best_match)
67 }
68 }
69}