use std::{
fs,
ops::Deref,
path::{Path, PathBuf},
};
use dlopen2::wrapper::{Container, WrapperApi};
use crate::{
types::{HotloadInner, MAX_LOAD_INDEX, UA},
R,
};
pub struct EventNewDll<API: WrapperApi> {
action: bool,
inner: UA<HotloadInner<API>>,
swith_value: Option<(usize, Container<API>)>,
}
impl<API: WrapperApi + 'static> Deref for EventNewDll<API> {
type Target = Container<API>;
fn deref(&self) -> &Self::Target {
self.swith_value
.as_ref()
.map(|x| &x.1)
.expect("New dll not loaded")
}
}
impl<API: WrapperApi> EventNewDll<API> {
pub(crate) fn new(call: UA<HotloadInner<API>>, cahe_path: PathBuf) -> R<Self> {
let swith_value = Self::load_new(call.clone(), cahe_path)?;
let drop = Self {
action: true,
inner: call,
swith_value: Some(swith_value),
};
Ok(drop)
}
pub fn dll_name(&self) -> String {
crate::types::get_file_name(self.inner.path.as_str())
.unwrap_or_else(|_| "".to_string())
.to_string()
}
pub fn cancel(&mut self) {
self.action = false;
debug!("Cancel load new dll");
}
pub(crate) fn load_new(
_self: UA<HotloadInner<API>>,
mut f_new: PathBuf,
) -> R<(usize, Container<API>)> {
let inner = _self.borrow_mut();
let mut load_index = inner.load_index + 1;
if load_index >= MAX_LOAD_INDEX {
load_index = 0;
}
let exe_name = std::env::current_exe().ok();
let exe_name = exe_name
.as_ref()
.and_then(|x| x.file_name())
.and_then(|x| x.to_str())
.unwrap_or_else(|| "unknown.exe");
let ext_index = exe_name.rfind('.').unwrap_or_else(|| exe_name.len());
let exe_name = &exe_name[..ext_index];
let f_name = crate::types::get_file_name(&inner.path)?;
let f_old: &Path = std::path::Path::new(&inner.path);
let new_name = format!(".{}_hotload_{exe_name}_{f_name}", load_index);
f_new.push(new_name);
let mut copy_r = Ok(0);
for i in 1..=10 {
copy_r = fs::copy(f_old, f_new.clone());
if copy_r.is_ok() {
break;
}
let t = 500 * i;
warn!("fs::copy({f_old:?}, {f_new:?}): {copy_r:?}; current file is being created, waiting for {t}ms, please wait");
std::thread::sleep(std::time::Duration::from_millis(t));
}
copy_r?;
let c: Container<API> = unsafe { Container::load(&f_new) }?;
debug!("load new dll start: {f_name} > {f_new:?}");
Ok((load_index, c))
}
fn swith(&mut self) {
if !self.action {
return;
}
let inner = self.inner.borrow_mut();
if let Some((load_index, c)) = self.swith_value.take() {
inner.container[load_index] = Some(c);
inner.load_index = load_index;
debug!("load new dll: {load_index} complete!");
}
}
}
impl<API: WrapperApi> Drop for EventNewDll<API> {
fn drop(&mut self) {
self.swith();
}
}
pub struct EventOldDll<API: WrapperApi> {
action: bool,
inner: UA<HotloadInner<API>>,
cur_index: usize,
}
impl<API: WrapperApi + 'static> Deref for EventOldDll<API> {
type Target = Container<API>;
fn deref(&self) -> &Self::Target {
let inner = &*self.inner;
let inner = &inner.container[self.cur_index];
inner.as_ref().expect("Old dll not loaded")
}
}
impl<API: WrapperApi> EventOldDll<API> {
pub fn new(call: UA<HotloadInner<API>>) -> Self {
let cur_index = call.borrow_mut().load_index;
let drop = Self {
action: true,
inner: call,
cur_index,
};
drop
}
pub fn cancel(&mut self) {
self.action = false;
debug!("Cancel release old dll");
}
fn release_old(&self) {
if !self.action {
return;
}
let inner = self.inner.borrow_mut();
let load_index = self.cur_index;
let take_v = inner.container[load_index].take();
if let Some(api) = take_v {
debug!("release old dll {load_index} start, please wait");
drop(api);
debug!("release old dll {load_index} complete!");
}
}
}
impl<API: WrapperApi> Drop for EventOldDll<API> {
fn drop(&mut self) {
self.release_old()
}
}