use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{Block, Token};
pub(super) struct WatchCfgFileArgs {
title: String,
files: Ident,
reload_block: Block,
}
impl Parse for WatchCfgFileArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let title = input.parse::<syn::LitStr>()?.value();
let _: Token![,] = input.parse()?;
let files = input.parse::<Ident>()?;
let _: Token![,] = input.parse()?;
let reload_block = input.parse()?;
Ok(WatchCfgFileArgs {
title,
files,
reload_block,
})
}
}
pub(super) fn watch_cfg_file_macro(args: WatchCfgFileArgs) -> TokenStream {
let WatchCfgFileArgs {
title,
files,
reload_block,
} = args;
let reload_block = &reload_block.stmts;
let expanded = quote! {
log::debug!("watch {} cfg file: {} ...", #title, #files);
tokio::spawn({
async move {
let (_watcher, receiver) = watch_cfg_file(#files).expect(&format!("watch {} cfg file error: {}", #title, #files));
let mut interval = tokio::time::interval(std::time::Duration::from_secs(1));
loop {
interval.tick().await;
match receiver.try_recv() {
Ok(event_result) => {
match event_result {
Ok(events) => {
let has_modify = events.iter().any(|e| e.event.kind.is_modify());
if !has_modify {
continue;
}
log::debug!("reload from {} cfg file: {} ...", #title, #files);
#( #reload_block )*
}
Err(e) => {
log::warn!("error receiving {} cfg file events: {} {:?}", #title, #files, e);
}
}
}
Err(std::sync::mpsc::TryRecvError::Empty) => {
continue;
}
Err(std::sync::mpsc::TryRecvError::Disconnected) => {
log::debug!("{} cfg file watcher channel closed, exiting watcher loop", #title);
break;
}
}
}
log::debug!("{} cfg file watcher task finished", #title);
}
});
};
TokenStream::from(expanded)
}