hotload/
hot_batch.rs

1use std::{path::Path, sync::Arc};
2
3use dlopen2::wrapper::WrapperApi;
4use fast_able::vec::{ReadGuard, SyncVec};
5use notify::{Event, RecursiveMode, Watcher};
6
7use crate::{
8    event::{EventNewDll, EventOldDll},
9    types::{get_file_name, Hotload, UA},
10    R,
11};
12
13pub const MAX_LOAD_INDEX: usize = 10;
14
15pub struct HotloadBatch<API: WrapperApi> {
16    inner_batch: Arc<SyncVec<Hotload<API>>>,
17    dir: String,
18    pub watch: UA<notify::RecommendedWatcher>,
19}
20
21// impl<API: WrapperApi + 'static> Deref for HotloadBatch<API> {
22//     type Target = Container<API>;
23//     fn deref(&self) -> &Self::Target {
24//         let inner = &*self.inner;
25//         let inner = &inner.container[inner.load_index];
26//         inner.as_ref().expect("container not loaded")
27//     }
28// }
29
30// // inpl DerefMut
31// impl<API: WrapperApi + 'static> DerefMut for HotloadBatch<API> {
32//     fn deref_mut(&mut self) -> &mut Self::Target {
33//         let inner = self.inner.get_mut();
34//         let inner = &mut inner.container[inner.load_index];
35//         inner.as_mut().expect("container not loaded")
36//     }
37// }
38
39const HOTLOAD_CACHE_DIR: &str = ".hotload_cache_dir";
40
41impl<API: WrapperApi + 'static + Send> HotloadBatch<API> {
42    pub fn new(dir_path: impl Into<String>) -> R<Self> {
43        let path = dir_path.into();
44
45        // 遍历文件夹里所有 动态库
46        let mut batch_file = vec![];
47        let p = Path::new(&path).to_path_buf();
48        let dir_iter = std::fs::read_dir(p.clone())?;
49        for ele in dir_iter {
50            let ele = ele?;
51            if !ele.file_type()?.is_file() {
52                continue;
53            }
54            batch_file.push(ele.path());
55        }
56
57        let dir_cahe = Path::new(&path);
58        let mut dir_cahe = dir_cahe.to_path_buf();
59        dir_cahe.push(HOTLOAD_CACHE_DIR);
60        _ = std::fs::create_dir(dir_cahe.clone());
61
62        let r = SyncVec::new();
63        for ele in batch_file.iter() {
64            let p = ele.to_str().unwrap_or_else(|| "").to_string();
65            if p.is_empty() {
66                continue;
67            }
68
69            static PATHS: fast_able::vec::SyncVec<String> = fast_able::vec::SyncVec::new();
70            PATHS.push(p);
71
72            let item = Hotload::new(PATHS[PATHS.len() - 1].as_str());
73            r.push(item);
74        }
75
76        Ok(Self {
77            inner_batch: r.into(),
78            dir: path,
79            watch: UA::new(),
80        })
81    }
82
83    /// "dll created" or "file rename to" is actively loaded
84    /// frist time load EventOldDll is None
85    /// load all "file_name-xxx" dll
86    pub fn init_load<
87        F: FnMut(EventNewDll<API>, Option<EventOldDll<API>>) + Send + 'static + Clone,
88    >(
89        &self,
90        event_call: F,
91    ) -> R<()> {
92        for ele in self.inner_batch.iter() {
93            ele.init_load(event_call.clone())?;
94        }
95
96        let inner_batch = self.inner_batch.clone();
97        let mut watcher = notify::recommended_watcher(move |res: Result<Event, notify::Error>| {
98            let event = match res {
99                Ok(v) => v,
100                Err(e) => {
101                    error!("watch error: {e:?}");
102                    return;
103                }
104            };
105
106            // The file in the incident is not a monitored file and will not be processed
107            let files = event
108                .paths
109                .iter()
110                .filter(|x| x.is_file())
111                .collect::<Vec<_>>();
112            if files.is_empty() {
113                return;
114            }
115
116            let mut is_action = false;
117            match &event.kind {
118                notify::EventKind::Create(_e) => is_action = true,
119                notify::EventKind::Modify(notify::event::ModifyKind::Name(
120                    notify::event::RenameMode::To,
121                )) => is_action = true,
122                _ => (),
123            };
124
125            if !is_action {
126                return;
127            }
128
129            for ele in files {
130                let full_path = ele.to_str().unwrap_or_else(|| "").to_string();
131                if full_path.is_empty() {
132                    continue;
133                }
134
135                let change_file_name = ele
136                    .as_path()
137                    .file_name()
138                    .and_then(|x| x.to_str())
139                    .unwrap_or_else(|| "")
140                    .to_string();
141                if change_file_name.is_empty() {
142                    continue;
143                }
144
145                if inner_batch
146                    .iter()
147                    .find(|x| {
148                        get_file_name(&x.inner.path).unwrap_or_else(|_| "".to_string())
149                            == change_file_name
150                    })
151                    .is_some()
152                {
153                    continue;
154                }
155
156                debug!("event: {:?}; load new dll", event);
157
158                static PATHS: fast_able::vec::SyncVec<String> = fast_able::vec::SyncVec::new();
159                PATHS.push(full_path);
160
161                let item = Hotload::<API>::new(PATHS[PATHS.len() - 1].as_str());
162                if let Err(e) = item.init_load(event_call.clone()) {
163                    error!("{e:?}; item.init_load(event_call.clone()): {change_file_name}")
164                } else {
165                    inner_batch.push(item);
166                }
167            }
168        })?;
169
170        // watch directory
171        let dir = &self.dir;
172        debug!("hoload watch path: {dir:?}");
173        watcher.watch(Path::new(&dir), RecursiveMode::NonRecursive)?;
174        self.watch.set(watcher);
175
176        Ok(())
177    }
178
179    pub fn get(&self, index: usize) -> Option<ReadGuard<Hotload<API>>> {
180        self.inner_batch.get(index)
181    }
182
183    pub fn iter(&self) -> fast_able::vec::Iter<'_, Hotload<API>> {
184        self.inner_batch.iter()
185    }
186}
187
188// IntoIterator
189// impl<API: WrapperApi, 'a> IntoIterator for HotloadBatch<API> {
190//     type IntoIter = std::slice::Iter<'a, Self::Item>;
191//     type Item = Hotload<API>;
192//     fn into_iter(self) -> Self::IntoIter {
193//         self.inner_batch.into_iter()
194//     }
195// }
196
197// impl<API: WrapperApi> Iterator for Hotload<API> {
198//     type Item =  Hotload<API>;
199//     fn next(&mut self) -> Option<Self::Item> {
200//         todo!()
201//     }
202// }