Files
codex/prs/bolinfest/study/PR-2115-study.md
2025-09-02 15:17:45 -07:00

4.3 KiB
Raw Blame History

DOs

  • Check Simple Conditions Before Locking: Gate on cheap checks like query.is_empty() or identical queries without taking the mutex.
// Good: no lock held for trivial early-exit
if query.is_empty() {
    let max_total = MAX_FILE_SEARCH_RESULTS.get();
    let matches = collect_top_level_suggestions(&self.search_dir, max_total);
    self.app_tx.send(AppEvent::FileSearchResult {
        query: String::new(),
        matches,
    });
    return;
}

// Only now take the lock
let mut st = self.state.lock().unwrap();
if query == st.latest_query {
    return;
}
  • Model State Explicitly (avoid overloading): Prefer clear flags or enums over Option<String> when representing “never shown” vs “empty string”.
// Prefer explicit fields
struct SearchState {
    latest_query: String,
    has_shown_popup: bool, // instead of latest_query: Option<String>
}
  • Keep Code Paths Symmetric: Make empty and non-empty query flows update/search state consistently to preserve invariants.
fn handle_query(&self, query: String) {
    if query.is_empty() {
        self.finish_empty_query(&query);      // updates scheduling state as needed
    } else {
        self.start_non_empty_search(query);   // sets active_search + cancellation_token
    }
}

fn finish_empty_query(&self, query: &str) {
    // Clear any scheduled search consistently
    let mut st = self.state.lock().unwrap();
    st.is_search_scheduled = false;
    st.active_search = None;
    drop(st);

    let max_total = MAX_FILE_SEARCH_RESULTS.get();
    let matches = collect_top_level_suggestions(&self.search_dir, max_total);
    self.app_tx.send(AppEvent::FileSearchResult {
        query: query.to_string(),
        matches,
    });
}
  • Short-Circuit Unchanged Inputs: Avoid re-scheduling when the query did not change; combine with the empty check if appropriate.
let unchanged = {
    let st = self.state.lock().unwrap();
    query == st.latest_query
};
if unchanged {
    return;
}
  • Put Imports At The Top: Keep use statements at module scope; use descriptive types.
use std::collections::HashSet;
use std::fs;
  • Use Descriptive Names: Prefer self-explanatory identifiers over two-letter names in filesystem loops.
let mut seen: HashSet<String> = HashSet::new();

if let Ok(dir_iter) = fs::read_dir(cwd) {
    for dir_entry in dir_iter.flatten() {
        let entry_path = dir_entry.path();
        let Some(name) = entry_path.strip_prefix(cwd).ok().and_then(|p| p.to_str()) else { continue };
        // ...
    }
}

DONTs

  • Dont Hold Locks During I/O or Heavy Work: Copy out what you need, drop the lock, then perform filesystem or compute operations.
// Anti-pattern: lock held across I/O
let mut st = self.state.lock().unwrap();
for entry in std::fs::read_dir(&self.search_dir).unwrap() { /* ... */ }

// Better:
let search_dir = self.search_dir.clone();
drop(st);
for entry in std::fs::read_dir(search_dir).unwrap() { /* ... */ }
  • Dont Introduce APIs Without Clear Need: Avoid adding methods like reset() unless theyre necessary, consistently used, and tested.
// Instead of adding a broad reset(), cancel the active search via existing state.
let mut st = self.state.lock().unwrap();
if let Some(active) = st.active_search.take() {
    active.cancellation_token.store(true, std::sync::atomic::Ordering::Relaxed);
}
st.is_search_scheduled = false;
  • Dont Overload Option<String> For Multiple States: None vs Some("") is ambiguous; model intent explicitly.
// Avoid:
latest_query: Option<String>

// Prefer:
latest_query: String,
has_shown_popup: bool,
  • Dont Put use Inside Functions For Common Types: Keep module imports centralized; local use obscures dependencies.
// Avoid inside fn:
fn collect_top_level_suggestions(...) {
    use std::collections::HashSet; // <-- move to top
}
  • Dont Diverge State Between Code Paths: Ensure the “empty query” fast path doesnt skip essential bookkeeping done by the normal path.
// Avoid: empty path bypasses state updates
if query.is_empty() {
    // send results and return; state not updated
    return;
}

// Prefer: both paths maintain scheduler/active_search invariants
handle_query(query);