use std::cmp::Ordering;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
mod r#const;
mod k_merge;
mod matcher;
mod prefilter;
mod smith_waterman;
mod sort;
use r#const::*;
pub use k_merge::k_merge_matches;
pub use matcher::Matcher;
pub use sort::radix_sort_matches;
pub mod iter {
pub use crate::matcher::{FuzzyMatch, FuzzyMatchExt, FuzzyMatchIndices};
}
pub fn match_list<S1: AsRef<str>, S2: AsRef<str>>(
needle: S1,
haystacks: &[S2],
config: &Config,
) -> Vec<Match> {
Matcher::new(needle.as_ref(), config).match_list(haystacks)
}
pub fn match_list_indices<S1: AsRef<str>, S2: AsRef<str>>(
needle: S1,
haystacks: &[S2],
config: &Config,
) -> Vec<MatchIndices> {
Matcher::new(needle.as_ref(), config).match_list_indices(haystacks)
}
pub fn match_list_parallel<S1: AsRef<str>, S2: AsRef<str> + Sync>(
needle: S1,
haystacks: &[S2],
config: &Config,
threads: usize,
) -> Vec<Match> {
Matcher::new(needle.as_ref(), config).match_list_parallel(haystacks, threads)
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Match {
pub score: u16,
pub index: u32,
pub exact: bool,
#[cfg(feature = "match_end_col")]
pub end_col: u16,
}
impl Match {
pub fn from_index(index: usize) -> Self {
Self {
score: 0,
index: index as u32,
exact: false,
#[cfg(feature = "match_end_col")]
end_col: 0,
}
}
}
impl PartialOrd for Match {
fn partial_cmp(&self, other: &Match) -> Option<Ordering> {
Some(std::cmp::Ord::cmp(self, other))
}
}
impl Ord for Match {
fn cmp(&self, other: &Self) -> Ordering {
(self.score as u64)
.cmp(&(other.score as u64))
.reverse()
.then_with(|| self.index.cmp(&other.index))
}
}
impl PartialEq for Match {
fn eq(&self, other: &Self) -> bool {
self.score == other.score && self.index == other.index
}
}
impl Eq for Match {}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MatchIndices {
pub score: u16,
pub index: u32,
pub exact: bool,
pub indices: Vec<usize>,
}
impl MatchIndices {
pub fn from_index(index: usize) -> Self {
Self {
score: 0,
index: index as u32,
exact: false,
indices: vec![],
}
}
}
impl PartialOrd for MatchIndices {
fn partial_cmp(&self, other: &MatchIndices) -> Option<Ordering> {
Some(std::cmp::Ord::cmp(self, other))
}
}
impl Ord for MatchIndices {
fn cmp(&self, other: &Self) -> Ordering {
(self.score as u64)
.cmp(&(other.score as u64))
.reverse()
.then_with(|| self.index.cmp(&other.index))
}
}
impl PartialEq for MatchIndices {
fn eq(&self, other: &Self) -> bool {
self.score == other.score && self.index == other.index
}
}
impl Eq for MatchIndices {}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct Config {
pub max_typos: Option<u16>,
#[cfg_attr(feature = "serde", serde(default))]
pub casing: CaseMatching,
#[cfg_attr(feature = "serde", serde(default))]
pub unicode: UnicodeMatching,
pub sort: bool,
pub scoring: Scoring,
}
impl Default for Config {
fn default() -> Self {
Config {
max_typos: Some(0),
casing: CaseMatching::Ignore,
unicode: UnicodeMatching::Smart,
sort: true,
scoring: Scoring::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CaseMatching {
#[default]
Ignore,
Smart,
Respect,
}
impl CaseMatching {
#[inline(always)]
pub(crate) fn respects_case_for(self, needle: &str) -> bool {
match self {
CaseMatching::Ignore => false,
CaseMatching::Smart => needle.chars().any(char::is_uppercase),
CaseMatching::Respect => true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum UnicodeMatching {
Ignore,
#[default]
Smart,
Always,
}
impl UnicodeMatching {
#[inline(always)]
pub(crate) fn respects_unicode_for(self, needle: &str) -> bool {
match self {
UnicodeMatching::Ignore => false,
UnicodeMatching::Smart => !needle.is_ascii(),
UnicodeMatching::Always => true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct Scoring {
pub match_score: u16,
pub mismatch_penalty: u16,
pub gap_open_penalty: u16,
pub gap_extend_penalty: u16,
pub prefix_bonus: u16,
pub capitalization_bonus: u16,
pub matching_case_bonus: u16,
pub exact_match_bonus: u16,
pub delimiter_bonus: u16,
}
impl Default for Scoring {
fn default() -> Self {
Scoring {
match_score: MATCH_SCORE,
mismatch_penalty: MISMATCH_PENALTY,
gap_open_penalty: GAP_OPEN_PENALTY,
gap_extend_penalty: GAP_EXTEND_PENALTY,
prefix_bonus: PREFIX_BONUS,
capitalization_bonus: CAPITALIZATION_BONUS,
matching_case_bonus: MATCHING_CASE_BONUS,
exact_match_bonus: EXACT_MATCH_BONUS,
delimiter_bonus: DELIMITER_BONUS,
}
}
}