#[cfg(not(target_arch = "wasm32"))]
mod build;
#[cfg(not(target_arch = "wasm32"))]
mod build_external;
#[cfg(not(target_arch = "wasm32"))]
mod calibrate;
#[cfg(not(target_arch = "wasm32"))]
mod commit;
#[cfg(not(target_arch = "wasm32"))]
mod compact;
#[cfg(not(target_arch = "wasm32"))]
mod compact_plan;
pub(crate) mod encoding;
#[cfg(not(target_arch = "wasm32"))]
pub mod freshness;
#[cfg(not(target_arch = "wasm32"))]
pub use freshness::{ChangeSet, FreshnessError, UpdateLimits, UpdateOutcome};
#[cfg(not(target_arch = "wasm32"))]
mod fsmonitor;
mod helpers;
pub(crate) mod io_util;
pub(crate) use io_util::open_readonly_nofollow;
#[cfg(any(unix, windows))]
pub(crate) use io_util::verify_fd_matches_stat;
#[cfg(not(target_arch = "wasm32"))]
mod deletes_idx;
#[cfg(not(target_arch = "wasm32"))]
mod delta;
#[cfg(not(target_arch = "wasm32"))]
mod delta_apply;
#[cfg(not(target_arch = "wasm32"))]
pub mod manifest;
#[cfg(not(target_arch = "wasm32"))]
mod open;
pub mod overlay;
#[cfg(not(target_arch = "wasm32"))]
mod paths_idx;
pub mod pending;
pub mod segment;
pub mod snapshot;
#[cfg(not(target_arch = "wasm32"))]
mod stats;
pub mod walk;
#[cfg(feature = "wasm")]
pub mod wasm_index;
#[cfg(not(target_arch = "wasm32"))]
pub use build_external::ExternalFileRecord;
pub use snapshot::{BaseSegments, IndexSnapshot};
pub(crate) use encoding::normalize_encoding;
pub use walk::is_binary;
#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;
#[cfg(not(target_arch = "wasm32"))]
use std::sync::Arc;
#[cfg(not(target_arch = "wasm32"))]
use arc_swap::ArcSwap;
#[cfg(not(target_arch = "wasm32"))]
use crate::index::manifest::Manifest;
#[cfg(not(target_arch = "wasm32"))]
use crate::index::overlay::PendingEdits;
#[cfg(not(target_arch = "wasm32"))]
use crate::{Config, IndexError, IndexStats, SearchMatch, SearchOptions};
#[cfg(not(target_arch = "wasm32"))]
const OVERLAY_WARN_THRESHOLD: f64 = 0.30;
#[cfg(not(target_arch = "wasm32"))]
const MAX_TOTAL_DOCS: u32 = 50_000_000;
#[cfg(not(target_arch = "wasm32"))]
const OVERLAY_ENFORCE_THRESHOLD: f64 = 0.50;
#[cfg(not(target_arch = "wasm32"))]
pub struct Index {
pub config: Config,
snapshot: ArcSwap<snapshot::IndexSnapshot>,
pending: PendingEdits,
_dir_lock: std::fs::File,
pub canonical_root: std::path::PathBuf,
#[cfg(feature = "symbols")]
pub symbol_index: Option<std::sync::Arc<crate::symbol::SymbolIndex>>,
}
#[cfg(not(target_arch = "wasm32"))]
impl Index {
pub fn build(config: Config) -> Result<Self, IndexError> {
build::build_index(config)
}
pub fn build_from_file_records(
config: Config,
records: Vec<ExternalFileRecord>,
) -> Result<Self, IndexError> {
build_external::build_index_from_external_records(config, records)
}
pub fn verify(&self) -> Result<(), IndexError> {
let snap = self.snapshot.load();
for seg in snap.base_segments() {
seg.verify_integrity()?;
seg.verify_postings()?;
}
let manifest = Manifest::load(&self.config.index_dir)?;
if manifest.checksum.is_none() {
return Err(IndexError::CorruptIndex(
"manifest has no integrity checksum; rebuild with `st index` to add one"
.to_string(),
));
}
if let Some(ref deletes_file) = manifest.overlay_deletes_file {
self::deletes_idx::read_deletes_idx(&self.config.index_dir, deletes_file).map_err(
|e| {
IndexError::CorruptIndex(format!(
"delete-set sidecar {deletes_file} verification failed: {e}"
))
},
)?;
}
Ok(())
}
pub fn stats(&self) -> IndexStats {
let snap = self.snapshot.load();
stats::compute_stats(
snap.as_ref(),
&self.config,
self.pending.uncommitted_count(),
)
}
pub fn search(
&self,
pattern: &str,
opts: &SearchOptions,
) -> Result<Vec<SearchMatch>, IndexError> {
crate::search::search(
self.snapshot(),
&self.config,
&self.canonical_root,
pattern,
opts,
)
}
pub(crate) fn search_with_content(
&self,
pattern: &str,
opts: &SearchOptions,
) -> Result<crate::search::SearchOutcome, IndexError> {
crate::search::search_with_content(
self.snapshot(),
&self.config,
&self.canonical_root,
pattern,
opts,
true,
)
}
pub fn search_grouped(
&self,
pattern: &str,
opts: &SearchOptions,
) -> Result<Vec<crate::FileMatches>, IndexError> {
Ok(crate::search::group_outcome(
self.search_with_content(pattern, opts)?,
))
}
pub fn snapshot(&self) -> Arc<IndexSnapshot> {
self.snapshot.load_full()
}
pub fn notify_change(&self, path: &Path) -> Result<(), IndexError> {
let rel = self.repo_relative_path(path)?;
self.pending.notify_change(&rel);
Ok(())
}
pub fn notify_delete(&self, path: &Path) -> Result<(), IndexError> {
let rel = self.repo_relative_path(path)?;
self.pending.notify_delete(&rel);
Ok(())
}
pub fn notify_change_immediate(&self, path: &Path) -> Result<(), IndexError> {
self.notify_change(path)?;
self.commit_batch()
}
pub fn maybe_compact(&self) -> Result<bool, IndexError> {
if self.pending.has_uncommitted() {
self.commit_batch()?;
}
let snapshot = self.snapshot();
if compact::plan(snapshot.as_ref(), &self.config).is_none() {
return Ok(false);
}
self.compact()?;
Ok(true)
}
pub fn compact(&self) -> Result<(), IndexError> {
if self.pending.has_uncommitted() {
self.commit_batch()?;
}
{
let snap = self.snapshot();
if compact::forced_plan(snap.as_ref(), &self.config).is_none() {
return Ok(());
}
}
let write_lock = helpers::acquire_writer_lock(&self.config.index_dir)?;
let snapshot = self.snapshot();
let Some(plan) = compact::forced_plan(snapshot.as_ref(), &self.config) else {
return Ok(());
};
self._dir_lock.unlock()?;
let rebuilt = match compact::compact_index(self.config.clone(), snapshot, plan, write_lock)
{
Ok(rebuilt) => rebuilt,
Err(err) => {
if let Err(e) = self._dir_lock.try_lock_shared() {
log::debug!(
"failed to re-acquire shared directory lock after compact error: {e}"
);
}
return Err(err);
}
};
self._dir_lock
.try_lock_shared()
.map_err(|_| IndexError::LockConflict(self.config.index_dir.clone()))?;
self.install_rebuilt_index(&rebuilt)?;
Ok(())
}
}
#[cfg(not(target_arch = "wasm32"))]
mod path_resolve;
#[cfg(all(not(target_arch = "wasm32"), feature = "symbols"))]
mod search_symbols;
#[cfg(not(target_arch = "wasm32"))]
mod update;
#[cfg(all(test, not(target_arch = "wasm32")))]
mod tests;