Skip to main content

yuru_core/
lib.rs

1//! Core candidate indexing, fuzzy matching, ranking, and source-span data types
2//! for Yuru.
3//!
4//! This crate is intentionally language-neutral. Japanese, Korean, and Chinese
5//! phonetic keys are supplied by separate backend crates through
6//! [`LanguageBackend`].
7
8/// Candidate indexing and search-key construction.
9pub mod candidate;
10/// Search configuration knobs shared by the CLI and TUI.
11pub mod config;
12/// Internal parser and scorer for fzf-style extended queries.
13mod fzf_query;
14/// Fuzzy and exact matching backends.
15pub mod matcher;
16/// Unicode normalization helpers used before matching.
17pub mod normalize;
18/// Query expansion and key compatibility rules.
19pub mod query;
20/// Candidate ranking and top-result selection.
21pub mod rank;
22/// Counters collected while searching.
23pub mod stats;
24
25use std::fmt;
26use std::str::FromStr;
27
28pub use candidate::{
29    build_candidate, build_index, dedup_and_limit_keys, Candidate, MappedText, MappedTextBuilder,
30    SearchKey, SourceSpan,
31};
32pub use config::{KeyBudget, MatcherAlgo, QueryBudget, SearchConfig, Tiebreak};
33pub use matcher::{
34    match_positions, score_exact_text, score_text, ExactMatcher, GreedyMatcher, MatchPositions,
35    MatcherBackend, NucleoMatcher,
36};
37pub use query::{
38    base_query_variants, dedup_and_limit_variants, key_kind_allowed, PlainBackend, QueryVariant,
39};
40pub use rank::{search, search_with_stats, ScoredCandidate};
41pub use stats::SearchStats;
42
43#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
44/// Language backend selected for one search run.
45pub enum LangMode {
46    /// No language-specific phonetic expansion.
47    Plain,
48    /// Japanese kana and romaji expansion.
49    Japanese,
50    /// Korean Hangul romanization, initials, and keyboard expansion.
51    Korean,
52    /// Chinese pinyin and initials expansion.
53    Chinese,
54    /// Japanese, Korean, and Chinese expansion together.
55    All,
56}
57
58impl fmt::Display for LangMode {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        match self {
61            LangMode::Plain => f.write_str("plain"),
62            LangMode::Japanese => f.write_str("ja"),
63            LangMode::Korean => f.write_str("ko"),
64            LangMode::Chinese => f.write_str("zh"),
65            LangMode::All => f.write_str("all"),
66        }
67    }
68}
69
70impl FromStr for LangMode {
71    type Err = String;
72
73    fn from_str(value: &str) -> Result<Self, Self::Err> {
74        match value {
75            "plain" => Ok(LangMode::Plain),
76            "ja" | "japanese" => Ok(LangMode::Japanese),
77            "ko" | "korean" => Ok(LangMode::Korean),
78            "zh" | "chinese" => Ok(LangMode::Chinese),
79            "all" => Ok(LangMode::All),
80            other => Err(format!("unsupported language mode: {other}")),
81        }
82    }
83}
84
85#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
86/// Kind of indexed key attached to a candidate.
87pub enum KeyKind {
88    /// Original display text.
89    Original,
90    /// Normalized display text.
91    Normalized,
92    /// Japanese kana reading.
93    KanaReading,
94    /// Japanese romaji reading.
95    RomajiReading,
96    /// Chinese pinyin syllables separated by spaces.
97    PinyinFull,
98    /// Chinese pinyin joined without separators.
99    PinyinJoined,
100    /// Chinese pinyin initials.
101    PinyinInitials,
102    /// Korean romanized Hangul.
103    KoreanRomanized,
104    /// Korean Hangul initial consonants.
105    KoreanInitials,
106    /// Korean keyboard-layout spelling.
107    KoreanKeyboard,
108    /// User-learned alias key.
109    LearnedAlias,
110}
111
112#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
113/// Kind of query expansion produced before scoring.
114pub enum QueryVariantKind {
115    /// Query text exactly as entered.
116    Original,
117    /// Normalized query text.
118    Normalized,
119    /// Kana query text.
120    Kana,
121    /// Romaji query converted to kana.
122    RomajiToKana,
123    /// Pinyin query text.
124    Pinyin,
125    /// Initial-letter query text.
126    Initials,
127}
128
129/// Language-specific candidate and query expansion.
130pub trait LanguageBackend: Send + Sync {
131    /// Returns the language mode implemented by this backend.
132    fn mode(&self) -> LangMode;
133
134    /// Normalizes candidate display text before the base normalized key is added.
135    ///
136    /// Extended-query exact matching reuses this key instead of re-folding the
137    /// original text per candidate, and skips it entirely when it is only a case-folded
138    /// copy of the display text, so an override must stay equivalent to
139    /// [`normalize::normalize`] for case- and width-insensitive comparison.
140    fn normalize_candidate(&self, text: &str) -> String {
141        normalize::normalize(text)
142    }
143
144    /// Builds additional language-specific search keys for candidate text.
145    fn build_candidate_keys(&self, text: &str, budget: KeyBudget) -> Vec<SearchKey>;
146
147    /// Expands a user query into language-specific query variants.
148    fn expand_query(&self, query: &str, budget: QueryBudget) -> Vec<QueryVariant>;
149}