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