gravel_core/
config.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//! For an explanation of the config, see `config.yml` in the crate's root.

use abi_stable::{std_types::RString, traits::IntoReprC};
use gravel_ffi::config::__private_api::{create_figment, ConfigLayer};
use gravel_ffi::{FrontendMessage, PluginConfigAdapter};
use nameof::name_of;
use serde::Deserialize;

pub const DEFAULT_CONFIG: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../config.yml"));

pub struct ConfigManager {
	root: Config,
	config_sources: Vec<ConfigLayer>,
}

impl ConfigManager {
	pub fn new(config_sources: Vec<ConfigLayer>) -> Self {
		let root = match create_figment(&config_sources).extract() {
			Ok(root) => root,
			Err(err) => {
				log::error!("error in config: {err}");
				std::process::exit(1);
			}
		};

		Self { config_sources, root }
	}

	pub fn root(&self) -> &Config {
		&self.root
	}

	pub fn adapt_provider(&self, index: usize) -> PluginConfigAdapter<'_> {
		self.adapt(format!("{}.{index}", name_of!(providers in Config)))
	}

	pub fn adapt_frontend(&self) -> PluginConfigAdapter<'_> {
		self.adapt(name_of!(frontend in Config))
	}

	fn adapt(&self, key: impl Into<RString>) -> PluginConfigAdapter<'_> {
		PluginConfigAdapter::from(key.into(), self.config_sources.as_slice().into_c())
	}
}

#[derive(Debug, Deserialize)]
pub struct Config {
	pub single_instance: Option<String>,
	pub external_plugins: ExternalPluginPolicy,
	pub hotkeys: Vec<Hotkey>,
	pub frontend: Frontend,
	pub providers: Vec<Provider>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ExternalPluginPolicy {
	Disabled,
	All,
	Whitelist(Vec<String>),
}

#[derive(Debug, Deserialize)]
pub struct Hotkey {
	pub binding: String,
	pub action: HotkeyAction,
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum HotkeyAction {
	ShowHide,
	Show,
	Hide,
	ShowWith(String),
}

impl From<&HotkeyAction> for FrontendMessage {
	fn from(value: &HotkeyAction) -> Self {
		match value {
			HotkeyAction::ShowHide => Self::ShowOrHide,
			HotkeyAction::Show => Self::Show,
			HotkeyAction::Hide => Self::Hide,
			HotkeyAction::ShowWith(query) => Self::ShowWithQuery(query.clone().into_c()),
		}
	}
}

#[derive(Debug, Deserialize)]
pub struct Frontend {
	pub plugin: String,
}

#[derive(Debug, Deserialize)]
pub struct Provider {
	pub plugin: String,
	pub keyword: Option<String>,
	// Technically expected here but is deserialized differently, see [`gravel_ffi::PluginConfigAdapter`]
	//pub config: Any,
}