tier 0.1.17

Rust configuration library for layered TOML, env, and CLI settings
Documentation
use std::sync::mpsc::{Receiver, RecvTimeoutError};
use std::time::{Duration, Instant};

use super::event::{WatchMessage, reload_deadline_for_event};
use super::target::WatchTarget;
use crate::reload::{ReloadFailurePolicy, ReloadHandle, ReloadOptions};

pub(super) fn run_native_watch_loop<T>(
    handle: ReloadHandle<T>,
    targets: Vec<WatchTarget>,
    rx: Receiver<WatchMessage>,
    debounce: Duration,
    options: ReloadOptions,
) where
    T: Send + Sync + 'static,
{
    loop {
        let Some(deadline) = wait_for_first_reload_deadline(&handle, &targets, &rx, debounce)
        else {
            return;
        };
        let Some(()) = collect_debounced_events(&handle, &targets, &rx, debounce, deadline) else {
            return;
        };

        if handle.reload_with_options(&options).is_err()
            && matches!(options.on_error, ReloadFailurePolicy::StopWatcher)
        {
            return;
        }
    }
}

fn wait_for_first_reload_deadline<T>(
    handle: &ReloadHandle<T>,
    targets: &[WatchTarget],
    rx: &Receiver<WatchMessage>,
    debounce: Duration,
) -> Option<Instant> {
    loop {
        match rx.recv() {
            Ok(WatchMessage::Stop) | Err(_) => return None,
            Ok(WatchMessage::Event(event)) => {
                if let Some(deadline) = reload_deadline_for_event(handle, targets, event, debounce)
                {
                    return Some(deadline);
                }
            }
        }
    }
}

fn collect_debounced_events<T>(
    handle: &ReloadHandle<T>,
    targets: &[WatchTarget],
    rx: &Receiver<WatchMessage>,
    debounce: Duration,
    mut deadline: Instant,
) -> Option<()> {
    loop {
        let timeout = deadline.saturating_duration_since(Instant::now());
        match rx.recv_timeout(timeout) {
            Ok(WatchMessage::Stop) | Err(RecvTimeoutError::Disconnected) => return None,
            Err(RecvTimeoutError::Timeout) => return Some(()),
            Ok(WatchMessage::Event(event)) => {
                if let Some(next_deadline) =
                    reload_deadline_for_event(handle, targets, event, debounce)
                {
                    deadline = next_deadline;
                }
            }
        }
    }
}