qubit_metadata/schema/
filter_validation.rs1use qubit_datatype::DataType;
13use qubit_value::Value;
14
15use super::metadata_field::MetadataField;
16use super::metadata_schema::MetadataSchema;
17use crate::{Condition, MetadataError, MetadataFilter, MetadataResult};
18
19impl MetadataSchema {
20 pub fn validate_filter(&self, filter: &MetadataFilter) -> MetadataResult<()> {
28 filter.visit_conditions(|condition| self.validate_condition(condition))
29 }
30
31 fn validate_condition(&self, condition: &Condition) -> MetadataResult<()> {
33 match condition {
34 Condition::Equal { key, value } => self.validate_value_condition(key, "eq", value),
35 Condition::NotEqual { key, value } => self.validate_value_condition(key, "ne", value),
36 Condition::Less { key, value } => self.validate_range_condition(key, "lt", value),
37 Condition::LessEqual { key, value } => self.validate_range_condition(key, "le", value),
38 Condition::Greater { key, value } => self.validate_range_condition(key, "gt", value),
39 Condition::GreaterEqual { key, value } => {
40 self.validate_range_condition(key, "ge", value)
41 }
42 Condition::In { key, values } => {
43 for value in values {
44 self.validate_value_condition(key, "in_set", value)?;
45 }
46 Ok(())
47 }
48 Condition::NotIn { key, values } => {
49 for value in values {
50 self.validate_value_condition(key, "not_in_set", value)?;
51 }
52 Ok(())
53 }
54 Condition::Exists { key } | Condition::NotExists { key } => {
55 self.require_field(key)?;
56 Ok(())
57 }
58 }
59 }
60
61 fn validate_value_condition(
63 &self,
64 key: &str,
65 operator: &'static str,
66 value: &Value,
67 ) -> MetadataResult<()> {
68 let field = self.require_field(key)?;
69 if value_matches_field_type(value, field.data_type()) {
70 return Ok(());
71 }
72 Err(MetadataError::InvalidFilterOperator {
73 key: key.to_string(),
74 operator,
75 data_type: field.data_type(),
76 message: format!(
77 "filter value type {} is not compatible with field type {}",
78 value.data_type(),
79 field.data_type()
80 ),
81 })
82 }
83
84 fn validate_range_condition(
86 &self,
87 key: &str,
88 operator: &'static str,
89 value: &Value,
90 ) -> MetadataResult<()> {
91 let field = self.require_field(key)?;
92 if !is_range_comparable_type(field.data_type()) {
93 return Err(MetadataError::InvalidFilterOperator {
94 key: key.to_string(),
95 operator,
96 data_type: field.data_type(),
97 message: "range operators require a numeric or string field".to_string(),
98 });
99 }
100 if value_matches_field_type(value, field.data_type()) {
101 return Ok(());
102 }
103 Err(MetadataError::InvalidFilterOperator {
104 key: key.to_string(),
105 operator,
106 data_type: field.data_type(),
107 message: format!(
108 "filter value type {} is not compatible with field type {}",
109 value.data_type(),
110 field.data_type()
111 ),
112 })
113 }
114
115 fn require_field(&self, key: &str) -> MetadataResult<&MetadataField> {
117 self.field(key)
118 .ok_or_else(|| MetadataError::UnknownFilterField {
119 key: key.to_string(),
120 })
121 }
122}
123
124#[inline]
126fn is_numeric_data_type(data_type: DataType) -> bool {
127 matches!(
128 data_type,
129 DataType::Int8
130 | DataType::Int16
131 | DataType::Int32
132 | DataType::Int64
133 | DataType::Int128
134 | DataType::UInt8
135 | DataType::UInt16
136 | DataType::UInt32
137 | DataType::UInt64
138 | DataType::UInt128
139 | DataType::Float32
140 | DataType::Float64
141 | DataType::BigInteger
142 | DataType::BigDecimal
143 | DataType::IntSize
144 | DataType::UIntSize
145 )
146}
147
148#[inline]
150fn is_range_comparable_type(data_type: DataType) -> bool {
151 is_numeric_data_type(data_type) || matches!(data_type, DataType::String)
152}
153
154#[inline]
156fn value_matches_field_type(value: &Value, field_type: DataType) -> bool {
157 let value_type = value.data_type();
158 value_type == field_type || is_numeric_data_type(value_type) && is_numeric_data_type(field_type)
159}