zrx_id/id/filter.rs
1// Copyright (c) 2025-2026 Zensical and contributors
2
3// SPDX-License-Identifier: MIT
4// All contributions are certified under the DCO
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to
8// deal in the Software without restriction, including without limitation the
9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10// sell copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22// IN THE SOFTWARE.
23
24// ----------------------------------------------------------------------------
25
26//! Filter.
27
28use slab::Slab;
29
30use super::matcher::Matcher;
31
32mod builder;
33mod candidates;
34mod condition;
35mod error;
36mod terms;
37
38pub use builder::Builder;
39pub use candidates::Candidates;
40use condition::Condition;
41pub use error::{Error, Result};
42pub use terms::Terms;
43
44// ----------------------------------------------------------------------------
45// Structs
46// ----------------------------------------------------------------------------
47
48/// Filter.
49///
50/// Filters efficiently match identifiers against a set of expressions, which
51/// are composed of conditions and logical operators, and compiled into a set
52/// of optimized instructions. This makes it possible to evaluate arbitrarily
53/// complex logical expressions against identifiers extremely fast.
54///
55/// Each [`Filter`] manages a [`Matcher`], used to identify matching terms in
56/// expressions in nanoseconds, and a set of conditions built from expressions,
57/// which are checked for satisfiability by using a bitwise stack-based virtual
58/// machine with an optimized set of instructions. Thus, the [`Matcher`] can be
59/// thought of as the first stage, eliminating non-matching expressions, while
60/// the condition set is the second stage, which checks whether the remaining
61/// expressions are actually satisfied by the identifier.
62///
63/// # Examples
64///
65/// ```
66/// # use std::error::Error;
67/// # fn main() -> Result<(), Box<dyn Error>> {
68/// use zrx_id::{selector, Expression, Filter, Id};
69///
70/// // Create filter builder and insert expression
71/// let mut builder = Filter::builder();
72/// builder.insert(Expression::all(|expr| {
73/// expr.with(selector!(location = "**/*.md")?)?
74/// .with(selector!(provider = "file")?)
75/// })?);
76///
77/// // Create filter from builder
78/// let filter = builder.build()?;
79///
80/// // Create identifier and obtain candidate expressions
81/// let id: Id = "zri:file:::docs:index.md:".parse()?;
82/// for index in filter.candidates(&id)? {
83/// println!("{index:?}");
84/// }
85/// # Ok(())
86/// # }
87/// ```
88#[derive(Debug, Default)]
89pub struct Filter {
90 /// Condition set, built from expressions.
91 conditions: Slab<Condition>,
92 /// Condition indices of negations.
93 negations: Vec<u32>,
94 /// Condition term mappings.
95 mapping: Vec<u32>,
96 /// Extracted terms.
97 matcher: Matcher,
98}
99
100// ----------------------------------------------------------------------------
101// Implementations
102// ----------------------------------------------------------------------------
103
104#[allow(clippy::must_use_candidate)]
105impl Filter {
106 /// Returns the number of expressions.
107 #[inline]
108 pub fn len(&self) -> usize {
109 self.conditions.len()
110 }
111
112 /// Returns whether there are any expressions.
113 #[inline]
114 pub fn is_empty(&self) -> bool {
115 self.conditions.is_empty()
116 }
117}