//! Boilerplate for the D-Bus adapters that bridge a background thread to the
//! main loop: a calloop channel whose receiver dispatches events on the main
//! thread, and a named worker thread running the blocking D-Bus server.

use std::thread;

use calloop::channel::{self, Sender};
use calloop::LoopHandle;

use crate::forge::Forge;

/// Wire a D-Bus adapter: `run` executes on a worker thread named `name` and
/// emits events through the returned `Sender`; `handler` receives each event on
/// the main loop with `&mut Forge`. A worker error is logged, tagged `name`.
pub fn spawn_adapter<E, R>(
    event_loop: &LoopHandle<'static, Forge>,
    name: &'static str,
    run: impl FnOnce(Sender<E>) -> Result<(), R> + Send + 'static,
    mut handler: impl FnMut(E, &mut Forge) + 'static,
) where
    E: Send + 'static,
    R: std::fmt::Display,
{
    let (sender, channel) = channel::channel::<E>();

    event_loop
        .insert_source(channel, move |event, _, forge| {
            if let channel::Event::Msg(evt) = event {
                handler(evt, forge);
            }
        })
        .expect("dbus adapter channel");

    thread::Builder::new()
        .name(name.to_string())
        .spawn(move || {
            if let Err(e) = run(sender) {
                tracing::error!("{name}: {e}");
            }
        })
        .expect("dbus adapter thread");
}
