sift_query_core/policy/
validation.rs1use super::{QueryPolicy, is_name};
2use crate::{Codec, ParamError};
3use std::any::TypeId;
4use std::sync::Arc;
5
6impl QueryPolicy {
7 pub fn for_each_codec_binding<E: crate::SearchEntity>(
10 &self,
11 mut callback: impl FnMut(&'static crate::FieldMeta, &dyn Codec),
12 ) -> Result<(), ParamError> {
13 let key = TypeId::of::<E>();
14 let bindings = {
15 let mut cache = self
16 .codec_bindings
17 .lock()
18 .unwrap_or_else(std::sync::PoisonError::into_inner);
19 cache.get(&key).cloned()
20 };
21 let bindings = if let Some(bindings) = bindings {
22 bindings?
23 } else {
24 let result = self.build_codec_bindings::<E>();
25 self.codec_bindings
26 .lock()
27 .unwrap_or_else(std::sync::PoisonError::into_inner)
28 .insert(key, result.clone());
29 result?
30 };
31 for binding in bindings.iter() {
32 callback(binding.field, binding.codec.as_ref());
33 }
34 Ok(())
35 }
36
37 fn build_codec_bindings<E: crate::SearchEntity>(
38 &self,
39 ) -> Result<Arc<[super::CodecBinding]>, ParamError> {
40 self.validate_codecs::<E>()?;
41 let bindings = E::META
42 .fields
43 .iter()
44 .filter_map(|field| {
45 self.codec_arc_for::<E>(field)
46 .map(|codec| super::CodecBinding { field, codec })
47 })
48 .collect::<Vec<_>>();
49 Ok(Arc::from(bindings))
50 }
51
52 pub(crate) fn validate_operators<E: crate::SearchEntity>(&self) -> Result<(), ParamError> {
53 let key = TypeId::of::<E>();
54 {
55 let mut cache = self
56 .validated_operators
57 .lock()
58 .unwrap_or_else(std::sync::PoisonError::into_inner);
59 if let Some(result) = cache.get(&key) {
60 return result.clone();
61 }
62 }
63 let result = self.validate_operators_uncached::<E>();
64 self.validated_operators
65 .lock()
66 .unwrap_or_else(std::sync::PoisonError::into_inner)
67 .insert(key, result.clone());
68 result
69 }
70
71 fn validate_operators_uncached<E: crate::SearchEntity>(&self) -> Result<(), ParamError> {
72 for field in E::META.fields {
73 let mut names = std::collections::HashSet::new();
74 for name in E::custom_operator_names(field.id) {
75 let registration =
76 self.registered_operator(name)
77 .ok_or_else(|| ParamError::InvalidValue {
78 field: field.public_name.to_owned(),
79 reason: "custom operator is not registered".to_owned(),
80 })?;
81 if !names.insert(*name)
82 || registration.field_type != field.value_type
83 || !field.filterable
84 {
85 return Err(ParamError::InvalidValue {
86 field: field.public_name.to_owned(),
87 reason: "duplicate or incompatible custom operator whitelist".to_owned(),
88 });
89 }
90 }
91 }
92 Ok(())
93 }
94
95 pub(crate) fn validate_entity_names<E: crate::SearchEntity>(&self) -> Result<(), ParamError> {
96 let key = TypeId::of::<E>();
97 {
98 let mut cache = self
99 .validated_names
100 .lock()
101 .unwrap_or_else(std::sync::PoisonError::into_inner);
102 if let Some(result) = cache.get(&key) {
103 return result.clone();
104 }
105 }
106 let result = crate::protocol::parser::validate_entity_names::<E>(self);
107 self.validated_names
108 .lock()
109 .unwrap_or_else(std::sync::PoisonError::into_inner)
110 .insert(key, result.clone());
111 result
112 }
113
114 pub fn codec_for<E: crate::SearchEntity>(
115 &self,
116 field: &crate::FieldMeta,
117 ) -> Option<&dyn Codec> {
118 match E::codec_name(field.id) {
119 Some(name) => self
120 .codecs
121 .iter()
122 .find(|codec| codec.name() == name)
123 .map(Arc::as_ref),
124 None => self.codecs.find(field),
125 }
126 }
127
128 fn codec_arc_for<E: crate::SearchEntity>(
129 &self,
130 field: &crate::FieldMeta,
131 ) -> Option<Arc<dyn Codec>> {
132 match E::codec_name(field.id) {
133 Some(name) => self
134 .codecs
135 .iter()
136 .find(|codec| codec.name() == name)
137 .cloned(),
138 None => self.codecs.find_arc(field),
139 }
140 }
141
142 pub(crate) fn validate_codecs<E: crate::SearchEntity>(&self) -> Result<(), ParamError> {
143 let key = TypeId::of::<E>();
144 {
145 let mut cache = self
146 .validated_codecs
147 .lock()
148 .unwrap_or_else(std::sync::PoisonError::into_inner);
149 if let Some(result) = cache.get(&key) {
150 return result.clone();
151 }
152 }
153 let result = self.validate_codecs_uncached::<E>();
154 self.validated_codecs
155 .lock()
156 .unwrap_or_else(std::sync::PoisonError::into_inner)
157 .insert(key, result.clone());
158 result
159 }
160
161 fn validate_codecs_uncached<E: crate::SearchEntity>(&self) -> Result<(), ParamError> {
162 let mut names = std::collections::HashSet::new();
163 for codec in self.codecs.iter() {
164 if !is_name(codec.name()) || !names.insert(codec.name()) {
165 return Err(ParamError::ConflictingParameters(
166 "invalid or duplicate codec registration".to_owned(),
167 ));
168 }
169 }
170 for field in E::META.fields {
171 if E::codec_name(field.id).is_some() {
172 if self
173 .codec_for::<E>(field)
174 .is_none_or(|codec| !codec.supports(field))
175 {
176 return Err(ParamError::InvalidValue {
177 field: field.public_name.to_owned(),
178 reason: "named codec is missing or incompatible".to_owned(),
179 });
180 }
181 } else if self
182 .codecs
183 .iter()
184 .filter(|codec| codec.supports(field))
185 .count()
186 > 1
187 {
188 return Err(ParamError::ConflictingParameters(
189 "ambiguous codec binding".to_owned(),
190 ));
191 }
192 }
193 Ok(())
194 }
195}