1use figment::Figment;
12use figment::providers::{Format, Json, Serialized};
13use pumpkin_plugin_api::Context;
14use serde::de::DeserializeOwned;
15use serde::{Deserialize, Serialize};
16use serde_json::Value;
17use std::cell::RefCell;
18use std::collections::HashMap;
19use std::fs;
20use std::path::PathBuf;
21use tracing::error;
22
23thread_local! {
24 static CONFIG: RefCell<Option<ConfigManager>> = const { RefCell::new(None) };
25}
26
27fn config_key<T>() -> String {
37 use std::any::type_name;
38
39 let full_name = type_name::<T>();
40 let parts: Vec<&str> = full_name.split("::").collect();
41
42 if parts.len() >= 2 {
43 parts[parts.len() - 2].to_string()
44 } else if let Some(&last) = parts.last() {
45 last.strip_suffix("Config")
46 .map_or_else(|| last.to_string(), |s| s.to_string())
47 } else {
48 full_name.to_string()
49 }
50}
51
52#[derive(Clone, Debug, Default, Serialize, Deserialize)]
56pub struct ConfigManager {
57 #[serde(flatten)]
58 configs: HashMap<String, Value>,
59}
60
61impl ConfigManager {
62 pub fn empty() -> Self {
64 Self::default()
65 }
66
67 pub fn get() -> Option<Self> {
69 CONFIG.with(|c| c.borrow().clone())
70 }
71
72 pub fn get_config<T: DeserializeOwned + Default + 'static>(&self) -> T {
75 let key = config_key::<T>();
76 self.configs
77 .get(&key)
78 .and_then(|v| {
79 serde_json::from_value(v.clone())
80 .inspect_err(|e| error!("Failed to parse config for key '{}': {}", key, e))
81 .ok()
82 })
83 .unwrap_or_default()
84 }
85
86 pub fn register<T: Serialize + Default + 'static>(&mut self) {
89 let key = config_key::<T>();
90 let config = T::default();
91 match serde_json::to_value(config) {
92 Ok(value) => {
93 self.configs.insert(key, value);
94 }
95 Err(e) => error!("Failed to serialize config for key: {}", e),
96 }
97 }
98
99 pub fn finalize(&mut self, context: &Context) {
102 let path =
103 PathBuf::from(context.get_data_folder().trim_start_matches("./")).join("config.json");
104
105 let mut figment = Figment::new();
106
107 for (key, value) in &self.configs {
108 figment = figment.merge(Serialized::default(key, value));
109 }
110
111 if path.exists() {
112 figment = figment.merge(Json::file(&path));
113 }
114
115 self.configs = figment
116 .extract()
117 .inspect_err(|e| error!("Failed to extract merged config: {:?}", e))
118 .unwrap_or_default();
119
120 if let Some(parent) = path.parent() {
121 fs::create_dir_all(parent).ok();
122 }
123
124 fs::write(
125 &path,
126 serde_json::to_string_pretty(self).unwrap_or_default(),
127 )
128 .inspect_err(|e| error!("Failed to write config: {}", e))
129 .ok();
130
131 CONFIG.set(Some(self.clone()));
132 }
133}