1use std::{
2 borrow::Cow,
3 fmt::{self, Display, Formatter},
4};
5
6use super::{
7 apply::InternalError,
8 context::{Context, JsonPath},
9 QueryKey,
10};
11use derive_getters::Getters;
12use derive_more::Constructor;
13use regex::Regex;
14use serde_json::{Number, Value};
15use thiserror::Error;
16
17#[derive(Debug, Clone, Error)]
18pub enum Error<'a> {
19 #[error("types '{value_type}' and '{operation_value_type}' are not comparable at '{context}'")]
20 IncomparableTypes {
21 value_type: String,
22 operation_value_type: String,
23 context: JsonPath<'a>,
24 },
25 #[error(
26 "operation '{operation_type}' is not compatible with value type '{value_type}' at '{context}'"
27 )]
28 IncompatibleOperation {
29 value_type: String,
30 operation_type: String,
31 context: JsonPath<'a>,
32 },
33 #[error("cannot conver number '{0}' to f64 at {1}")]
34 NumberConversionError(Number, JsonPath<'a>),
35 #[error("{error} while processing arguments at '{context}'")]
36 InsideArguments {
37 error: Box<Self>,
38 context: JsonPath<'a>,
39 },
40 #[error("{0}")]
41 InternalError(InternalError<'a>),
42}
43
44impl<'a> From<InternalError<'a>> for Error<'a> {
45 fn from(internal_error: InternalError<'a>) -> Self {
46 Self::InternalError(internal_error)
47 }
48}
49
50pub trait ValueType {
52 fn value_type(&self) -> String;
53}
54
55impl ValueType for Value {
56 fn value_type(&self) -> String {
57 match self {
58 Value::String(_) => "string".to_string(),
59 Value::Number(_) => "number".to_string(),
60 Value::Bool(_) => "bool".to_string(),
61 Value::Null => "null".to_string(),
62 Value::Array(_) => "array".to_string(),
63 Value::Object(_) => "object".to_string(),
64 }
65 }
66}
67
68impl ValueType for QueryArgumentValue {
69 fn value_type(&self) -> String {
70 match self {
71 QueryArgumentValue::String(_) => "string".to_string(),
72 QueryArgumentValue::Number(_) => "number".to_string(),
73 QueryArgumentValue::Bool(_) => "bool".to_string(),
74 QueryArgumentValue::Null => "null".to_string(),
75 }
76 }
77}
78
79impl ValueType for f64 {
80 fn value_type(&self) -> String {
81 "number".to_string()
82 }
83}
84
85impl ValueType for Regex {
86 fn value_type(&self) -> String {
87 "regex".to_string()
88 }
89}
90
91impl ValueType for QueryArgumentOperation {
92 fn value_type(&self) -> String {
93 match self {
94 QueryArgumentOperation::Equal(value) => value.value_type(),
95 QueryArgumentOperation::NotEqual(value) => value.value_type(),
96 QueryArgumentOperation::Greater(value) => value.value_type(),
97 QueryArgumentOperation::GreaterEqual(value) => value.value_type(),
98 QueryArgumentOperation::Less(value) => value.value_type(),
99 QueryArgumentOperation::LessEqual(value) => value.value_type(),
100 QueryArgumentOperation::Match(value) => value.value_type(),
101 QueryArgumentOperation::NotMatch(value) => value.value_type(),
102 }
103 }
104}
105
106pub trait OperationType {
107 fn operation_type(&self) -> String;
108}
109
110impl OperationType for QueryArgumentOperation {
111 fn operation_type(&self) -> String {
112 match self {
113 QueryArgumentOperation::Equal(_) => "=".to_string(),
114 QueryArgumentOperation::NotEqual(_) => "!=".to_string(),
115 QueryArgumentOperation::Greater(_) => ">".to_string(),
116 QueryArgumentOperation::GreaterEqual(_) => ">=".to_string(),
117 QueryArgumentOperation::Less(_) => "<".to_string(),
118 QueryArgumentOperation::LessEqual(_) => "<=".to_string(),
119 QueryArgumentOperation::Match(_) => "~".to_string(),
120 QueryArgumentOperation::NotMatch(_) => "!~".to_string(),
121 }
122 }
123}
124
125#[derive(Debug, Clone)]
126pub enum QueryArgumentValue {
127 String(String),
128 Number(f64),
129 Bool(bool),
130 Null,
131}
132
133impl Display for QueryArgumentValue {
134 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
135 match self {
136 QueryArgumentValue::String(value) => write!(f, "\"{value}\""),
137 QueryArgumentValue::Number(value) => write!(f, "{value}"),
138 QueryArgumentValue::Bool(value) => write!(f, "{value}"),
139 QueryArgumentValue::Null => write!(f, "null"),
140 }
141 }
142}
143
144#[derive(Debug, Clone)]
145pub enum QueryArgumentOperation {
146 Equal(QueryArgumentValue),
147 NotEqual(QueryArgumentValue),
148 Greater(f64),
149 GreaterEqual(f64),
150 Less(f64),
151 LessEqual(f64),
152 Match(Regex),
153 NotMatch(Regex),
154}
155
156impl Display for QueryArgumentOperation {
157 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
158 match self {
160 QueryArgumentOperation::Equal(value) => write!(f, "={value}"),
161 QueryArgumentOperation::NotEqual(value) => write!(f, "!={value}"),
162 QueryArgumentOperation::Greater(value) => write!(f, ">{value}"),
163 QueryArgumentOperation::GreaterEqual(value) => write!(f, ">={value}"),
164 QueryArgumentOperation::Less(value) => write!(f, "<{value}"),
165 QueryArgumentOperation::LessEqual(value) => write!(f, "<={value}"),
166 QueryArgumentOperation::Match(regex) => write!(f, "~\"{regex}\""),
167 QueryArgumentOperation::NotMatch(regex) => write!(f, "!~\"{regex}\""),
168 }
169 }
170}
171
172impl<'a> QueryArgumentOperation {
173 fn satisfies(&self, value: &Value, context: &Context<'a>) -> Result<bool, Error<'a>> {
174 match self {
175 QueryArgumentOperation::Equal(operation_value) => {
176 self.satisfies_equal(operation_value, value, context)
177 }
178 QueryArgumentOperation::NotEqual(operation_value) => self
179 .satisfies_equal(operation_value, value, context)
180 .map(|result| !result),
181 QueryArgumentOperation::Greater(operation_value) => {
182 self.satisfies_greater(*operation_value, value, context)
183 }
184 QueryArgumentOperation::GreaterEqual(operation_value) => self
185 .satisfies_less(*operation_value, value, context)
186 .map(|result| !result),
187 QueryArgumentOperation::Less(operation_value) => {
188 self.satisfies_less(*operation_value, value, context)
189 }
190 QueryArgumentOperation::LessEqual(operation_value) => self
191 .satisfies_greater(*operation_value, value, context)
192 .map(|result| !result),
193 QueryArgumentOperation::Match(operation_value) => {
194 self.satisfies_match(operation_value, value, context)
195 }
196 QueryArgumentOperation::NotMatch(operation_value) => self
197 .satisfies_match(operation_value, value, context)
198 .map(|result| !result),
199 }
200 }
201
202 fn satisfies_op_array<F>(array: &[Value], satisfies_op: F, context: &Context<'a>) -> bool
203 where
204 F: Fn(&Value, &Context<'a>) -> Result<bool, Error<'a>>,
205 {
206 array
207 .iter()
208 .enumerate()
209 .map(|(index, item)| (context.push_index(index), item))
210 .map(|(item_context, item)| satisfies_op(item, &item_context))
211 .any(|result| {
212 result
213 .map_err(|error| {
214 log::warn!("{error}");
215 })
216 .unwrap_or(false)
217 })
218 }
219
220 fn satisfies_equal(
221 &self,
222 operation_value: &QueryArgumentValue,
223 value: &Value,
224 context: &Context<'a>,
225 ) -> Result<bool, Error<'a>> {
226 match (operation_value, value) {
227 (QueryArgumentValue::String(operation_value), Value::String(value)) => {
228 Ok(operation_value == value)
229 }
230 (QueryArgumentValue::Number(operation_value), Value::Number(value)) => {
231 value
232 .as_f64()
233 .map(|value| value == *operation_value)
234 .ok_or(Error::NumberConversionError(
236 value.clone(),
237 context.path().clone(),
238 ))
239 }
240 (QueryArgumentValue::Bool(operation_value), Value::Bool(value)) => {
241 Ok(operation_value == value)
242 }
243 (QueryArgumentValue::Null, Value::Null) => Ok(true),
244 (QueryArgumentValue::Null, _) => Ok(false),
245 (_, Value::Null) => Ok(false),
246 (_, Value::Array(array)) => {
247 let satisfies_op = |item: &Value, context: &Context<'a>| {
248 self.satisfies_equal(operation_value, item, context)
249 };
250 Ok(Self::satisfies_op_array(array, satisfies_op, context))
251 }
252 _ => Err(self.incomparable_types_error(operation_value, value, context)),
253 }
254 }
255 fn satisfies_greater(
256 &self,
257 operation_value: f64,
258 value: &Value,
259 context: &Context<'a>,
260 ) -> Result<bool, Error<'a>> {
261 match value {
262 Value::Number(value) => value.as_f64().map(|value| value > operation_value).ok_or(
263 Error::NumberConversionError(value.clone(), context.path().clone()),
264 ),
265 Value::Array(array) => {
266 let satisfies_op = |item: &Value, context: &Context<'a>| {
267 self.satisfies_greater(operation_value, item, context)
268 };
269 Ok(Self::satisfies_op_array(array, satisfies_op, context))
270 }
271 _ => Err(self.incompatible_operation_error(value, context)),
272 }
273 }
274
275 fn satisfies_less(
276 &self,
277 operation_value: f64,
278 value: &Value,
279 context: &Context<'a>,
280 ) -> Result<bool, Error<'a>> {
281 match value {
282 Value::Number(value) => value.as_f64().map(|value| value < operation_value).ok_or(
283 Error::NumberConversionError(value.clone(), context.path().clone()),
284 ),
285 Value::Array(array) => {
286 let satisfies_op = |item: &Value, context: &Context<'a>| {
287 self.satisfies_less(operation_value, item, context)
288 };
289 Ok(Self::satisfies_op_array(array, satisfies_op, context))
290 }
291 _ => Err(self.incompatible_operation_error(value, context)),
292 }
293 }
294
295 fn satisfies_match(
296 &self,
297 operation_value: &Regex,
298 value: &Value,
299 context: &Context<'a>,
300 ) -> Result<bool, Error<'a>> {
301 match value {
302 Value::String(value) => Ok(operation_value.is_match(value)),
303 Value::Array(array) => {
304 let satisfies_op = |item: &Value, context: &Context<'a>| {
305 self.satisfies_match(operation_value, item, context)
306 };
307 Ok(Self::satisfies_op_array(array, satisfies_op, context))
308 }
309 _ => Err(self.incompatible_operation_error(value, context)),
310 }
311 }
312
313 fn incomparable_types_error<T: ValueType, U: ValueType>(
314 &self,
315 operation_value: &T,
316 value: &U,
317 context: &Context<'a>,
318 ) -> Error<'a> {
319 let value_type = value.value_type();
320 let operation_value_type = operation_value.value_type();
321 Error::IncomparableTypes {
322 value_type,
323 operation_value_type,
324 context: context.path().clone(),
325 }
326 }
327 fn incompatible_operation_error<T: ValueType>(
328 &self,
329 value: &T,
330 context: &Context<'a>,
331 ) -> Error<'a> {
332 let value_type = value.value_type();
333 let operation_type = self.operation_type();
334 Error::IncompatibleOperation {
335 value_type,
336 operation_type,
337 context: context.path().clone(),
338 }
339 }
340}
341
342#[derive(Debug, Clone, Constructor, Getters)]
343pub struct QueryArgument {
344 key: QueryKey,
345 operation: QueryArgumentOperation,
346}
347
348impl Display for QueryArgument {
349 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
350 let key = self.key();
351 let operation = self.operation();
352 write!(f, "{key}{operation}")
353 }
354}
355
356#[derive(Debug, Clone, Constructor, Default)]
357pub struct QueryArguments(pub Vec<QueryArgument>);
358
359impl Display for QueryArguments {
360 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
361 let arguments = self
362 .0
363 .iter()
364 .map(ToString::to_string)
365 .collect::<Vec<_>>()
366 .join(", ");
367 arguments.fmt(f)
368 }
369}
370
371impl QueryArguments {
372 pub fn satisfies(&self, value: &Value, context: &Context) -> bool {
374 self.0.iter().all(|argument| {
375 argument
376 .satisfies(value, context)
377 .map_err(|error| {
378 let argument_error = Error::InsideArguments {
379 error: Box::new(error),
381 context: context.path().clone(),
382 };
383 log::warn!("{argument_error}");
384 })
385 .unwrap_or(false)
386 })
387 }
388}
389
390impl<'a> QueryArgument {
391 const DEFAULT_INSPECTED_VALUE: Cow<'static, Value> = Cow::Owned(Value::Null);
392
393 fn satisfies(&'a self, value: &Value, context: &Context<'a>) -> Result<bool, Error<'a>> {
394 let argument_key = self.key();
395
396 let inspected_value = match argument_key.inspect(value, context) {
397 Ok(value) => value,
398 Err(error @ InternalError::KeyNotFound(_)) => {
401 log::info!("{error}, using null value");
402 Self::DEFAULT_INSPECTED_VALUE
403 }
404 Err(error) => return Err(error.into()),
405 };
406
407 let inspected_context = context.push_query_key(argument_key);
408 self.operation
409 .satisfies(&inspected_value, &inspected_context)
410 }
411}