feat(watcher): watchers can now operate over entire workspaces, or individual projects

What does this MR do and why?

The primary change was the introduction of configurable watcher modes, which determines the granularity of reindexing operations. The modifications enable both workspace-level and project-level reindexing strategies.

Architectural Change: Configurable Watcher Granularity

The watcher system was modified to support two operational modes through a configuration-driven approach.

1. WatcherConfig Enhancement (crates/http-server/src/watcher.rs)

A new configuration system was implemented to control watcher behavior.

  • New Field: Added single_watcher: bool to WatcherConfig struct
  • Builder Pattern: Implemented fluent configuration methods periodic_force_index() and single_watcher()
  • Default Behavior: Both flags default to false, maintaining backward compatibility

2. Job Type Addition (crates/http-server/src/queue/job.rs)

A new job variant was added to support project-level reindexing.

  • New Job Variant: ReindexProjectFolderWithWatchedFiles was added with fields:
    • workspace_folder_path: String
    • project_folder_path: String
    • project_changes: Vec<PathBuf>
    • priority: JobPriority

Implementation Details

3. Conditional Job Dispatch Logic (crates/http-server/src/watcher.rs)

The event processing logic was modified to dispatch different job types based on configuration.

  • Single Watcher Mode: When single_watcher is true, dispatches ReindexWorkspaceFolderWithWatchedFiles jobs
  • Multiple Watcher Mode: When single_watcher is false, dispatches ReindexProjectFolderWithWatchedFiles jobs
  • Path Resolution: The get_active_paths() function returns different path sets depending on the mode:
    • Single watcher: workspace folder paths only
    • Multiple watcher: individual project paths within workspaces

4. Worker Implementation (crates/http-server/src/queue/worker.rs)

The worker was updated to handle the new job type.

  • Job Processing: Added process_reindex_project_job() method to handle project-level reindexing
  • Execution Strategy: Uses single-threaded execution (threads = 1) to avoid performance issues
  • Error Handling: Implements comprehensive error handling for task execution, including panic recovery

5. Pathset Computation Logic

The compute_project_watcher_pathset() function was modified to handle both modes.

  • Condition Update: Changed from if project_path != workspace_path to if project_path != workspace_path || watcher_config.single_watcher
  • Behavior: In single watcher mode, always uses recursive watching regardless of path equality

Related Issues

#64 (closed), #106 (closed)

Testing

A test module was added to validate the configuration system.

  • Configuration Tests: Validates default values and builder pattern functionality
  • Integration Tests: Tests watcher creation with custom configurations
  • Test Utilities: Helper functions for creating test workspace managers and job dispatchers

All existing tests pass.

Performance Analysis

Performance Checklist

  • Have you reviewed your memory allocations to ensure you're optimizing correctly? Are you cloning or copying unnecessary data?
  • Have you profiled with cargo bench or criterion to measure performance impact?
  • Are you using zero-copy operations where possible (e.g., &str instead of String, slice references)?
  • Have you considered using Cow<'_, T> for conditional ownership to avoid unnecessary clones?
  • Are iterator chains and lazy evaluation being used effectively instead of intermediate collections?
  • Are you reusing allocations where possible (e.g., Vec::clear() and reuse vs new allocation)?
  • Have you considered using SmallVec or similar for small, stack-allocated collections?
  • Are async operations properly structured to avoid blocking the executor?
  • Have you reviewed unsafe code blocks for both safety and performance implications?
  • Are you using appropriate data structures (e.g., HashMap vs BTreeMap vs IndexMap)?
  • Have you considered compile-time optimizations (e.g., const fn, generics instead of trait objects)?
  • Are debug assertions (debug_assert!) used instead of runtime checks where appropriate?
Edited by Michael Usachenko

Merge request reports

Loading