futuresdr/runtime/
config.rs1#[cfg(not(target_arch = "wasm32"))]
3use config::File;
4#[cfg(not(target_arch = "wasm32"))]
5use config::Source;
6use config::Value;
7use once_cell::sync::Lazy;
8use std::collections::HashMap;
9use std::path::PathBuf;
10use std::str::FromStr;
11use std::sync::Mutex;
12use std::sync::MutexGuard;
13use tracing::level_filters::LevelFilter;
14
15pub fn config() -> Config {
17 get_config().clone()
18}
19
20fn get_config() -> MutexGuard<'static, Config> {
22 CONFIG.lock().unwrap_or_else(|poison| {
23 warn!("config poisoned, restoring initial config");
24 let mut c = poison.into_inner();
25 *c = init_config();
26 CONFIG.clear_poison();
27 c
28 })
29}
30
31pub fn set<V: Into<config::Value>>(name: impl Into<String>, value: V) {
33 get_config().set_value(name, value);
34}
35
36pub fn get_value(name: &str) -> Option<Value> {
38 get_config().misc.get(name).cloned()
39}
40
41pub fn get<T: FromStr>(name: &str) -> Option<T> {
43 get_config()
44 .misc
45 .get(name)
46 .and_then(|v| v.clone().into_string().ok())
47 .and_then(|v| v.parse::<T>().ok())
48}
49
50#[cfg(not(target_arch = "wasm32"))]
51fn init_config() -> Config {
52 let mut settings = ::config::Config::builder();
53
54 if let Some(mut path) = dirs::config_dir() {
56 path.push("futuresdr");
57 path.push("config.toml");
58
59 settings = settings.add_source(File::from(path.clone()).required(false));
60 }
61
62 settings =
64 settings.add_source(File::new("config.toml", config::FileFormat::Toml).required(false));
65
66 settings = settings.add_source(config::Environment::with_prefix("futuresdr"));
68
69 let mut c = Config::default();
71
72 match settings.build() {
73 Ok(settings) => match settings.collect() {
74 Ok(config) => {
75 for (k, v) in config.iter() {
76 match k.as_str() {
77 "queue_size" => {
78 c.queue_size = config_parse::<usize>(v);
79 }
80 "buffer_size" => {
81 c.buffer_size = config_parse::<usize>(v);
82 }
83 "stack_size" => {
84 c.stack_size = config_parse::<usize>(v);
85 }
86 "slab_reserved" => {
87 c.slab_reserved = config_parse::<usize>(v);
88 }
89 "log_level" => {
90 c.log_level = config_parse::<LevelFilter>(v);
91 }
92 "ctrlport_enable" => {
93 c.ctrlport_enable = config_parse::<bool>(v);
94 }
95 "ctrlport_bind" => {
96 c.ctrlport_bind = v.to_string();
97 }
98 "frontend_path" => {
99 c.frontend_path = Some(config_parse::<PathBuf>(v));
100 }
101 _ => {
102 c.misc.insert(k.clone(), v.clone());
103 }
104 }
105 }
106 }
107 Err(e) => warn!("error parsing config {e:?}"),
108 },
109 Err(e) => warn!("error reading config {e:?}"),
110 }
111 c
112}
113
114#[cfg(target_arch = "wasm32")]
115fn init_config() -> Config {
116 Config::default()
117}
118
119static CONFIG: Lazy<Mutex<Config>> = Lazy::new(|| Mutex::new(init_config()));
120
121#[derive(Debug, Clone)]
123pub struct Config {
124 pub queue_size: usize,
126 pub buffer_size: usize,
128 pub stack_size: usize,
130 pub slab_reserved: usize,
132 pub log_level: LevelFilter,
134 pub ctrlport_enable: bool,
136 pub ctrlport_bind: String,
138 pub frontend_path: Option<PathBuf>,
140 misc: HashMap<String, Value>,
141}
142
143impl Config {
144 fn set_value<V: Into<config::Value>>(&mut self, name: impl Into<String>, value: V) {
145 let name = name.into();
146 let value = value.into();
147
148 match name.as_str() {
149 "queue_size" => {
150 self.queue_size = config_parse::<usize>(&value);
151 }
152 "buffer_size" => {
153 self.buffer_size = config_parse::<usize>(&value);
154 }
155 "stack_size" => {
156 self.stack_size = config_parse::<usize>(&value);
157 }
158 "slab_reserved" => {
159 self.slab_reserved = config_parse::<usize>(&value);
160 }
161 "log_level" => {
162 self.log_level = config_parse::<LevelFilter>(&value);
163 }
164 "ctrlport_enable" => {
165 self.ctrlport_enable = config_parse::<bool>(&value);
166 }
167 "ctrlport_bind" => {
168 self.ctrlport_bind = value.to_string();
169 }
170 "frontend_path" => {
171 self.frontend_path = Some(config_parse::<PathBuf>(&value));
172 }
173 _ => {
174 self.misc.insert(name, value);
175 }
176 }
177 }
178}
179
180impl Default for Config {
181 #[cfg(debug_assertions)]
182 fn default() -> Self {
183 Config {
184 queue_size: 8192,
185 buffer_size: 32768,
186 stack_size: 16 * 1024 * 1024,
187 slab_reserved: 0,
188 log_level: LevelFilter::DEBUG,
189 ctrlport_enable: true,
190 ctrlport_bind: "127.0.0.1:1337".to_string(),
191 frontend_path: None,
192 misc: HashMap::new(),
193 }
194 }
195
196 #[cfg(not(debug_assertions))]
197 fn default() -> Self {
198 Config {
199 queue_size: 8192,
200 buffer_size: 32768,
201 stack_size: 16 * 1024 * 1024,
202 slab_reserved: 0,
203 log_level: LevelFilter::INFO,
204 ctrlport_enable: true,
205 ctrlport_bind: "127.0.0.1:1337".to_string(),
206 frontend_path: None,
207 misc: HashMap::new(),
208 }
209 }
210}
211
212fn config_parse<T: FromStr>(v: &Value) -> T {
214 if let Ok(v) = v.clone().into_string() {
215 if let Ok(v) = v.parse::<T>() {
216 return v;
217 }
218 }
219
220 println!("invalid config value {v:?}");
221 panic!();
222}