metasearch/src/config.rs

318 lines
9.0 KiB
Rust
Raw Normal View History

use std::{collections::HashMap, fs, net::SocketAddr, path::Path, sync::LazyLock};
2024-04-13 02:17:34 +00:00
use serde::Deserialize;
2024-04-16 00:19:09 +00:00
use tracing::info;
2024-04-13 02:17:34 +00:00
use crate::engines::Engine;
2024-06-29 05:21:55 +00:00
#[derive(Debug)]
2024-04-13 02:17:34 +00:00
pub struct Config {
pub bind: SocketAddr,
2024-06-29 06:06:27 +00:00
/// Whether the JSON API should be accessible.
pub api: bool,
2024-04-18 04:41:26 +00:00
pub ui: UiConfig,
pub image_search: ImageSearchConfig,
2024-04-18 04:41:26 +00:00
pub engines: EnginesConfig,
}
2024-06-29 05:21:55 +00:00
#[derive(Deserialize, Debug)]
pub struct PartialConfig {
pub bind: Option<SocketAddr>,
2024-06-29 06:06:27 +00:00
pub api: Option<bool>,
2024-06-29 05:21:55 +00:00
pub ui: Option<PartialUiConfig>,
pub image_search: Option<PartialImageSearchConfig>,
pub engines: Option<PartialEnginesConfig>,
}
impl Config {
pub fn overlay(&mut self, partial: PartialConfig) {
self.bind = partial.bind.unwrap_or(self.bind);
2024-06-29 06:06:27 +00:00
self.api = partial.api.unwrap_or(self.api);
2024-06-29 05:21:55 +00:00
self.ui.overlay(partial.ui.unwrap_or_default());
self.image_search
.overlay(partial.image_search.unwrap_or_default());
self.engines.overlay(partial.engines.unwrap_or_default());
}
}
#[derive(Debug)]
2024-04-18 04:41:26 +00:00
pub struct UiConfig {
2024-06-29 05:21:55 +00:00
pub show_engine_list_separator: bool,
pub show_version_info: bool,
}
#[derive(Deserialize, Debug, Default)]
pub struct PartialUiConfig {
#[serde(default)]
2024-04-18 04:41:26 +00:00
pub show_engine_list_separator: Option<bool>,
tweaks i am going insane 🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈🐈
2024-04-17 06:35:24 +00:00
#[serde(default)]
2024-04-18 04:41:26 +00:00
pub show_version_info: Option<bool>,
}
2024-06-29 05:21:55 +00:00
impl UiConfig {
pub fn overlay(&mut self, partial: PartialUiConfig) {
self.show_engine_list_separator = partial
.show_engine_list_separator
.unwrap_or(self.show_engine_list_separator);
self.show_version_info = partial.show_version_info.unwrap_or(self.show_version_info);
}
}
#[derive(Debug)]
pub struct ImageSearchConfig {
2024-06-29 05:21:55 +00:00
pub enabled: bool,
pub show_engines: bool,
pub proxy: ImageProxyConfig,
}
#[derive(Deserialize, Debug, Default)]
pub struct PartialImageSearchConfig {
pub enabled: Option<bool>,
pub show_engines: Option<bool>,
#[serde(default)]
2024-06-29 05:21:55 +00:00
pub proxy: PartialImageProxyConfig,
}
2024-06-29 05:21:55 +00:00
impl ImageSearchConfig {
pub fn overlay(&mut self, partial: PartialImageSearchConfig) {
self.enabled = partial.enabled.unwrap_or(self.enabled);
self.show_engines = partial.show_engines.unwrap_or(self.show_engines);
self.proxy.overlay(partial.proxy);
}
}
#[derive(Debug)]
pub struct ImageProxyConfig {
2024-06-29 06:06:27 +00:00
/// Whether we should proxy remote images through our server. This is mostly
/// a privacy feature.
2024-06-29 05:21:55 +00:00
pub enabled: bool,
2024-06-29 06:06:27 +00:00
/// The maximum size of an image that can be proxied. This is in bytes.
2024-06-29 05:21:55 +00:00
pub max_download_size: u64,
}
#[derive(Deserialize, Debug, Default)]
pub struct PartialImageProxyConfig {
pub enabled: Option<bool>,
pub max_download_size: Option<u64>,
}
2024-06-29 05:21:55 +00:00
impl ImageProxyConfig {
pub fn overlay(&mut self, partial: PartialImageProxyConfig) {
self.enabled = partial.enabled.unwrap_or(self.enabled);
self.max_download_size = partial.max_download_size.unwrap_or(self.max_download_size);
}
}
#[derive(Debug)]
2024-04-18 04:41:26 +00:00
pub struct EnginesConfig {
2024-06-29 05:21:55 +00:00
pub map: HashMap<Engine, EngineConfig>,
}
#[derive(Deserialize, Debug, Default)]
pub struct PartialEnginesConfig {
2024-04-18 04:41:26 +00:00
#[serde(flatten)]
2024-06-29 05:21:55 +00:00
pub map: HashMap<Engine, PartialDefaultableEngineConfig>,
2024-04-13 02:17:34 +00:00
}
#[derive(Deserialize, Clone, Debug)]
#[serde(untagged)]
2024-06-29 05:21:55 +00:00
pub enum PartialDefaultableEngineConfig {
Boolean(bool),
2024-06-29 05:21:55 +00:00
Full(PartialEngineConfig),
}
2024-06-29 05:21:55 +00:00
impl EnginesConfig {
pub fn overlay(&mut self, partial: PartialEnginesConfig) {
for (key, value) in partial.map {
let full = match value {
PartialDefaultableEngineConfig::Boolean(enabled) => PartialEngineConfig {
enabled: Some(enabled),
..Default::default()
},
PartialDefaultableEngineConfig::Full(full) => full,
};
if let Some(existing) = self.map.get_mut(&key) {
existing.overlay(full);
} else {
let mut new = EngineConfig::default();
new.overlay(full);
self.map.insert(key, new);
}
}
}
pub fn get(&self, engine: Engine) -> &EngineConfig {
self.map.get(&engine).unwrap_or(&DEFAULT_ENGINE_CONFIG_REF)
}
}
#[derive(Debug)]
pub struct EngineConfig {
pub enabled: bool,
2024-06-29 06:06:27 +00:00
/// The priority of this engine relative to the other engines.
2024-06-29 05:21:55 +00:00
pub weight: f64,
2024-06-29 06:06:27 +00:00
/// Per-engine configs. These are parsed at request time.
2024-06-29 05:21:55 +00:00
pub extra: toml::Table,
}
2024-06-29 05:21:55 +00:00
#[derive(Deserialize, Clone, Debug, Default)]
pub struct PartialEngineConfig {
#[serde(default)]
2024-06-29 05:21:55 +00:00
pub enabled: Option<bool>,
#[serde(default)]
pub weight: Option<f64>,
#[serde(flatten)]
pub extra: toml::Table,
}
2024-06-29 05:21:55 +00:00
impl EngineConfig {
pub fn overlay(&mut self, partial: PartialEngineConfig) {
self.enabled = partial.enabled.unwrap_or(self.enabled);
self.weight = partial.weight.unwrap_or(self.weight);
self.extra.extend(partial.extra);
}
}
2024-04-13 02:17:34 +00:00
impl Config {
2024-04-18 04:41:26 +00:00
pub fn read_or_create(config_path: &Path) -> eyre::Result<Self> {
2024-06-29 05:21:55 +00:00
let mut config = Config::default();
2024-04-13 02:17:34 +00:00
2024-04-18 04:41:26 +00:00
if !config_path.exists() {
2024-04-16 00:19:09 +00:00
info!("No config found, creating one at {config_path:?}");
2024-04-18 04:41:26 +00:00
let default_config_str = include_str!("../config-default.toml");
2024-04-13 02:17:34 +00:00
fs::write(config_path, default_config_str)?;
}
2024-04-18 04:41:26 +00:00
2024-06-29 05:21:55 +00:00
let given_config = toml::from_str::<PartialConfig>(&fs::read_to_string(config_path)?)?;
config.overlay(given_config);
2024-04-18 04:41:26 +00:00
Ok(config)
2024-04-13 02:17:34 +00:00
}
}
2024-06-29 05:21:55 +00:00
//
// DEFAULTS
//
2024-06-29 05:21:55 +00:00
impl Default for Config {
fn default() -> Self {
Config {
bind: "0.0.0.0:28019".parse().unwrap(),
2024-06-29 06:06:27 +00:00
api: false,
2024-06-29 05:21:55 +00:00
ui: UiConfig {
show_engine_list_separator: false,
show_version_info: false,
},
image_search: ImageSearchConfig {
enabled: false,
show_engines: true,
proxy: ImageProxyConfig {
enabled: true,
max_download_size: 10_000_000,
},
},
engines: EnginesConfig::default(),
}
}
}
2024-06-29 05:21:55 +00:00
impl Default for EngineConfig {
fn default() -> Self {
Self {
enabled: true,
weight: 1.0,
extra: Default::default(),
}
2024-04-13 02:17:34 +00:00
}
}
static DEFAULT_ENGINE_CONFIG_REF: LazyLock<EngineConfig> = LazyLock::new(EngineConfig::default);
2024-06-29 05:21:55 +00:00
impl EngineConfig {
pub fn new() -> Self {
Self::default()
2024-04-13 02:17:34 +00:00
}
2024-06-29 05:21:55 +00:00
pub fn with_weight(self, weight: f64) -> Self {
Self { weight, ..self }
}
pub fn disabled(self) -> Self {
Self {
enabled: false,
..self
}
}
2024-06-29 05:21:55 +00:00
pub fn with_extra(self, extra: toml::Table) -> Self {
Self { extra, ..self }
2024-04-13 02:17:34 +00:00
}
}
2024-06-29 05:21:55 +00:00
impl Default for EnginesConfig {
2024-04-13 02:17:34 +00:00
fn default() -> Self {
2024-06-29 05:21:55 +00:00
use toml::value::Value;
2024-04-13 02:17:34 +00:00
2024-06-29 05:21:55 +00:00
let mut map = HashMap::new();
// engines are enabled by default, so engines that aren't listed here are
// enabled
2024-04-13 02:17:34 +00:00
2024-06-29 05:21:55 +00:00
// main search engines
map.insert(Engine::Google, EngineConfig::new().with_weight(1.05));
map.insert(Engine::Bing, EngineConfig::new().with_weight(1.0));
map.insert(Engine::Brave, EngineConfig::new().with_weight(1.25));
map.insert(
Engine::Marginalia,
EngineConfig::new().with_weight(0.15).with_extra(
vec![(
"args".to_string(),
Value::Table(
vec![
("profile".to_string(), Value::String("corpo".to_string())),
("js".to_string(), Value::String("default".to_string())),
("adtech".to_string(), Value::String("default".to_string())),
]
.into_iter()
.collect(),
),
)]
.into_iter()
.collect(),
),
);
2024-04-13 02:17:34 +00:00
2024-06-29 05:21:55 +00:00
// additional search engines
map.insert(
Engine::GoogleScholar,
EngineConfig::new().with_weight(0.50).disabled(),
);
map.insert(
Engine::RightDao,
EngineConfig::new().with_weight(0.10).disabled(),
);
map.insert(
Engine::Stract,
EngineConfig::new().with_weight(0.15).disabled(),
);
map.insert(
Engine::Yep,
EngineConfig::new().with_weight(0.10).disabled(),
);
2024-04-13 02:17:34 +00:00
2024-06-29 05:21:55 +00:00
// calculators (give them a high weight so they're always the first thing in
// autocomplete)
map.insert(Engine::Numbat, EngineConfig::new().with_weight(10.0));
map.insert(
Engine::Fend,
EngineConfig::new().with_weight(10.0).disabled(),
);
// other engines
map.insert(
Engine::Mdn,
EngineConfig::new().with_extra(
vec![("max_sections".to_string(), Value::Integer(1))]
.into_iter()
.collect(),
),
);
Self { map }
2024-04-13 02:17:34 +00:00
}
}