srcsearch
srcsearch is a lightweight search engine for source code and project documentation. It indexes Rust source files and Markdown content, then lets developers query it using Tantivy-powered full-text search with BM25 ranking.
It can be used in two ways:
- CLI (
srcsearch) for local workflows and scripting.
- Library (
srcsearch) for embedding indexing/search in your own Rust tooling.
CLI usage
The crate provides a binary named srcsearch with these subcommands:
json — build a JSON output
index — build a Tantivy index directory
update — incrementally update an existing Tantivy index for changed files
search — query a Tantivy index
Build and run
cargo run -- --help
1) Generate a JSON output
cargo run -- json --project-root . --output index.json
Short form:
cargo run -- json -p . -o index.json
2) Build a Tantivy index directory
cargo run -- index --project-root . --output-dir index
Short form:
cargo run -- index -p . -o index
--output-dir must be empty (or not exist yet) when creating a fresh index.
3) Update an existing index after file changes
cargo run -- update \
--project-root . \
--index-dir index \
--changed-file src/lib.rs \
--changed-file docs/guide.md
Short form:
cargo run -- update -p . -i index --changed-file src/lib.rs
4) Search the index
Search all fields (default scope):
cargo run -- search --index-dir index --query quickstart
Restrict search to documentation-focused fields only:
cargo run -- search --index-dir index --query quickstart --scope doc
JSON output:
cargo run -- search --index-dir index --query quickstart --json
Search scopes
all (default): query title/body text + Rust symbol/signature/doc/code fields
doc: query title/body text + Rust doc fields only (ignores signatures/code)
Notes:
- Queries run against
title, body_text, and Rust doc fields use stemming, so inflected forms (for example running vs run) may match.
Library usage
Add srcsearch from crates.io:
[dependencies]
srcsearch = "0.1"
If you are working from a local checkout instead, you can use a path dependency:
srcsearch = { path = "../srcsearch" }
Build records from a project (or a single target)
use std::path::Path;
use srcsearch::{index_project, index_target};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let records = index_project(Path::new("."))?;
println!("indexed {} records", records.len());
let changed = index_target(Path::new("src/lib.rs"), Path::new("."))?;
println!("indexed {} changed-records", changed.len());
Ok(())
}
Write JSON or Tantivy index
use std::path::Path;
use srcsearch::{index_project, write_json, write_tantivy_index};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let root = Path::new(".");
let records = index_project(root)?;
write_json(&records, Path::new("index.json"))?;
write_tantivy_index(&records, Path::new("index"), Some(root))?;
Ok(())
}
Incremental update
use std::path::Path;
use srcsearch::{index_target, update_tantivy_index};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let root = Path::new(".");
let changed_files = vec!["src/lib.rs".to_string()];
let mut changed_records = Vec::new();
for file in &changed_files {
let path = root.join(file);
let mut file_records = index_target(&path, root)?;
changed_records.append(&mut file_records);
}
update_tantivy_index(&changed_records, Path::new("index"), Some(root), &changed_files)?;
Ok(())
}
Search from code
use std::path::Path;
use srcsearch::{search_tantivy_index, SearchScope};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let hits = search_tantivy_index(Path::new("index"), "quickstart", 10, SearchScope::Doc)?;
for hit in hits {
println!("{} {} {:?}", hit.record_type, hit.file_path, hit.line_start);
}
Ok(())
}
Development
cargo test
cargo fmt