use std::error::Error;
use std::fmt;
pub(super) const MAX_LOCK_RETRIES: usize = 5;
pub(super) const MAX_LOCK_RETRY_DELAY_MS: u64 = 30_000;
pub(super) const DEFAULT_MAX_DIR_WATCHES: usize = 50_000;
pub(super) const MAX_MIN_SYNC_INTERVAL_MS: u64 = 60_000;
const DEFAULT_MAX_DEBOUNCE_MULTIPLIER: u64 = 5;
pub(super) const MAX_MAX_DEBOUNCE_MS: u64 = 60_000;
pub(super) fn resolve_max_debounce_ms(option: Option<u64>, debounce_ms: u64) -> u64 {
let resolved =
option.unwrap_or_else(|| debounce_ms.saturating_mul(DEFAULT_MAX_DEBOUNCE_MULTIPLIER));
resolved.max(debounce_ms).min(MAX_MAX_DEBOUNCE_MS)
}
pub(super) fn resolve_min_sync_interval_ms(option: Option<u64>, debounce_ms: u64) -> u64 {
let resolved = option.unwrap_or(debounce_ms);
resolved.min(MAX_MIN_SYNC_INTERVAL_MS)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ScheduledSyncKind {
Debounce,
Retry,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct FileSnapshot {
pub modified_ns: u128,
pub len: u64,
}
pub(super) const EXHAUSTION_REASON: &str = "OS watch/file limit exhausted; auto-sync disabled. Run \
`rustcodegraph sync` (or install git sync hooks) to refresh the graph after changes.";
pub(super) const INOTIFY_LIMIT_REASON: &str = "Linux inotify watch limit reached \
(fs.inotify.max_user_watches); live watching now covers only part of the project, so edits \
in unwatched directories will not auto-sync. Raise the limit (e.g. `sudo sysctl \
fs.inotify.max_user_watches=1048576`, persisted in /etc/sysctl.d) and restart, or run \
`rustcodegraph sync` (or install git sync hooks) to refresh.";
pub(super) type SyncFn = Box<dyn FnMut() -> Result<SyncRunResult, WatchSyncError> + Send>;
pub type SyncCompleteCallback = Box<dyn Fn(&SyncRunResult) + Send + Sync>;
pub type SyncErrorCallback = Box<dyn Fn(&WatchSyncError) + Send + Sync>;
pub type DegradedCallback = Box<dyn Fn(&str) + Send + Sync>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SyncRunResult {
pub files_changed: usize,
pub duration_ms: u64,
pub skipped: bool,
}
#[derive(Default)]
pub struct WatchOptions {
pub debounce_ms: Option<u64>,
pub min_sync_interval_ms: Option<u64>,
pub max_debounce_ms: Option<u64>,
pub on_sync_complete: Option<SyncCompleteCallback>,
pub on_sync_error: Option<SyncErrorCallback>,
pub on_degraded: Option<DegradedCallback>,
pub inert_for_tests: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LockUnavailableError {
message: String,
}
impl LockUnavailableError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
pub fn message(&self) -> &str {
&self.message
}
}
impl Default for LockUnavailableError {
fn default() -> Self {
Self::new("RustCodeGraph file lock unavailable; another process is writing")
}
}
impl fmt::Display for LockUnavailableError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl Error for LockUnavailableError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WatchSyncError {
LockUnavailable(LockUnavailableError),
Other(String),
}
impl WatchSyncError {
pub(super) fn message(&self) -> &str {
match self {
Self::LockUnavailable(err) => err.message(),
Self::Other(message) => message,
}
}
}
impl fmt::Display for WatchSyncError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.message())
}
}
impl Error for WatchSyncError {}
impl From<LockUnavailableError> for WatchSyncError {
fn from(value: LockUnavailableError) -> Self {
Self::LockUnavailable(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PendingFile {
pub path: String,
pub first_seen_ms: i64,
pub last_seen_ms: i64,
pub indexing: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum WatchMode {
Recursive,
PerDirectory,
Inert,
}
#[derive(Debug, Clone, Copy)]
pub(super) struct PendingInfo {
pub(super) first_seen_ms: i64,
pub(super) last_seen_ms: i64,
pub(super) indexing: bool,
}