1use abi_stable::StableAbi;
2use abi_stable::std_types::{RSlice, RString};
3use figment::providers::{Format, Yaml};
4use serde::Deserialize;
5
6#[repr(C)]
9#[derive(StableAbi)]
10pub struct PluginConfigAdapter<'a> {
11 key: RString,
12 config_sources: RSlice<'a, __private_api::ConfigLayer>,
13}
14
15impl<'a> PluginConfigAdapter<'a> {
16 #[doc(hidden)]
18 pub fn from(key: RString, config_sources: RSlice<'a, __private_api::ConfigLayer>) -> Self {
19 Self { key, config_sources }
20 }
21
22 #[must_use]
24 pub fn get<'de, T: Deserialize<'de>>(&self, default_config: &str) -> T {
25 log::trace!("reading plugin config for {}", self.key);
26
27 let figment = __private_api::create_figment(&self.config_sources)
29 .focus(&format!("{}.config", self.key))
30 .join(Yaml::string(default_config));
31
32 match figment.extract() {
33 Ok(config) => config,
34 Err(err) => {
35 log::error!("error in plugin config {}: {err}", self.key);
36 std::process::exit(1);
37 }
38 }
39 }
40}
41
42#[doc(hidden)]
44pub mod __private_api {
45 use abi_stable::StableAbi;
46 use abi_stable::std_types::{RStr, RString};
47 use figment::Figment;
48 use figment::providers::{Format, Yaml};
49
50 pub fn create_figment(config_sources: &[ConfigLayer]) -> Figment {
53 fn fold(figment: Figment, ConfigLayer(source, strategy): &ConfigLayer) -> Figment {
54 let source = match source {
55 ConfigSource::String(s) => Yaml::string(s.as_str()),
56 ConfigSource::File(f) => Yaml::file(f.as_str()),
57 };
58
59 match strategy {
60 MergeStrategy::Merge => figment.merge(source),
61 MergeStrategy::AdMerge => figment.admerge(source),
62 }
63 }
64
65 config_sources.iter().fold(Figment::new(), fold)
66 }
67
68 #[repr(C)]
69 #[derive(StableAbi, Debug)]
70 pub struct ConfigLayer(pub ConfigSource, pub MergeStrategy);
71
72 #[repr(u8)]
73 #[derive(StableAbi, Debug)]
74 pub enum MergeStrategy {
75 Merge,
76 AdMerge,
77 }
78
79 #[repr(u8)]
80 #[derive(StableAbi, Debug)]
81 pub enum ConfigSource {
82 String(RStr<'static>),
83 File(RString),
84 }
85}