codenexus 0.4.0-rc.1

A queryable code knowledge graph tool built on LadybugDB and tree-sitter
// Copyright (c) 2026 Kirky.X🌠
// SPDX-License-Identifier: MIT

//! DaemonRunner capability trait.
//!
//! Defines [`DaemonRunner`], the capability trait object stored in
//! [`Kit`](crate::kit::Kit) under [`DaemonKey`](crate::kit::DaemonKey) when
//! the `daemon` feature is enabled. The concrete impl wraps the
//! existing [`Daemon`] + [`IndexObserver`] (Observer pattern) so that the
//! unified Kit can hand a pre-configured daemon handle to `daemon_cmd::run`
//! instead of having the CLI construct subsystems ad-hoc.
//!
//! # Blocking semantics
//!
//! [`DaemonRunner::start`] is **blocking** — it enters the daemon event loop
//! and returns only when the daemon stops (user interrupt, watcher error, or
//! channel disconnect). This mirrors the existing [`Daemon::run`] semantics.
//! Callers that need non-blocking behavior should spawn a thread.
//!
//! [`Daemon`]: super::Daemon
//! [`IndexObserver`]: super::IndexObserver
//! [`Daemon::run`]: super::Daemon::run
use std::path::Path;

use super::DaemonError;

/// Capability trait for the Daemon subsystem (file-watcher + incremental
/// indexing).
///
/// Stored in [`Kit`](crate::kit::Kit) as `Arc<dyn DaemonRunner>` under
/// [`DaemonKey`](crate::kit::DaemonKey) when the `daemon` feature is enabled.
/// Conceptually requires `StorageKey` + `IndexerKey`; the concrete impl
/// is self-contained — it opens its own [`IndexFacade`] from the
/// supplied `db_path` and constructs a fresh [`Daemon`] per `start` call.
/// Therefore `Requirements = NoRequirements` at the type level; the bootstrap
/// enforces build ordering (Storage → ... → Indexer → Daemon).
///
/// [`IndexFacade`]: crate::index::IndexFacade
/// [`Daemon`]: super::Daemon
pub trait DaemonRunner: Send + Sync {
    /// Starts the file-watching daemon over `watch_path`, triggering
    /// incremental indexing on code-file changes.
    ///
    /// # Blocking
    ///
    /// This method blocks until the daemon stops. See
    /// [Blocking semantics](self#blocking-semantics).
    ///
    /// # Errors
    ///
    /// Returns [`DaemonError::Notify`] if the watcher cannot be created or
    /// started (e.g. `watch_path` does not exist).
    fn start(&self, watch_path: &Path, project_name: &str) -> Result<(), DaemonError>;

    /// Hot-reloads the debounce window at runtime (spec §Hot reconfiguration).
    ///
    /// The default implementation is a no-op. Concrete capabilities that hold
    /// the config behind an `Arc<RwLock<…>>` override this to propagate the
    /// new value so the next [`start`](DaemonRunner::start) call picks it up.
    fn update_debounce_ms(&self, _new_ms: u64) {}

    /// Hot-reloadable impact-notify toggle (default no-op). Mirrors
    /// [`update_debounce_ms`](Self::update_debounce_ms): capabilities holding
    /// the config override this so the next [`start`](DaemonRunner::start)
    /// call picks it up.
    fn update_impact_notify(&self, _enabled: bool) {}
}

/// Compile-time assertion that `DaemonRunner` is object-safe and `Send + Sync`.
#[cfg(test)]
const _: () = {
    fn _assert_object_safe(_: &dyn DaemonRunner) {}
    fn _assert_send_sync<T: Send + Sync + ?Sized>() {}
    fn _check() {
        _assert_send_sync::<dyn DaemonRunner>();
        let _ = _assert_object_safe;
    }
};