riffdb_types/
text_analyzer.rs1use unicode_segmentation::{UnicodeSegmentation, UnicodeWords};
8
9use crate::unicode_fold_v1;
10
11pub const TEXT_ANALYZER_V1_UNICODE_VERSION: (u64, u64, u64) = (17, 0, 0);
13pub const TOKENIZED_TEXT_PROVIDER_STATE_SCHEMA_HASH_V1: [u8; 32] = [
15 0x8e, 0x32, 0x7e, 0x0e, 0x53, 0xa3, 0x01, 0xe0, 0x3c, 0xe3, 0xbe, 0x04, 0xec, 0xe5, 0x7c, 0x3a,
16 0x94, 0x87, 0x83, 0x94, 0x9a, 0xc1, 0x01, 0x3d, 0x52, 0x16, 0x7f, 0x88, 0xd1, 0xe4, 0xd3, 0x0c,
17];
18
19#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
21#[repr(u8)]
22pub enum TextAnalyzerV1 {
23 KeywordV1 = 1,
25 StandardV1 = 2,
27}
28
29#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
34#[repr(u8)]
35pub enum TextSearchResultModelV1 {
36 BooleanV1 = 1,
38}
39
40impl TextSearchResultModelV1 {
41 #[must_use]
43 pub const fn as_str(self) -> &'static str {
44 match self {
45 Self::BooleanV1 => "boolean_v1",
46 }
47 }
48}
49
50impl TextAnalyzerV1 {
51 #[must_use]
53 pub const fn from_discriminant(value: u8) -> Option<Self> {
54 match value {
55 1 => Some(Self::KeywordV1),
56 2 => Some(Self::StandardV1),
57 _ => None,
58 }
59 }
60
61 #[must_use]
63 pub const fn as_str(self) -> &'static str {
64 match self {
65 Self::KeywordV1 => "keyword_v1",
66 Self::StandardV1 => "standard_v1",
67 }
68 }
69
70 #[must_use]
76 pub fn analyze(self, value: &str) -> TextAnalyzerTermsV1<'_> {
77 match self {
78 Self::KeywordV1 => TextAnalyzerTermsV1 {
79 inner: TextAnalyzerTermsInner::Keyword(Some(value)),
80 },
81 Self::StandardV1 => TextAnalyzerTermsV1 {
82 inner: TextAnalyzerTermsInner::Standard(value.unicode_words().enumerate()),
83 },
84 }
85 }
86}
87
88#[derive(Clone, Debug, Eq, PartialEq)]
90pub struct AnalyzedTermV1 {
91 position: usize,
92 term: String,
93}
94
95impl AnalyzedTermV1 {
96 #[must_use]
98 pub const fn position(&self) -> usize {
99 self.position
100 }
101
102 #[must_use]
104 pub fn term(&self) -> &str {
105 &self.term
106 }
107}
108
109#[derive(Debug)]
111pub struct TextAnalyzerTermsV1<'a> {
112 inner: TextAnalyzerTermsInner<'a>,
113}
114
115#[derive(Debug)]
116enum TextAnalyzerTermsInner<'a> {
117 Keyword(Option<&'a str>),
118 Standard(std::iter::Enumerate<UnicodeWords<'a>>),
119}
120
121impl Iterator for TextAnalyzerTermsV1<'_> {
122 type Item = AnalyzedTermV1;
123
124 fn next(&mut self) -> Option<Self::Item> {
125 match &mut self.inner {
126 TextAnalyzerTermsInner::Keyword(value) => value.take().map(|value| AnalyzedTermV1 {
127 position: 0,
128 term: value.to_owned(),
129 }),
130 TextAnalyzerTermsInner::Standard(words) => {
131 words.next().map(|(position, word)| AnalyzedTermV1 {
132 position,
133 term: unicode_fold_v1(word),
134 })
135 }
136 }
137 }
138}
139
140crate::inspect_owned_capacity_copy_v1!(TextAnalyzerV1);
142
143#[cfg(test)]
144mod tests {
145 use super::TEXT_ANALYZER_V1_UNICODE_VERSION;
146
147 #[test]
148 fn segmentation_and_folding_use_the_same_unicode_revision() {
149 assert_eq!(
150 unicode_segmentation::UNICODE_VERSION,
151 TEXT_ANALYZER_V1_UNICODE_VERSION,
152 "tokenizer tables moved; add a new analyzer identity instead"
153 );
154 assert_eq!(
155 crate::UNICODE_FOLD_V1_UNICODE_VERSION,
156 (17, 0, 0),
157 "fold and tokenizer revisions must remain aligned"
158 );
159 }
160}
161
162crate::inspect_owned_capacity_copy_v1!(TextSearchResultModelV1);