1use std::error::Error;
4use std::fmt;
5
6pub const EXACT_TEXT_PROFILE_VERSION_V1: u16 = 1;
8pub const EXACT_TEXT_PROVIDER_STATE_SCHEMA_HASH_V1: [u8; 32] = [
10 0x9b, 0x8a, 0x2f, 0x7e, 0xd9, 0xfd, 0xf1, 0xc5, 0xd9, 0x68, 0x8f, 0xbd, 0x39, 0xad, 0x3d, 0x86,
11 0x20, 0x70, 0xa7, 0x17, 0x74, 0xdc, 0x77, 0x4d, 0x0c, 0xe0, 0x0e, 0xdb, 0x69, 0x1a, 0x1e, 0x40,
12];
13pub const EXACT_TEXT_PROVIDER_STATE_SCHEMA_HASH_V2: [u8; 32] = [
15 0x58, 0xa4, 0x1e, 0x51, 0x2c, 0x04, 0xef, 0x93, 0xe7, 0xc1, 0xfd, 0xb2, 0xbf, 0xfb, 0x2d, 0x44,
16 0xbb, 0x54, 0xfc, 0x76, 0xac, 0x61, 0xab, 0x8e, 0x47, 0x93, 0xf1, 0xf0, 0x1d, 0x2b, 0x2f, 0x8a,
17];
18pub const EXACT_TEXT_PROVIDER_STATE_SCHEMA_HASH_V3: [u8; 32] = [
20 0xda, 0x13, 0x39, 0xc5, 0x30, 0x4c, 0x8d, 0x9d, 0x90, 0x58, 0x74, 0x74, 0x1d, 0xf8, 0xd8, 0xa2,
21 0x9a, 0xe8, 0xa6, 0x14, 0xbd, 0x11, 0xe5, 0x53, 0xc7, 0xd9, 0xfc, 0xd0, 0x06, 0x8f, 0xcc, 0x4b,
22];
23pub const EXACT_PREDICATE_PROVIDER_STATE_SCHEMA_HASH_V4: [u8; 32] = [
25 0x60, 0xe6, 0x99, 0xf7, 0x7d, 0x47, 0x2b, 0xb0, 0xc1, 0xa7, 0xc7, 0x78, 0x48, 0x8a, 0x6e, 0xf8,
26 0x9e, 0xe7, 0x7e, 0x04, 0x83, 0xdc, 0x62, 0x5e, 0x85, 0xdd, 0xc4, 0x0b, 0x88, 0xc2, 0x0a, 0x28,
27];
28pub const EXACT_PREDICATE_PROVIDER_STATE_SCHEMA_HASH_V5: [u8; 32] = [
30 0x7e, 0x64, 0x5e, 0x51, 0x89, 0x2e, 0x83, 0xca, 0x4e, 0x05, 0xaa, 0x24, 0xa4, 0x7a, 0xa9, 0x21,
31 0xc4, 0xc5, 0x86, 0xbc, 0xb9, 0x9d, 0x2a, 0x7e, 0x0b, 0x4a, 0x0f, 0x13, 0x39, 0x47, 0xc0, 0xa1,
32];
33pub const MAX_EXACT_TEXT_VALUE_BYTES_V1: usize = 256;
35pub const MAX_EXACT_TEXT_OUTPUT_ROW_BYTES_V1: usize = 64 * 1024;
37pub const MAX_EXACT_TEXT_NEEDLE_BYTES_V1: usize = 64;
39pub const MAX_EXACT_TEXT_ROWS_PER_PARTITION_V1: usize = 4_096;
41pub const MAX_EXACT_TEXT_TERMS_PER_ROW_V1: usize =
43 MAX_EXACT_TEXT_VALUE_BYTES_V1 * (MAX_EXACT_TEXT_VALUE_BYTES_V1 + 1) / 2;
44
45#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
47#[repr(u8)]
48pub enum ExactTextProfileV1 {
49 BinaryUtf8V1 = 1,
51 UnicodeFoldV1 = 2,
56}
57
58impl ExactTextProfileV1 {
59 #[must_use]
61 pub const fn from_discriminant(value: u8) -> Option<Self> {
62 match value {
63 1 => Some(Self::BinaryUtf8V1),
64 2 => Some(Self::UnicodeFoldV1),
65 _ => None,
66 }
67 }
68
69 #[must_use]
74 pub fn matched_form(self, value: &str) -> String {
75 match self {
76 Self::BinaryUtf8V1 => value.to_owned(),
77 Self::UnicodeFoldV1 => crate::unicode_fold_v1(value),
78 }
79 }
80}
81
82#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
84#[repr(u8)]
85pub enum ExactTextOperatorV1 {
86 Equals = 1,
88 StartsWith = 2,
90 EndsWith = 3,
92 Contains = 4,
94}
95
96#[derive(Clone, Copy, Debug, Eq, PartialEq)]
98#[repr(u8)]
99pub enum ExactTextOrderV1 {
100 ValueAscEntityKey = 1,
102 ValueDescEntityKey = 2,
104}
105
106#[derive(Clone, Copy, Debug, Eq, PartialEq)]
108pub enum ExactTextFieldValueV1<'a> {
109 Missing,
111 Null,
113 Value(&'a str),
115}
116
117#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
119pub struct ExactTextNeedleV1(String);
120
121impl ExactTextNeedleV1 {
122 #[must_use]
124 pub fn as_str(&self) -> &str {
125 &self.0
126 }
127
128 #[must_use]
130 pub fn to_canonical_bytes(&self) -> Vec<u8> {
131 let bytes = self.0.as_bytes();
132 let mut output = Vec::with_capacity(8 + bytes.len());
133 output.extend_from_slice(b"RXTN");
134 output.extend_from_slice(&EXACT_TEXT_PROFILE_VERSION_V1.to_be_bytes());
135 output.extend_from_slice(&(bytes.len() as u16).to_be_bytes());
136 output.extend_from_slice(bytes);
137 output
138 }
139}
140
141impl ExactTextProfileV1 {
142 pub fn bind_needle(self, value: &str) -> Result<ExactTextNeedleV1, ExactTextBindErrorV1> {
144 if value.is_empty() {
145 return Err(ExactTextBindErrorV1::EmptyNeedle);
146 }
147 let bound = self.matched_form(value);
151 if bound.len() > MAX_EXACT_TEXT_NEEDLE_BYTES_V1 {
152 return Err(ExactTextBindErrorV1::NeedleTooLong);
153 }
154 if bound.is_empty() {
157 return Err(ExactTextBindErrorV1::EmptyNeedle);
158 }
159 Ok(ExactTextNeedleV1(bound))
160 }
161
162 #[must_use]
164 pub fn matches(
165 self,
166 operator: ExactTextOperatorV1,
167 value: ExactTextFieldValueV1<'_>,
168 needle: &ExactTextNeedleV1,
169 ) -> bool {
170 let ExactTextFieldValueV1::Value(value) = value else {
171 return false;
172 };
173 match operator {
174 ExactTextOperatorV1::Equals => value == needle.as_str(),
175 ExactTextOperatorV1::StartsWith => value.starts_with(needle.as_str()),
176 ExactTextOperatorV1::EndsWith => value.ends_with(needle.as_str()),
177 ExactTextOperatorV1::Contains => value.contains(needle.as_str()),
178 }
179 }
180}
181
182#[derive(Clone, Copy, Debug, Eq, PartialEq)]
184pub enum ExactTextBindErrorV1 {
185 EmptyNeedle,
187 NeedleTooLong,
189}
190
191impl fmt::Display for ExactTextBindErrorV1 {
192 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
193 formatter.write_str(match self {
194 Self::EmptyNeedle => "exact text needle must not be empty",
195 Self::NeedleTooLong => "exact text needle exceeds its compiled bound",
196 })
197 }
198}
199
200impl Error for ExactTextBindErrorV1 {}
201
202crate::inspect_owned_capacity_copy_v1!(ExactTextOperatorV1);
204
205crate::inspect_owned_capacity_copy_v1!(ExactTextOrderV1);
206
207crate::inspect_owned_capacity_copy_v1!(ExactTextProfileV1);