Skip to main content

xsd_schema/xpath/
quantified.rs

1//! Quantified expression support for XPath evaluation.
2//!
3//! This module implements XPath 2.0 quantified expressions:
4//! - `some $x in ... satisfies ...`
5//! - `every $x in ... satisfies ...`
6
7use super::boolean::effective_boolean_value;
8use super::error::XPathError;
9use crate::types::value::XmlValue;
10
11/// Check if some item in the sequence satisfies the condition.
12///
13/// This implements `some $x in $seq satisfies $condition`.
14/// Returns true if at least one item evaluates to true.
15///
16/// # Arguments
17///
18/// * `values` - The sequence of boolean values (or values convertible to boolean)
19///
20/// # Returns
21///
22/// `true` if any value is true, `false` if all are false or sequence is empty.
23pub fn some(values: &[XmlValue]) -> Result<bool, XPathError> {
24    for value in values {
25        if effective_boolean_value(value)? {
26            return Ok(true);
27        }
28    }
29    Ok(false)
30}
31
32/// Check if every item in the sequence satisfies the condition.
33///
34/// This implements `every $x in $seq satisfies $condition`.
35/// Returns true if all items evaluate to true (including empty sequence).
36///
37/// # Arguments
38///
39/// * `values` - The sequence of boolean values (or values convertible to boolean)
40///
41/// # Returns
42///
43/// `true` if all values are true or sequence is empty, `false` otherwise.
44pub fn every(values: &[XmlValue]) -> Result<bool, XPathError> {
45    for value in values {
46        if !effective_boolean_value(value)? {
47            return Ok(false);
48        }
49    }
50    Ok(true)
51}
52
53/// Check if some value in the sequence matches a predicate.
54///
55/// Generic version that takes a predicate function.
56///
57/// # Arguments
58///
59/// * `values` - The sequence of values
60/// * `predicate` - Function to test each value
61///
62/// # Returns
63///
64/// `true` if any value satisfies the predicate.
65pub fn some_with<F>(values: &[XmlValue], predicate: F) -> bool
66where
67    F: FnMut(&XmlValue) -> bool,
68{
69    values.iter().any(predicate)
70}
71
72/// Check if every value in the sequence matches a predicate.
73///
74/// Generic version that takes a predicate function.
75///
76/// # Arguments
77///
78/// * `values` - The sequence of values
79/// * `predicate` - Function to test each value
80///
81/// # Returns
82///
83/// `true` if all values satisfy the predicate.
84pub fn every_with<F>(values: &[XmlValue], predicate: F) -> bool
85where
86    F: FnMut(&XmlValue) -> bool,
87{
88    values.iter().all(predicate)
89}
90
91/// Check if the sequence contains at least one true value.
92///
93/// Simplified version for boolean sequences.
94pub fn some_true(values: &[bool]) -> bool {
95    values.iter().any(|&v| v)
96}
97
98/// Check if all values in the sequence are true.
99///
100/// Simplified version for boolean sequences.
101pub fn every_true(values: &[bool]) -> bool {
102    values.iter().all(|&v| v)
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_some_with_true_values() {
111        let values = vec![
112            XmlValue::boolean(false),
113            XmlValue::boolean(true),
114            XmlValue::boolean(false),
115        ];
116        assert!(some(&values).unwrap());
117    }
118
119    #[test]
120    fn test_some_with_all_false() {
121        let values = vec![XmlValue::boolean(false), XmlValue::boolean(false)];
122        assert!(!some(&values).unwrap());
123    }
124
125    #[test]
126    fn test_some_empty_sequence() {
127        let values: Vec<XmlValue> = vec![];
128        assert!(!some(&values).unwrap());
129    }
130
131    #[test]
132    fn test_every_with_all_true() {
133        let values = vec![XmlValue::boolean(true), XmlValue::boolean(true)];
134        assert!(every(&values).unwrap());
135    }
136
137    #[test]
138    fn test_every_with_some_false() {
139        let values = vec![
140            XmlValue::boolean(true),
141            XmlValue::boolean(false),
142            XmlValue::boolean(true),
143        ];
144        assert!(!every(&values).unwrap());
145    }
146
147    #[test]
148    fn test_every_empty_sequence() {
149        // Vacuous truth: every item in empty sequence satisfies any condition
150        let values: Vec<XmlValue> = vec![];
151        assert!(every(&values).unwrap());
152    }
153
154    #[test]
155    fn test_some_with_strings() {
156        // Non-empty strings are true
157        let values = vec![XmlValue::string(""), XmlValue::string("hello")];
158        assert!(some(&values).unwrap());
159    }
160
161    #[test]
162    fn test_every_with_strings() {
163        // All non-empty strings
164        let values = vec![XmlValue::string("a"), XmlValue::string("b")];
165        assert!(every(&values).unwrap());
166
167        // One empty string
168        let values = vec![XmlValue::string("a"), XmlValue::string("")];
169        assert!(!every(&values).unwrap());
170    }
171
172    #[test]
173    fn test_some_with_predicate() {
174        let values = vec![
175            XmlValue::string("apple"),
176            XmlValue::string("banana"),
177            XmlValue::string("cherry"),
178        ];
179        assert!(some_with(&values, |v| v.to_string_value().starts_with('b')));
180        assert!(!some_with(&values, |v| v
181            .to_string_value()
182            .starts_with('z')));
183    }
184
185    #[test]
186    fn test_every_with_predicate() {
187        let values = vec![
188            XmlValue::string("apple"),
189            XmlValue::string("avocado"),
190            XmlValue::string("apricot"),
191        ];
192        assert!(every_with(&values, |v| v
193            .to_string_value()
194            .starts_with('a')));
195        assert!(!every_with(&values, |v| v.to_string_value().len() == 5));
196    }
197
198    #[test]
199    fn test_some_true_bool_slice() {
200        assert!(some_true(&[false, true, false]));
201        assert!(!some_true(&[false, false]));
202        assert!(!some_true(&[]));
203    }
204
205    #[test]
206    fn test_every_true_bool_slice() {
207        assert!(every_true(&[true, true, true]));
208        assert!(!every_true(&[true, false, true]));
209        assert!(every_true(&[])); // Vacuous truth
210    }
211}