xsd_schema/xpath/
boolean.rs1use num_bigint::BigInt;
18
19use super::error::XPathError;
20use crate::types::{
21 value::{XmlAtomicValue, XmlValue, XmlValueKind},
22 XmlTypeCode,
23};
24
25pub fn effective_boolean_value(value: &XmlValue) -> Result<bool, XPathError> {
50 match &value.value {
51 XmlValueKind::Atomic(XmlAtomicValue::Boolean(b)) => Ok(*b),
53
54 XmlValueKind::Atomic(XmlAtomicValue::String(s)) => Ok(!s.is_empty()),
56 XmlValueKind::UntypedAtomic(s) => Ok(!s.is_empty()),
57 XmlValueKind::Atomic(XmlAtomicValue::AnyUri(s)) => Ok(!s.is_empty()),
58
59 XmlValueKind::Atomic(XmlAtomicValue::Float(f)) => Ok(!f.is_nan() && *f != 0.0),
61
62 XmlValueKind::Atomic(XmlAtomicValue::Double(d)) => Ok(!d.is_nan() && *d != 0.0),
64
65 XmlValueKind::Atomic(XmlAtomicValue::Decimal(d)) => Ok(!d.is_zero()),
67
68 XmlValueKind::Atomic(XmlAtomicValue::Integer(i)) => Ok(*i != BigInt::from(0)),
70
71 XmlValueKind::Union(inner) => effective_boolean_value(inner),
73
74 XmlValueKind::List { .. } => Err(XPathError::invalid_argument_type(
76 "fn:boolean",
77 format_type_for_error(value.type_code),
78 )),
79
80 _ => Err(XPathError::invalid_argument_type(
82 "fn:boolean",
83 format_type_for_error(value.type_code),
84 )),
85 }
86}
87
88pub fn effective_boolean_value_opt(value: Option<&XmlValue>) -> Result<bool, XPathError> {
102 match value {
103 None => Ok(false), Some(v) => effective_boolean_value(v),
105 }
106}
107
108pub fn not(value: &XmlValue) -> Result<bool, XPathError> {
121 effective_boolean_value(value).map(|b| !b)
122}
123
124pub fn not_opt(value: Option<&XmlValue>) -> Result<bool, XPathError> {
126 effective_boolean_value_opt(value).map(|b| !b)
127}
128
129pub fn is_numeric_type(type_code: XmlTypeCode) -> bool {
133 matches!(
134 type_code,
135 XmlTypeCode::Decimal
136 | XmlTypeCode::Float
137 | XmlTypeCode::Double
138 | XmlTypeCode::Integer
139 | XmlTypeCode::NonPositiveInteger
140 | XmlTypeCode::NegativeInteger
141 | XmlTypeCode::Long
142 | XmlTypeCode::Int
143 | XmlTypeCode::Short
144 | XmlTypeCode::Byte
145 | XmlTypeCode::NonNegativeInteger
146 | XmlTypeCode::UnsignedLong
147 | XmlTypeCode::UnsignedInt
148 | XmlTypeCode::UnsignedShort
149 | XmlTypeCode::UnsignedByte
150 | XmlTypeCode::PositiveInteger
151 )
152}
153
154pub fn is_string_like_type(type_code: XmlTypeCode) -> bool {
158 matches!(
159 type_code,
160 XmlTypeCode::String
161 | XmlTypeCode::NormalizedString
162 | XmlTypeCode::Token
163 | XmlTypeCode::Language
164 | XmlTypeCode::NmToken
165 | XmlTypeCode::Name
166 | XmlTypeCode::NCName
167 | XmlTypeCode::Id
168 | XmlTypeCode::IdRef
169 | XmlTypeCode::Entity
170 | XmlTypeCode::UntypedAtomic
171 | XmlTypeCode::AnyUri
172 )
173}
174
175pub fn supports_ebv(type_code: XmlTypeCode) -> bool {
177 type_code == XmlTypeCode::Boolean
178 || is_numeric_type(type_code)
179 || is_string_like_type(type_code)
180}
181
182fn format_type_for_error(type_code: XmlTypeCode) -> String {
184 match type_code {
185 XmlTypeCode::None => "none".to_string(),
186 XmlTypeCode::Item => "item()".to_string(),
187 XmlTypeCode::Node => "node()".to_string(),
188 XmlTypeCode::Document => "document-node()".to_string(),
189 XmlTypeCode::Element => "element()".to_string(),
190 XmlTypeCode::Attribute => "attribute()".to_string(),
191 XmlTypeCode::Namespace => "namespace-node()".to_string(),
192 XmlTypeCode::ProcessingInstruction => "processing-instruction()".to_string(),
193 XmlTypeCode::Comment => "comment()".to_string(),
194 XmlTypeCode::Text => "text()".to_string(),
195 XmlTypeCode::AnyType => "xs:anyType".to_string(),
196 XmlTypeCode::AnySimpleType => "xs:anySimpleType".to_string(),
197 XmlTypeCode::AnyAtomicType => "xs:anyAtomicType".to_string(),
198 XmlTypeCode::UntypedAtomic => "xs:untypedAtomic".to_string(),
199 XmlTypeCode::String => "xs:string".to_string(),
200 XmlTypeCode::Boolean => "xs:boolean".to_string(),
201 XmlTypeCode::Decimal => "xs:decimal".to_string(),
202 XmlTypeCode::Float => "xs:float".to_string(),
203 XmlTypeCode::Double => "xs:double".to_string(),
204 XmlTypeCode::Integer => "xs:integer".to_string(),
205 XmlTypeCode::Duration => "xs:duration".to_string(),
206 XmlTypeCode::DateTime => "xs:dateTime".to_string(),
207 XmlTypeCode::Time => "xs:time".to_string(),
208 XmlTypeCode::Date => "xs:date".to_string(),
209 XmlTypeCode::GYearMonth => "xs:gYearMonth".to_string(),
210 XmlTypeCode::GYear => "xs:gYear".to_string(),
211 XmlTypeCode::GMonthDay => "xs:gMonthDay".to_string(),
212 XmlTypeCode::GDay => "xs:gDay".to_string(),
213 XmlTypeCode::GMonth => "xs:gMonth".to_string(),
214 XmlTypeCode::HexBinary => "xs:hexBinary".to_string(),
215 XmlTypeCode::Base64Binary => "xs:base64Binary".to_string(),
216 XmlTypeCode::QName => "xs:QName".to_string(),
217 XmlTypeCode::Notation => "xs:NOTATION".to_string(),
218 XmlTypeCode::YearMonthDuration => "xs:yearMonthDuration".to_string(),
219 XmlTypeCode::DayTimeDuration => "xs:dayTimeDuration".to_string(),
220 _ => format!("type({})", type_code as u8),
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227 use crate::types::XmlTypeCode;
228 use num_bigint::BigInt;
229 use rust_decimal::Decimal;
230
231 #[test]
232 fn test_boolean_ebv() {
233 assert!(effective_boolean_value(&XmlValue::boolean(true)).unwrap());
234 assert!(!effective_boolean_value(&XmlValue::boolean(false)).unwrap());
235 }
236
237 #[test]
238 fn test_string_ebv() {
239 assert!(!effective_boolean_value(&XmlValue::string("")).unwrap());
240 assert!(effective_boolean_value(&XmlValue::string("hello")).unwrap());
241 assert!(effective_boolean_value(&XmlValue::string(" ")).unwrap()); }
243
244 #[test]
245 fn test_untyped_atomic_ebv() {
246 assert!(!effective_boolean_value(&XmlValue::untyped("")).unwrap());
247 assert!(effective_boolean_value(&XmlValue::untyped("value")).unwrap());
248 }
249
250 #[test]
251 fn test_numeric_ebv() {
252 assert!(!effective_boolean_value(&XmlValue::integer(BigInt::from(0))).unwrap());
254 assert!(effective_boolean_value(&XmlValue::integer(BigInt::from(42))).unwrap());
255 assert!(effective_boolean_value(&XmlValue::integer(BigInt::from(-1))).unwrap());
256
257 assert!(!effective_boolean_value(&XmlValue::decimal(Decimal::ZERO)).unwrap());
259 assert!(effective_boolean_value(&XmlValue::decimal(Decimal::new(123, 2))).unwrap());
260
261 assert!(!effective_boolean_value(&XmlValue::double(0.0)).unwrap());
263 assert!(effective_boolean_value(&XmlValue::double(1.5)).unwrap());
264 assert!(!effective_boolean_value(&XmlValue::double(f64::NAN)).unwrap());
265 assert!(effective_boolean_value(&XmlValue::double(f64::INFINITY)).unwrap());
266
267 assert!(!effective_boolean_value(&XmlValue::float(0.0)).unwrap());
269 assert!(effective_boolean_value(&XmlValue::float(1.5)).unwrap());
270 assert!(!effective_boolean_value(&XmlValue::float(f32::NAN)).unwrap());
271 }
272
273 #[test]
274 fn test_empty_sequence_ebv() {
275 assert!(!effective_boolean_value_opt(None).unwrap());
276 }
277
278 #[test]
279 fn test_not() {
280 assert!(!not(&XmlValue::boolean(true)).unwrap());
281 assert!(not(&XmlValue::boolean(false)).unwrap());
282 assert!(not(&XmlValue::string("")).unwrap());
283 assert!(!not(&XmlValue::string("x")).unwrap());
284 }
285
286 #[test]
287 fn test_unsupported_type_error() {
288 let dt = XmlValue::new(
290 XmlTypeCode::DateTime,
291 XmlValueKind::Atomic(XmlAtomicValue::DateTime(
292 crate::types::value::DateTimeValue {
293 year: 2024,
294 month: 1,
295 day: 15,
296 hour: 12,
297 minute: 30,
298 second: Decimal::ZERO,
299 timezone: None,
300 },
301 )),
302 );
303 let result = effective_boolean_value(&dt);
304 assert!(result.is_err());
305 if let Err(XPathError::FORG0006Named { function, .. }) = result {
306 assert_eq!(function, "fn:boolean");
307 } else {
308 panic!("Expected FORG0006Named error");
309 }
310 }
311
312 #[test]
313 fn test_is_numeric_type() {
314 assert!(is_numeric_type(XmlTypeCode::Integer));
315 assert!(is_numeric_type(XmlTypeCode::Decimal));
316 assert!(is_numeric_type(XmlTypeCode::Float));
317 assert!(is_numeric_type(XmlTypeCode::Double));
318 assert!(is_numeric_type(XmlTypeCode::Long));
319 assert!(!is_numeric_type(XmlTypeCode::String));
320 assert!(!is_numeric_type(XmlTypeCode::Boolean));
321 }
322
323 #[test]
324 fn test_is_string_like_type() {
325 assert!(is_string_like_type(XmlTypeCode::String));
326 assert!(is_string_like_type(XmlTypeCode::UntypedAtomic));
327 assert!(is_string_like_type(XmlTypeCode::AnyUri));
328 assert!(!is_string_like_type(XmlTypeCode::Integer));
329 assert!(!is_string_like_type(XmlTypeCode::Boolean));
330 }
331}