1use crate::{
2 error::{Error, Result},
3 ffi::{
4 C4IndexType, C4SliceResult, FLDict_Get, FLString, FLTrust, FLValueType, FLValue_AsDict,
5 FLValue_AsInt, FLValue_AsString, FLValue_FromData, FLValue_GetType, FLValue_IsInteger,
6 _FLDict,
7 },
8 value::{ValueRef, ValueRefArray},
9};
10use fallible_streaming_iterator::FallibleStreamingIterator;
11use serde_fleece::NonNullConst;
12
13pub enum IndexType {
15 ValueIndex,
17 FullTextIndex,
19 ArrayIndex,
21 PredictiveIndex,
23}
24
25#[derive(Default)]
26pub struct IndexOptions<'a> {
27 pub language: &'a str,
36 pub ignore_diacritics: bool,
39 pub disable_stemming: bool,
46 pub stop_words: Option<&'a [&'a str]>,
54}
55
56pub(crate) struct DbIndexesListIterator {
57 _enc_data: C4SliceResult,
58 array: ValueRefArray,
59 next_idx: u32,
60 cur_val: Option<IndexInfo>,
61}
62
63impl DbIndexesListIterator {
64 pub(crate) fn new(enc_data: C4SliceResult) -> Result<Self> {
65 let fvalue = unsafe { FLValue_FromData(enc_data.as_fl_slice(), FLTrust::kFLTrusted) };
66 let val = unsafe { ValueRef::new(fvalue) };
67 let array = match val {
68 ValueRef::Array(arr) => arr,
69 _ => {
70 return Err(Error::LogicError(
71 "db indexes are not fleece encoded array".into(),
72 ))
73 }
74 };
75
76 Ok(Self {
77 _enc_data: enc_data,
78 array,
79 next_idx: 0,
80 cur_val: None,
81 })
82 }
83}
84
85pub struct IndexInfo {
86 name: FLString,
87 type_: C4IndexType,
88 expr: FLString,
89}
90
91impl IndexInfo {
92 #[inline]
93 pub fn name_as_str(&self) -> Result<&str> {
94 self.name
95 .try_into()
96 .map_err(|_: std::str::Utf8Error| Error::InvalidUtf8)
97 }
98 #[inline]
99 pub fn type_(&self) -> C4IndexType {
100 self.type_
101 }
102 #[inline]
103 pub fn expr_as_str(&self) -> Result<&str> {
104 self.expr
105 .try_into()
106 .map_err(|_: std::str::Utf8Error| Error::InvalidUtf8)
107 }
108}
109
110impl TryFrom<NonNullConst<_FLDict>> for IndexInfo {
111 type Error = Error;
112
113 fn try_from(dict: NonNullConst<_FLDict>) -> Result<Self> {
114 fn get_str(dict: NonNullConst<_FLDict>, key: &str) -> Result<FLString> {
115 let s = unsafe { FLDict_Get(dict.as_ptr(), key.into()) };
116 let s = NonNullConst::new(s).ok_or_else(|| {
117 Error::LogicError(format!("No '{key}' key in index info dict").into())
118 })?;
119 if unsafe { FLValue_GetType(s.as_ptr()) } != FLValueType::kFLString {
120 return Err(Error::LogicError(
121 format!("Key '{key}' in index info dict has not string type").into(),
122 ));
123 }
124 let s = unsafe { FLValue_AsString(s.as_ptr()) };
125 let _s_utf8: &str = s.try_into().map_err(|_| Error::InvalidUtf8)?;
126 Ok(s)
127 }
128
129 let t = unsafe { FLDict_Get(dict.as_ptr(), "type".into()) };
130 let t = NonNullConst::new(t)
131 .ok_or_else(|| Error::LogicError("No 'type' key in index info dict".into()))?;
132 if !(unsafe { FLValue_GetType(t.as_ptr()) } == FLValueType::kFLNumber
133 && unsafe { FLValue_IsInteger(t.as_ptr()) })
134 {
135 return Err(Error::LogicError(
136 "Key 'type' in index info dict has not integer type".into(),
137 ));
138 }
139 let t: u32 = unsafe { FLValue_AsInt(t.as_ptr()) }
140 .try_into()
141 .map_err(|err| {
142 Error::LogicError(format!("Can convert index type to u32: {err}").into())
143 })?;
144
145 Ok(Self {
146 name: get_str(dict, "name")?,
147 type_: C4IndexType(t),
148 expr: get_str(dict, "expr")?,
149 })
150 }
151}
152
153impl FallibleStreamingIterator for DbIndexesListIterator {
154 type Error = Error;
155 type Item = IndexInfo;
156
157 fn advance(&mut self) -> Result<()> {
158 if self.next_idx < self.array.len() {
159 let val = unsafe { self.array.get_raw(self.next_idx) };
160 let val_type = unsafe { FLValue_GetType(val) };
161 if val_type != FLValueType::kFLDict {
162 return Err(Error::LogicError(
163 format!("Wrong index type, expect String, got {val_type:?}").into(),
164 ));
165 }
166 let dict = unsafe { FLValue_AsDict(val) };
167 let dict = NonNullConst::new(dict).ok_or_else(|| {
168 Error::LogicError("Can not convert one index info to Dict".into())
169 })?;
170 self.cur_val = Some(dict.try_into()?);
171 self.next_idx += 1;
172 } else {
173 self.cur_val = None;
174 }
175 Ok(())
176 }
177
178 #[inline]
179 fn get(&self) -> Option<&IndexInfo> {
180 self.cur_val.as_ref()
181 }
182}