Skip to main content

qubit_metadata/filter/
metadata_filter.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! [`MetadataFilter`].
10use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
11
12use super::filter_expr::FilterExpr;
13use super::metadata_filter_builder::MetadataFilterBuilder;
14use super::wire::MetadataFilterWire;
15use crate::metadata::Metadata;
16use crate::{
17    Condition, FilterMatchOptions, MetadataResult, MissingKeyPolicy, NumberComparisonPolicy,
18};
19
20/// An immutable, composable filter expression over [`Metadata`].
21///
22/// Construct filters with [`MetadataFilter::builder`]. An empty builder builds a
23/// match-all filter, while structurally invalid expressions such as empty groups
24/// are rejected by [`MetadataFilterBuilder::build`].
25#[derive(Debug, Clone, PartialEq, Default)]
26pub struct MetadataFilter {
27    /// Root expression tree. `None` means match all.
28    pub(crate) expr: Option<FilterExpr>,
29    /// Match policies used by [`MetadataFilter::matches`].
30    pub(crate) options: FilterMatchOptions,
31}
32
33impl MetadataFilter {
34    /// Creates a filter from expression and options.
35    #[inline]
36    pub(crate) fn new(expr: Option<FilterExpr>, options: FilterMatchOptions) -> Self {
37        Self { expr, options }
38    }
39
40    /// Creates a builder for a metadata filter.
41    #[inline]
42    #[must_use]
43    pub fn builder() -> MetadataFilterBuilder {
44        MetadataFilterBuilder::default()
45    }
46
47    /// Creates a filter that matches every metadata object.
48    #[inline]
49    #[must_use]
50    pub fn all() -> Self {
51        Self::default()
52    }
53
54    /// Creates a filter that matches no metadata object.
55    #[inline]
56    #[must_use]
57    pub fn none() -> Self {
58        Self {
59            expr: Some(FilterExpr::False),
60            options: FilterMatchOptions::default(),
61        }
62    }
63
64    /// Returns the current match options.
65    #[inline]
66    #[must_use]
67    pub fn options(&self) -> FilterMatchOptions {
68        self.options
69    }
70
71    /// Replaces the current match options and returns a new filter.
72    #[inline]
73    #[must_use]
74    pub fn with_options(mut self, options: FilterMatchOptions) -> Self {
75        self.options = options;
76        self
77    }
78
79    /// Returns a new filter with the supplied missing-key policy.
80    #[inline]
81    #[must_use]
82    pub fn with_missing_key_policy(mut self, missing_key_policy: MissingKeyPolicy) -> Self {
83        self.options.missing_key_policy = missing_key_policy;
84        self
85    }
86
87    /// Returns a new filter with the supplied number-comparison policy.
88    #[inline]
89    #[must_use]
90    pub fn with_number_comparison_policy(
91        mut self,
92        number_comparison_policy: NumberComparisonPolicy,
93    ) -> Self {
94        self.options.number_comparison_policy = number_comparison_policy;
95        self
96    }
97
98    /// Returns a new filter that negates this filter.
99    #[allow(clippy::should_implement_trait)]
100    #[inline]
101    #[must_use]
102    pub fn not(mut self) -> Self {
103        self.expr = MetadataFilterBuilder::negate_expr(self.expr);
104        self
105    }
106
107    /// Returns `true` if `meta` satisfies this filter.
108    #[inline]
109    #[must_use]
110    pub fn matches(&self, meta: &Metadata) -> bool {
111        self.matches_with_options(meta, self.options)
112    }
113
114    /// Returns `true` if `meta` satisfies this filter with explicit options.
115    #[inline]
116    #[must_use]
117    pub fn matches_with_options(&self, meta: &Metadata, options: FilterMatchOptions) -> bool {
118        self.expr
119            .as_ref()
120            .is_none_or(|expr| expr.matches(meta, options))
121    }
122
123    /// Visits all leaf conditions in this filter.
124    pub(crate) fn visit_conditions<F>(&self, mut visitor: F) -> MetadataResult<()>
125    where
126        F: FnMut(&Condition) -> MetadataResult<()>,
127    {
128        if let Some(expr) = &self.expr {
129            expr.visit_conditions(&mut visitor)?;
130        }
131        Ok(())
132    }
133}
134
135impl Serialize for MetadataFilter {
136    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137    where
138        S: Serializer,
139    {
140        MetadataFilterWire::from(self).serialize(serializer)
141    }
142}
143
144impl<'de> Deserialize<'de> for MetadataFilter {
145    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
146    where
147        D: Deserializer<'de>,
148    {
149        MetadataFilterWire::deserialize(deserializer)?
150            .into_filter()
151            .map_err(de::Error::custom)
152    }
153}
154
155impl std::ops::Not for MetadataFilter {
156    type Output = MetadataFilter;
157
158    #[inline]
159    fn not(self) -> Self::Output {
160        MetadataFilter::not(self)
161    }
162}