use std::path::Path;
use anyhow::Result;
pub mod client;
pub mod compact;
pub mod config;
pub mod confirm;
pub mod cursor;
pub mod index;
pub mod mcp;
pub mod pagination;
pub mod paths;
pub mod proto;
pub mod query;
pub mod server;
pub mod skill;
pub mod status;
pub mod transport;
pub mod trigram;
use confirm::SearchOptions;
use index::Index;
use query::{Options as QueryOptions, Query};
pub fn effective_pattern(pattern: &str, opts: SearchOptions) -> String {
if opts.fixed_strings {
regex_syntax::escape(pattern)
} else {
pattern.to_string()
}
}
fn query_options(opts: SearchOptions) -> QueryOptions {
QueryOptions {
case_insensitive: opts.case_insensitive,
multi_line: opts.multi_line,
dot_matches_new_line: opts.dot_matches_new_line,
}
}
pub fn is_fallback(pattern: &str, opts: SearchOptions) -> bool {
Query::for_pattern(&effective_pattern(pattern, opts), query_options(opts)).is_fallback()
}
pub fn candidate_paths(
index: &Index,
pattern: &str,
opts: SearchOptions,
) -> Vec<std::path::PathBuf> {
let effective = effective_pattern(pattern, opts);
let query = Query::for_pattern(&effective, query_options(opts));
index
.candidates(&query)
.into_iter()
.map(Path::to_path_buf)
.collect()
}
pub fn stream_search(
index: &Index,
root: &Path,
pattern: &str,
opts: SearchOptions,
emit: impl FnMut(&[u8]) -> Result<()>,
) -> Result<()> {
let effective = effective_pattern(pattern, opts);
let query = Query::for_pattern(&effective, query_options(opts));
let paths = index.candidates(&query);
confirm::search_streaming(&effective, &paths, root, opts, emit)
}
pub fn stream_full_scan(
root: impl AsRef<Path>,
pattern: &str,
opts: SearchOptions,
sink: impl Fn(&[u8]) + Sync,
) -> Result<()> {
let effective = effective_pattern(pattern, opts);
confirm::full_scan(root.as_ref(), &effective, opts, sink)
}
pub fn collect_search(root: &Path, pattern: &str, opts: SearchOptions) -> Result<Vec<u8>> {
if is_fallback(pattern, opts) {
let chunks = std::sync::Mutex::new(Vec::<Vec<u8>>::new());
stream_full_scan(root, pattern, opts, |c| {
if let Ok(mut v) = chunks.lock() {
v.push(c.to_vec());
}
})?;
let mut chunks = chunks.into_inner().unwrap_or_default();
chunks.sort_unstable(); Ok(chunks.concat())
} else {
client::request(
root,
&proto::Request::Search {
opts,
pattern: pattern.to_string(),
},
)
}
}
pub fn search(index: &Index, root: &Path, pattern: &str, opts: SearchOptions) -> Result<Vec<u8>> {
let mut out = Vec::new();
stream_search(index, root, pattern, opts, |c| {
out.extend_from_slice(c);
Ok(())
})?;
Ok(out)
}