#![warn(missing_docs)]
mod cursor;
mod events;
mod ext_groups;
mod extract;
mod frn_table;
mod indexer;
mod meta;
#[cfg(windows)]
mod mft;
mod ocr;
mod ocr_queue;
mod ocr_worker;
mod reconcile;
mod roots;
mod searcher;
mod status;
mod tokenizer;
mod updater;
#[cfg(windows)]
mod usn;
mod usn_translate;
mod volume;
mod watch;
pub use events::{Debouncer, PendingChange, PendingOp, WatchEvent};
pub use ext_groups::by_name as ext_group_by_name;
pub use indexer::{
IndexProgress, IndexStats, rebuild_index, rebuild_index_with_progress, remove_dir_all_retrying,
};
pub use meta::registered_roots;
pub use ocr::is_available;
pub use ocr_queue::OcrQueue;
pub use ocr_worker::{DEFAULT_WORKERS, OcrDrainStats, OcrPipeline, drain_ocr_queue};
pub use reconcile::{ReconcileStats, reconcile, reconcile_orphans};
pub use roots::{
AddRootStats, RemoveRootStats, add_root, add_root_with_progress, rebuild_root,
rebuild_root_with_progress, remove_root,
};
pub use searcher::{PreviewHit, SearchHit, Searcher, SortMode, normalize_ranges};
pub use status::{IndexStatus, index_status};
pub use updater::{BatchOutcome, IndexUpdater};
pub use volume::ntfs_fast_path_available;
pub use watch::{
EventSource, NotifyEventSource, WatchGuard, WatchProgress, run_watch, watch_roots_auto,
};
use tantivy::schema::{
FAST, IndexRecordOption, STORED, STRING, Schema, SchemaBuilder, TextFieldIndexing, TextOptions,
};
pub(crate) struct Fields {
pub path: tantivy::schema::Field,
pub name: tantivy::schema::Field,
pub ext: tantivy::schema::Field,
pub content: tantivy::schema::Field,
pub mtime: tantivy::schema::Field,
pub size: tantivy::schema::Field,
pub kind: tantivy::schema::Field,
}
pub(crate) fn build_schema() -> (Schema, Fields) {
let mut builder: SchemaBuilder = Schema::builder();
let jieba_indexing = TextFieldIndexing::default()
.set_tokenizer("jieba")
.set_index_option(IndexRecordOption::WithFreqsAndPositions);
let jieba_text = TextOptions::default()
.set_indexing_options(jieba_indexing)
.set_stored();
let fields = Fields {
path: builder.add_text_field("path", STRING | STORED),
name: builder.add_text_field("name", jieba_text.clone()),
ext: builder.add_text_field("ext", STRING | STORED),
content: builder.add_text_field("content", jieba_text),
mtime: builder.add_i64_field("mtime", STORED | FAST),
size: builder.add_u64_field("size", STORED | FAST),
kind: builder.add_text_field("kind", STRING | STORED),
};
(builder.build(), fields)
}
pub(crate) fn register_tokenizers(index: &tantivy::Index) {
index
.tokenizers()
.register("jieba", tokenizer::MixedTokenizer::new());
}
pub fn display_path(path: &str) -> String {
if let Some(rest) = path.strip_prefix(r"\\?\UNC\") {
format!(r"\\{rest}")
} else if let Some(rest) = path.strip_prefix(r"\\?\") {
rest.to_string()
} else {
path.to_string()
}
}
#[cfg(test)]
mod display_path_tests {
use super::display_path;
#[test]
fn strips_plain_verbatim_prefix() {
assert_eq!(display_path(r"\\?\E:\BLOG\post.md"), r"E:\BLOG\post.md");
}
#[test]
fn strips_unc_verbatim_prefix() {
assert_eq!(
display_path(r"\\?\UNC\server\share\file.txt"),
r"\\server\share\file.txt"
);
}
#[test]
fn leaves_normal_windows_path_untouched() {
assert_eq!(display_path(r"E:\BLOG\post.md"), r"E:\BLOG\post.md");
}
#[test]
fn leaves_unix_style_path_untouched() {
assert_eq!(display_path("/home/user/file.txt"), "/home/user/file.txt");
}
#[test]
fn leaves_empty_string_untouched() {
assert_eq!(display_path(""), "");
}
}