json_gettext/common/rocket_feature/reloadable/
json_get_text_manager.rs1use std::{
2 collections::HashMap,
3 mem,
4 ops::Deref,
5 path::{Path, PathBuf},
6 sync::atomic::{AtomicBool, Ordering},
7 time::SystemTime,
8};
9
10use rocket::fairing::Fairing;
11
12use crate::{
13 DebuggableMutate, JSONGetText, JSONGetTextBuildError, JSONGetTextBuilder, JSONGetTextFairing,
14};
15
16#[derive(Debug)]
17pub struct JSONGetTextManager {
18 empty: JSONGetText<'static>,
19 json_gettext: DebuggableMutate<JSONGetText<'static>>,
20 files: DebuggableMutate<HashMap<crate::Key, (PathBuf, Option<SystemTime>)>>,
21 reloading: AtomicBool,
22}
23
24impl JSONGetTextManager {
25 pub fn from_files(
26 default_key: crate::Key,
27 source: Vec<(crate::Key, &'static str)>,
28 ) -> Result<JSONGetTextManager, JSONGetTextBuildError> {
29 let mut files = HashMap::with_capacity(source.len());
30
31 for (key, json_file) in source {
32 let metadata = Path::new(json_file).metadata()?;
33
34 let mtime = metadata.modified().ok();
35
36 files.insert(key, (PathBuf::from(json_file), mtime));
37 }
38
39 let mut builder = JSONGetTextBuilder::new(default_key);
40
41 for (key, (path, _)) in &files {
42 builder.add_json_file(key, path)?;
43 }
44
45 let json_gettext = builder.build()?;
46
47 let mut empty_builder = JSONGetTextBuilder::new(json_gettext.get_default_key());
49
50 empty_builder.add_map(json_gettext.get_default_key(), HashMap::new())?;
51
52 Ok(JSONGetTextManager {
53 empty: empty_builder.build()?,
54 json_gettext: DebuggableMutate::new(json_gettext),
55 files: DebuggableMutate::new(files),
56 reloading: AtomicBool::new(false),
57 })
58 }
59
60 pub fn reload_if_needed(&self) -> Result<(), JSONGetTextBuildError> {
61 if self
62 .reloading
63 .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
64 .is_ok()
65 {
66 let mut do_reload = false;
67
68 let files = self.files.get_mut();
69
70 for (path, mtime) in files.values_mut() {
71 let metadata = path.metadata().inspect_err(|_| {
72 self.reloading.store(false, Ordering::Relaxed);
73 })?;
74
75 let (reload, new_mtime) = match mtime {
76 Some(mtime) => match metadata.modified() {
77 Ok(new_mtime) => (new_mtime > *mtime, Some(new_mtime)),
78 Err(_) => (true, None),
79 },
80 None => match metadata.modified() {
81 Ok(new_mtime) => (true, Some(new_mtime)),
82 Err(_) => (true, None),
83 },
84 };
85
86 if reload {
87 *mtime = new_mtime;
88
89 do_reload = true;
90 }
91 }
92
93 if do_reload {
94 let mut builder = JSONGetTextBuilder::new(self.get_default_key());
95
96 for (key, (path, _)) in files.iter() {
97 builder.add_json_file(key, path).inspect_err(|_| {
98 self.reloading.store(false, Ordering::Relaxed);
99 })?;
100 }
101
102 let json_gettext = builder.build().inspect_err(|_| {
103 self.reloading.store(false, Ordering::Relaxed);
104 })?;
105
106 drop(mem::replace(self.json_gettext.get_mut(), json_gettext));
107 }
108
109 self.reloading.store(false, Ordering::Relaxed);
110 }
111
112 Ok(())
113 }
114}
115
116impl JSONGetTextManager {
117 pub fn fairing<F>(f: F) -> impl Fairing
119 where
120 F: Fn() -> (crate::Key, Vec<(crate::Key, &'static str)>) + Send + Sync + 'static, {
121 JSONGetTextFairing {
122 custom_callback: Box::new(f)
123 }
124 }
125}
126
127impl Deref for JSONGetTextManager {
128 type Target = JSONGetText<'static>;
129
130 #[inline]
131 fn deref(&self) -> &JSONGetText<'static> {
132 if self.reloading.load(Ordering::Relaxed) { &self.empty } else { self.json_gettext.get() }
134 }
135}