mc-tcpmux 0.1.0

A TCP port multiplexer for Minecraft servers.
use notify::{
	Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher,
	event::{DataChange, ModifyKind},
};
use std::{
	path::Path,
	sync::{
		Arc,
		atomic::{AtomicBool, Ordering},
	},
	time::Duration,
};
use tokio::{
	sync::{Notify, mpsc},
	time::sleep,
};

pub async fn watch_file(path: &Path, ms: u64, event_tx: mpsc::Sender<Event>) -> notify::Result<()> {
	let notifier = Arc::new(Notify::new());
	let event_holder = Arc::new(tokio::sync::Mutex::new(None));
	let cooldown = Arc::new(AtomicBool::new(false));

	let notifier_cb = notifier.clone();
	let event_holder_cb = event_holder.clone();
	let cooldown_cb = cooldown.clone();

	let mut watcher: RecommendedWatcher = Watcher::new(
		move |res: Result<Event, notify::Error>| {
			if let Ok(event) = res {
				if event.kind == EventKind::Modify(ModifyKind::Data(DataChange::Any))
					&& !cooldown_cb.load(Ordering::Relaxed)
				{
					let mut guard = tokio::task::block_in_place(|| event_holder_cb.blocking_lock());
					*guard = Some(event);
					notifier_cb.notify_one();
				}
			}
		},
		notify::Config::default(),
	)?;

	watcher.watch(path, RecursiveMode::NonRecursive)?;

	let block_duration = Duration::from_millis(ms);

	loop {
		notifier.notified().await;
		cooldown.store(true, Ordering::Relaxed);

		if let Some(ev) = event_holder.lock().await.take() {
			let _ = event_tx.send(ev).await;
		}

		sleep(block_duration).await;
		cooldown.store(false, Ordering::Relaxed);
	}
}