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}