Skip to main content

qubit_metadata/filter/
filter_limits.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! [`FilterLimits`] — resource bounds for metadata filters.
9
10use super::FilterLimitKind;
11use super::FilterLimitsBuilder;
12use crate::MetadataError;
13use crate::MetadataResult;
14use crate::constants::FILTER_MAX_DEPTH;
15use crate::constants::FILTER_MAX_KEY_BYTES;
16use crate::constants::FILTER_MAX_NODES;
17use crate::constants::FILTER_MAX_SET_VALUES;
18
19/// Resource bounds enforced for every constructed or deserialized filter.
20///
21/// The defaults keep filters small enough for predictable allocation and
22/// recursive evaluation while leaving room for realistic application queries.
23///
24/// # Examples
25///
26/// ```
27/// use qubit_metadata::FilterLimits;
28///
29/// # fn main() -> qubit_metadata::MetadataResult<()> {
30/// let limits = FilterLimits::builder().max_depth(8).build()?;
31/// assert_eq!(limits.max_depth(), 8);
32/// # Ok(())
33/// # }
34/// ```
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36#[must_use]
37pub struct FilterLimits {
38    /// Maximum nesting depth of an expression tree.
39    max_depth: usize,
40    /// Maximum number of expression nodes, including constants and groups.
41    max_nodes: usize,
42    /// Maximum number of candidate values in one membership condition.
43    max_set_values: usize,
44    /// Maximum UTF-8 byte length of one metadata key.
45    max_key_length: usize,
46}
47
48impl FilterLimits {
49    /// Library-wide hard maximums for every resource bound.
50    pub const MAX: Self = Self {
51        max_depth: FILTER_MAX_DEPTH,
52        max_nodes: FILTER_MAX_NODES,
53        max_set_values: FILTER_MAX_SET_VALUES,
54        max_key_length: FILTER_MAX_KEY_BYTES,
55    };
56
57    /// Creates a fluent builder for validated resource limits.
58    ///
59    /// # Returns
60    ///
61    /// A builder whose omitted properties use library hard maximums.
62    #[inline(always)]
63    #[must_use = "the builder must be configured or used to build filter limits"]
64    pub const fn builder() -> FilterLimitsBuilder {
65        FilterLimitsBuilder::new()
66    }
67
68    /// Validates and creates resource limits.
69    ///
70    /// # Parameters
71    ///
72    /// * `max_depth` - Maximum root-inclusive expression depth.
73    /// * `max_nodes` - Maximum expression node count.
74    /// * `max_set_values` - Maximum membership candidate count.
75    /// * `max_key_bytes` - Maximum UTF-8 byte length of one metadata key.
76    ///
77    /// # Errors
78    ///
79    /// Returns [`MetadataError::InvalidFilterLimit`] when a value is zero or
80    /// exceeds the corresponding hard maximum.
81    ///
82    /// # Returns
83    ///
84    /// Validated resource limits.
85    pub(crate) fn try_new(
86        max_depth: usize,
87        max_nodes: usize,
88        max_set_values: usize,
89        max_key_bytes: usize,
90    ) -> MetadataResult<Self> {
91        Self::validate_limit(FilterLimitKind::Depth, max_depth, Self::MAX.max_depth)?;
92        Self::validate_limit(FilterLimitKind::Nodes, max_nodes, Self::MAX.max_nodes)?;
93        Self::validate_limit(FilterLimitKind::SetValues, max_set_values, Self::MAX.max_set_values)?;
94        Self::validate_limit(FilterLimitKind::KeyBytes, max_key_bytes, Self::MAX.max_key_length)?;
95        Ok(Self {
96            max_depth,
97            max_nodes,
98            max_set_values,
99            max_key_length: max_key_bytes,
100        })
101    }
102
103    /// Returns the maximum nesting depth.
104    #[inline(always)]
105    #[must_use]
106    pub const fn max_depth(&self) -> usize {
107        self.max_depth
108    }
109
110    /// Returns the maximum number of expression nodes.
111    #[inline(always)]
112    #[must_use]
113    pub const fn max_nodes(&self) -> usize {
114        self.max_nodes
115    }
116
117    /// Returns the maximum candidate values in one membership condition.
118    #[inline(always)]
119    #[must_use]
120    pub const fn max_set_values(&self) -> usize {
121        self.max_set_values
122    }
123
124    /// Returns the maximum UTF-8 byte length of one metadata key.
125    ///
126    /// # Returns
127    ///
128    /// The configured key byte bound.
129    #[inline(always)]
130    #[must_use]
131    pub const fn max_key_bytes(&self) -> usize {
132        self.max_key_length
133    }
134
135    /// Validates one configured resource bound.
136    ///
137    /// # Parameters
138    ///
139    /// * `kind` - Category of the bounded resource.
140    /// * `value` - Requested bound.
141    /// * `maximum` - Library hard maximum.
142    ///
143    /// # Errors
144    ///
145    /// Returns [`MetadataError::InvalidFilterLimit`] when `value` is zero or
146    /// greater than `maximum`.
147    fn validate_limit(kind: FilterLimitKind, value: usize, maximum: usize) -> MetadataResult<()> {
148        if value == 0 || value > maximum {
149            return Err(MetadataError::InvalidFilterLimit { kind, value, maximum });
150        }
151        Ok(())
152    }
153}
154
155impl Default for FilterLimits {
156    #[inline]
157    fn default() -> Self {
158        Self::MAX
159    }
160}