modcli/output/
messages.rs1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::sync::Mutex;
4use std::sync::OnceLock;
5
6static CATALOG: OnceLock<Mutex<HashMap<String, String>>> = OnceLock::new();
7type InterceptorFn = dyn Fn(&str, &str) -> Cow<'static, str> + Send + Sync;
8static INTERCEPTOR: OnceLock<Mutex<Option<Box<InterceptorFn>>>> = OnceLock::new();
9
10fn catalog() -> &'static Mutex<HashMap<String, String>> {
11 CATALOG.get_or_init(|| Mutex::new(HashMap::new()))
12}
13
14fn interceptor() -> &'static Mutex<Option<Box<InterceptorFn>>> {
15 INTERCEPTOR.get_or_init(|| Mutex::new(None))
16}
17
18pub fn set_message<K: Into<String>, V: Into<String>>(key: K, value: V) {
20 if let Ok(mut map) = catalog().lock() {
21 map.insert(key.into(), value.into());
22 }
23}
24
25pub fn reset_message(key: &str) {
27 if let Ok(mut map) = catalog().lock() {
28 map.remove(key);
29 }
30}
31
32pub fn get_message(key: &str) -> Option<String> {
34 catalog().lock().ok().and_then(|m| m.get(key).cloned())
35}
36
37pub fn message_or_default<'a>(key: &str, default: &'a str) -> Cow<'a, str> {
39 if let Some(val) = get_message(key) {
40 Cow::Owned(val)
41 } else {
42 Cow::Borrowed(default)
43 }
44}
45
46pub fn set_output_interceptor<F>(f: F)
49where
50 F: Fn(&str, &str) -> Cow<'static, str> + Send + Sync + 'static,
51{
52 if let Ok(mut slot) = interceptor().lock() {
53 *slot = Some(Box::new(f));
54 }
55}
56
57pub fn clear_output_interceptor() {
59 if let Ok(mut slot) = interceptor().lock() {
60 *slot = None;
61 }
62}
63
64pub fn intercept<'a>(category: &str, text: &'a str) -> Cow<'a, str> {
66 if let Ok(slot) = interceptor().lock() {
67 if let Some(ref cb) = *slot {
68 let owned: Cow<'static, str> = cb(category, text);
70 return Cow::Owned(owned.into_owned());
71 }
72 }
73 Cow::Borrowed(text)
74}
75
76#[cfg(feature = "theme-config")]
79pub fn load_messages_from_json(path: &str) -> Result<(), crate::error::ModCliError> {
80 let data = std::fs::read_to_string(path)?;
81 let map: HashMap<String, String> = serde_json::from_str(&data)?;
82 if let Ok(mut cat) = catalog().lock() {
83 for (k, v) in map {
84 cat.insert(k, v);
85 }
86 }
87 Ok(())
88}
89
90#[cfg(not(feature = "theme-config"))]
91pub fn load_messages_from_json(_path: &str) -> Result<(), crate::error::ModCliError> {
92 Err(crate::error::ModCliError::InvalidUsage(
93 "messages JSON loader requires feature: theme-config".into(),
94 ))
95}