1use num_bigint::BigInt;
14use rust_decimal::Decimal;
15
16use super::error::XPathError;
17use crate::namespace::qname::QualifiedName;
18use crate::namespace::table::{well_known, NameTable};
19use crate::types::value::{
20 DateTimeValue, DateValue, DayTimeDurationValue, DurationValue, GDayValue, GMonthDayValue,
21 GMonthValue, GYearMonthValue, GYearValue, TimeValue, XmlAtomicValue, XmlValue, XmlValueKind,
22 YearMonthDurationValue,
23};
24use crate::types::{XmlTypeCode, VALIDATOR_REGISTRY};
25use crate::xpath::ast::OccurrenceIndicator;
26
27fn can_cast(source: XmlTypeCode, target: XmlTypeCode) -> bool {
32 if source == target {
34 return true;
35 }
36
37 if target == XmlTypeCode::Notation {
39 return false;
40 }
41
42 if source == XmlTypeCode::UntypedAtomic
44 || source == XmlTypeCode::String
45 || source.is_string_derived()
46 {
47 return true;
48 }
49
50 let source_numeric = source.is_numeric();
52 let source_boolean = source == XmlTypeCode::Boolean;
53 let source_duration = matches!(
54 source,
55 XmlTypeCode::Duration | XmlTypeCode::YearMonthDuration | XmlTypeCode::DayTimeDuration
56 );
57 let source_datetime = source == XmlTypeCode::DateTime || source == XmlTypeCode::DateTimeStamp;
58 let source_date = source == XmlTypeCode::Date;
59 let source_time = source == XmlTypeCode::Time;
60 let source_gyearmonth = source == XmlTypeCode::GYearMonth;
61 let source_gyear = source == XmlTypeCode::GYear;
62 let source_gmonthday = source == XmlTypeCode::GMonthDay;
63 let source_gday = source == XmlTypeCode::GDay;
64 let source_gmonth = source == XmlTypeCode::GMonth;
65 let source_binary = matches!(source, XmlTypeCode::Base64Binary | XmlTypeCode::HexBinary);
66 let source_anyuri = source == XmlTypeCode::AnyUri;
67
68 let target_ua_or_string = target == XmlTypeCode::UntypedAtomic
69 || target == XmlTypeCode::String
70 || target.is_string_derived();
71 let target_numeric = target.is_numeric() || target == XmlTypeCode::Boolean;
72
73 if source_numeric {
75 return target_ua_or_string || target_numeric;
76 }
77
78 if source_boolean {
80 return target_ua_or_string || target_numeric;
81 }
82
83 if source_duration {
85 return target_ua_or_string
86 || matches!(
87 target,
88 XmlTypeCode::Duration
89 | XmlTypeCode::YearMonthDuration
90 | XmlTypeCode::DayTimeDuration
91 );
92 }
93
94 if source_datetime {
96 return target_ua_or_string
97 || matches!(
98 target,
99 XmlTypeCode::DateTime
100 | XmlTypeCode::DateTimeStamp
101 | XmlTypeCode::Date
102 | XmlTypeCode::Time
103 | XmlTypeCode::GYearMonth
104 | XmlTypeCode::GYear
105 | XmlTypeCode::GMonthDay
106 | XmlTypeCode::GDay
107 | XmlTypeCode::GMonth
108 );
109 }
110
111 if source_date {
113 return target_ua_or_string
114 || matches!(
115 target,
116 XmlTypeCode::DateTime
117 | XmlTypeCode::Date
118 | XmlTypeCode::GYearMonth
119 | XmlTypeCode::GYear
120 | XmlTypeCode::GMonthDay
121 | XmlTypeCode::GDay
122 | XmlTypeCode::GMonth
123 );
124 }
125
126 if source_time {
128 return target_ua_or_string || target == XmlTypeCode::Time;
129 }
130
131 if source_gyearmonth {
133 return target_ua_or_string || target == XmlTypeCode::GYearMonth;
134 }
135
136 if source_gyear {
138 return target_ua_or_string || target == XmlTypeCode::GYear;
139 }
140
141 if source_gmonthday {
143 return target_ua_or_string || target == XmlTypeCode::GMonthDay;
144 }
145
146 if source_gday {
148 return target_ua_or_string || target == XmlTypeCode::GDay;
149 }
150
151 if source_gmonth {
153 return target_ua_or_string || target == XmlTypeCode::GMonth;
154 }
155
156 if source_binary {
158 return target_ua_or_string
159 || matches!(target, XmlTypeCode::Base64Binary | XmlTypeCode::HexBinary);
160 }
161
162 if source_anyuri {
164 return target_ua_or_string || target == XmlTypeCode::AnyUri;
165 }
166
167 false
168}
169
170pub fn cast_to(value: &XmlValue, target_type: XmlTypeCode) -> Result<XmlValue, XPathError> {
184 if value.type_code == target_type {
186 return Ok(value.clone());
187 }
188
189 if !can_cast(value.type_code, target_type) {
191 return Err(XPathError::type_mismatch(
192 format!("{:?}", value.type_code),
193 format!("{:?}", target_type),
194 ));
195 }
196
197 let string_val = value.to_string_value();
198
199 match target_type {
200 XmlTypeCode::String => Ok(XmlValue::string(string_val)),
201
202 XmlTypeCode::Boolean => cast_to_boolean(value, &string_val),
203
204 XmlTypeCode::Decimal => cast_to_decimal(value, &string_val),
205
206 XmlTypeCode::Integer => cast_to_integer(value, &string_val),
207
208 XmlTypeCode::Float => cast_to_float(value, &string_val),
209
210 XmlTypeCode::Double => cast_to_double(value, &string_val),
211
212 XmlTypeCode::UntypedAtomic => Ok(XmlValue::untyped(string_val)),
213
214 target if is_integer_derived(target) => cast_to_integer_subtype(value, target),
216
217 XmlTypeCode::DateTime
219 | XmlTypeCode::Date
220 | XmlTypeCode::Time
221 | XmlTypeCode::Duration
222 | XmlTypeCode::YearMonthDuration
223 | XmlTypeCode::DayTimeDuration
224 | XmlTypeCode::GYearMonth
225 | XmlTypeCode::GYear
226 | XmlTypeCode::GMonthDay
227 | XmlTypeCode::GDay
228 | XmlTypeCode::GMonth
229 | XmlTypeCode::DateTimeStamp => {
230 if let Some(result) = cast_datetime_cross(value, target_type) {
232 return result;
233 }
234 let type_name = target_type.local_name().unwrap_or("unknown");
235 VALIDATOR_REGISTRY
236 .validate(target_type, string_val.trim())
237 .map_err(|_| {
238 XPathError::invalid_cast_value(&string_val, format!("xs:{}", type_name))
239 })
240 }
241
242 XmlTypeCode::AnyUri | XmlTypeCode::HexBinary | XmlTypeCode::Base64Binary => {
244 if let Some(result) = cast_binary_cross(value, target_type) {
246 return result;
247 }
248 let type_name = target_type.local_name().unwrap_or("unknown");
249 VALIDATOR_REGISTRY
250 .validate(target_type, string_val.trim())
251 .map_err(|_| {
252 XPathError::invalid_cast_value(&string_val, format!("xs:{}", type_name))
253 })
254 }
255
256 XmlTypeCode::NormalizedString
258 | XmlTypeCode::Token
259 | XmlTypeCode::Language
260 | XmlTypeCode::NmToken
261 | XmlTypeCode::Name
262 | XmlTypeCode::NCName
263 | XmlTypeCode::Id
264 | XmlTypeCode::IdRef
265 | XmlTypeCode::Entity => {
266 let type_name = target_type.local_name().unwrap_or("unknown");
267 VALIDATOR_REGISTRY
268 .validate(target_type, &string_val)
269 .map_err(|_| {
270 XPathError::invalid_cast_value(&string_val, format!("xs:{}", type_name))
271 })
272 }
273
274 XmlTypeCode::QName | XmlTypeCode::Notation => Err(XPathError::type_mismatch(
276 format!("{:?}", value.type_code),
277 format!("{:?}", target_type),
278 )),
279
280 _ => Err(XPathError::type_mismatch(
282 format!("{:?}", value.type_code),
283 format!("{:?}", target_type),
284 )),
285 }
286}
287
288fn cast_to_boolean(value: &XmlValue, string_val: &str) -> Result<XmlValue, XPathError> {
290 let result = match &value.value {
291 XmlValueKind::Atomic(XmlAtomicValue::Boolean(b)) => *b,
292 XmlValueKind::Atomic(XmlAtomicValue::Integer(i)) => *i != BigInt::from(0),
293 XmlValueKind::Atomic(XmlAtomicValue::Decimal(d)) => !d.is_zero(),
294 XmlValueKind::Atomic(XmlAtomicValue::Float(f)) => *f != 0.0 && !f.is_nan(),
295 XmlValueKind::Atomic(XmlAtomicValue::Double(d)) => *d != 0.0 && !d.is_nan(),
296 _ => {
297 let s = string_val.trim();
298 match s {
299 "true" | "1" => true,
300 "false" | "0" => false,
301 _ => {
302 return Err(XPathError::invalid_cast_value(string_val, "xs:boolean"));
303 }
304 }
305 }
306 };
307 Ok(XmlValue::boolean(result))
308}
309
310fn cast_to_decimal(value: &XmlValue, string_val: &str) -> Result<XmlValue, XPathError> {
312 let result = match &value.value {
313 XmlValueKind::Atomic(XmlAtomicValue::Decimal(d)) => *d,
314 XmlValueKind::Atomic(XmlAtomicValue::Integer(i)) => i
315 .to_string()
316 .parse::<Decimal>()
317 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:decimal"))?,
318 XmlValueKind::Atomic(XmlAtomicValue::Float(f)) => {
319 if f.is_nan() || f.is_infinite() {
320 return Err(XPathError::invalid_cast_value(string_val, "xs:decimal"));
321 }
322 if *f == 0.0 {
323 Decimal::ZERO
324 } else {
325 Decimal::try_from(*f)
326 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:decimal"))?
327 }
328 }
329 XmlValueKind::Atomic(XmlAtomicValue::Double(d)) => {
330 if d.is_nan() || d.is_infinite() {
331 return Err(XPathError::invalid_cast_value(string_val, "xs:decimal"));
332 }
333 if *d == 0.0 {
334 Decimal::ZERO
335 } else {
336 Decimal::try_from(*d)
337 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:decimal"))?
338 }
339 }
340 XmlValueKind::Atomic(XmlAtomicValue::Boolean(b)) => {
341 if *b {
342 Decimal::ONE
343 } else {
344 Decimal::ZERO
345 }
346 }
347 _ => string_val
348 .trim()
349 .parse::<Decimal>()
350 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:decimal"))?,
351 };
352 Ok(XmlValue::decimal(result))
353}
354
355fn cast_to_integer(value: &XmlValue, string_val: &str) -> Result<XmlValue, XPathError> {
357 let result = match &value.value {
358 XmlValueKind::Atomic(XmlAtomicValue::Integer(i)) => i.clone(),
359 XmlValueKind::Atomic(XmlAtomicValue::Decimal(d)) => {
360 let truncated = d.trunc();
362 truncated
363 .to_string()
364 .parse::<BigInt>()
365 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:integer"))?
366 }
367 XmlValueKind::Atomic(XmlAtomicValue::Float(f)) => {
368 if f.is_nan() || f.is_infinite() {
369 return Err(XPathError::invalid_cast_value(string_val, "xs:integer"));
370 }
371 let truncated = f.trunc() as f64;
372 let s = format!("{:.0}", truncated);
374 s.parse::<BigInt>().map_err(|_| XPathError::FOCA0003 {
375 message: format!("Value {} is too large for xs:integer", string_val),
376 })?
377 }
378 XmlValueKind::Atomic(XmlAtomicValue::Double(d)) => {
379 if d.is_nan() || d.is_infinite() {
380 return Err(XPathError::invalid_cast_value(string_val, "xs:integer"));
381 }
382 let s = format!("{:.0}", d.trunc());
384 s.parse::<BigInt>().map_err(|_| XPathError::FOCA0003 {
385 message: format!("Value {} is too large for xs:integer", string_val),
386 })?
387 }
388 XmlValueKind::Atomic(XmlAtomicValue::Boolean(b)) => BigInt::from(if *b { 1 } else { 0 }),
389 _ => string_val
390 .trim()
391 .parse::<BigInt>()
392 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:integer"))?,
393 };
394 Ok(XmlValue::integer(result))
395}
396
397fn cast_to_float(value: &XmlValue, string_val: &str) -> Result<XmlValue, XPathError> {
399 let result = match &value.value {
400 XmlValueKind::Atomic(XmlAtomicValue::Float(f)) => *f,
401 XmlValueKind::Atomic(XmlAtomicValue::Double(d)) => *d as f32,
402 XmlValueKind::Atomic(XmlAtomicValue::Decimal(d)) => d
403 .to_string()
404 .parse::<f32>()
405 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:float"))?,
406 XmlValueKind::Atomic(XmlAtomicValue::Integer(i)) => i
407 .to_string()
408 .parse::<f32>()
409 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:float"))?,
410 XmlValueKind::Atomic(XmlAtomicValue::Boolean(b)) => {
411 if *b {
412 1.0
413 } else {
414 0.0
415 }
416 }
417 _ => parse_float_with_special(string_val.trim())
418 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:float"))?,
419 };
420 Ok(XmlValue::float(result))
421}
422
423fn cast_to_double(value: &XmlValue, string_val: &str) -> Result<XmlValue, XPathError> {
425 let result = match &value.value {
426 XmlValueKind::Atomic(XmlAtomicValue::Double(d)) => *d,
427 XmlValueKind::Atomic(XmlAtomicValue::Float(f)) => *f as f64,
428 XmlValueKind::Atomic(XmlAtomicValue::Decimal(d)) => d
429 .to_string()
430 .parse::<f64>()
431 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:double"))?,
432 XmlValueKind::Atomic(XmlAtomicValue::Integer(i)) => i
433 .to_string()
434 .parse::<f64>()
435 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:double"))?,
436 XmlValueKind::Atomic(XmlAtomicValue::Boolean(b)) => {
437 if *b {
438 1.0
439 } else {
440 0.0
441 }
442 }
443 _ => parse_double_with_special(string_val.trim())
444 .map_err(|_| XPathError::invalid_cast_value(string_val, "xs:double"))?,
445 };
446 Ok(XmlValue::double(result))
447}
448
449fn parse_float_with_special(s: &str) -> Result<f32, ()> {
451 match s {
452 "INF" => Ok(f32::INFINITY),
453 "-INF" => Ok(f32::NEG_INFINITY),
454 "NaN" => Ok(f32::NAN),
455 _ => s.parse::<f32>().map_err(|_| ()),
456 }
457}
458
459fn parse_double_with_special(s: &str) -> Result<f64, ()> {
461 match s {
462 "INF" => Ok(f64::INFINITY),
463 "-INF" => Ok(f64::NEG_INFINITY),
464 "NaN" => Ok(f64::NAN),
465 _ => s.parse::<f64>().map_err(|_| ()),
466 }
467}
468
469pub fn treat_as(value: &XmlValue, target_type: XmlTypeCode) -> Result<XmlValue, XPathError> {
485 if type_matches(value.type_code, target_type) {
486 Ok(value.clone())
487 } else {
488 Err(XPathError::type_mismatch(
489 format!("{:?}", target_type),
490 format!("{:?}", value.type_code),
491 ))
492 }
493}
494
495pub fn instance_of(value: &XmlValue, target_type: XmlTypeCode) -> bool {
508 type_matches(value.type_code, target_type)
509}
510
511pub fn instance_of_opt(
515 value: Option<&XmlValue>,
516 target_type: XmlTypeCode,
517 allow_empty: bool,
518) -> bool {
519 match value {
520 None => allow_empty,
521 Some(v) => instance_of(v, target_type),
522 }
523}
524
525pub fn castable(value: &XmlValue, target_type: XmlTypeCode) -> bool {
538 cast_to(value, target_type).is_ok()
539}
540
541pub fn castable_opt(value: Option<&XmlValue>, target_type: XmlTypeCode, allow_empty: bool) -> bool {
543 match value {
544 None => allow_empty,
545 Some(v) => castable(v, target_type),
546 }
547}
548
549fn is_integer_derived(code: XmlTypeCode) -> bool {
551 matches!(
552 code,
553 XmlTypeCode::Integer
554 | XmlTypeCode::NonPositiveInteger
555 | XmlTypeCode::NegativeInteger
556 | XmlTypeCode::Long
557 | XmlTypeCode::Int
558 | XmlTypeCode::Short
559 | XmlTypeCode::Byte
560 | XmlTypeCode::NonNegativeInteger
561 | XmlTypeCode::UnsignedLong
562 | XmlTypeCode::UnsignedInt
563 | XmlTypeCode::UnsignedShort
564 | XmlTypeCode::UnsignedByte
565 | XmlTypeCode::PositiveInteger
566 )
567}
568
569pub fn type_matches(source: XmlTypeCode, target: XmlTypeCode) -> bool {
577 if source == target {
578 return true;
579 }
580
581 if target == XmlTypeCode::AnyAtomicType {
583 return source.is_atomic();
584 }
585
586 if target == XmlTypeCode::Item {
588 return true;
589 }
590
591 if target == XmlTypeCode::String {
593 return source.is_string_derived() || source == XmlTypeCode::UntypedAtomic;
594 }
595
596 if target == XmlTypeCode::Integer {
598 return is_integer_derived(source);
599 }
600
601 if target == XmlTypeCode::Decimal {
603 return source == XmlTypeCode::Decimal || is_integer_derived(source);
604 }
605
606 false
607}
608
609pub fn resolved_type_to_type_code(
618 qname: &QualifiedName,
619 names: &NameTable,
620) -> Result<XmlTypeCode, XPathError> {
621 match qname.namespace_uri {
623 Some(ns_id) if ns_id == well_known::XS_NAMESPACE => {}
624 _ => {
625 let local = names.resolve(qname.local_name);
626 return Err(XPathError::XPST0051 {
627 type_name: local.to_string(),
628 });
629 }
630 }
631
632 let local_name = names.resolve(qname.local_name);
634 XmlTypeCode::from_local_name(&local_name).ok_or_else(|| XPathError::XPST0051 {
635 type_name: local_name.to_string(),
636 })
637}
638
639pub fn occurrence_allows_count(occ: OccurrenceIndicator, count: usize) -> bool {
647 match occ {
648 OccurrenceIndicator::One => count == 1,
649 OccurrenceIndicator::ZeroOrOne => count <= 1,
650 OccurrenceIndicator::ZeroOrMore => true,
651 OccurrenceIndicator::OneOrMore => count >= 1,
652 }
653}
654
655fn cast_datetime_cross(
658 value: &XmlValue,
659 target: XmlTypeCode,
660) -> Option<Result<XmlValue, XPathError>> {
661 match (&value.value, target) {
662 (XmlValueKind::Atomic(XmlAtomicValue::DateTime(dt)), XmlTypeCode::Date) => {
664 Some(Ok(XmlValue::new(
665 XmlTypeCode::Date,
666 XmlValueKind::Atomic(XmlAtomicValue::Date(DateValue {
667 year: dt.year,
668 month: dt.month,
669 day: dt.day,
670 timezone: dt.timezone,
671 })),
672 )))
673 }
674 (XmlValueKind::Atomic(XmlAtomicValue::DateTime(dt)), XmlTypeCode::Time) => {
676 Some(Ok(XmlValue::new(
677 XmlTypeCode::Time,
678 XmlValueKind::Atomic(XmlAtomicValue::Time(TimeValue {
679 hour: dt.hour,
680 minute: dt.minute,
681 second: dt.second,
682 timezone: dt.timezone,
683 })),
684 )))
685 }
686 (XmlValueKind::Atomic(XmlAtomicValue::Date(d)), XmlTypeCode::DateTime) => {
688 Some(Ok(XmlValue::new(
689 XmlTypeCode::DateTime,
690 XmlValueKind::Atomic(XmlAtomicValue::DateTime(DateTimeValue {
691 year: d.year,
692 month: d.month,
693 day: d.day,
694 hour: 0,
695 minute: 0,
696 second: Decimal::ZERO,
697 timezone: d.timezone,
698 })),
699 )))
700 }
701 (XmlValueKind::Atomic(XmlAtomicValue::DateTime(dt)), XmlTypeCode::GYearMonth) => {
703 Some(Ok(XmlValue::new(
704 XmlTypeCode::GYearMonth,
705 XmlValueKind::Atomic(XmlAtomicValue::GYearMonth(GYearMonthValue {
706 year: dt.year,
707 month: dt.month,
708 timezone: dt.timezone,
709 })),
710 )))
711 }
712 (XmlValueKind::Atomic(XmlAtomicValue::DateTime(dt)), XmlTypeCode::GYear) => {
714 Some(Ok(XmlValue::new(
715 XmlTypeCode::GYear,
716 XmlValueKind::Atomic(XmlAtomicValue::GYear(GYearValue {
717 year: dt.year,
718 timezone: dt.timezone,
719 })),
720 )))
721 }
722 (XmlValueKind::Atomic(XmlAtomicValue::DateTime(dt)), XmlTypeCode::GMonthDay) => {
724 Some(Ok(XmlValue::new(
725 XmlTypeCode::GMonthDay,
726 XmlValueKind::Atomic(XmlAtomicValue::GMonthDay(GMonthDayValue {
727 month: dt.month,
728 day: dt.day,
729 timezone: dt.timezone,
730 })),
731 )))
732 }
733 (XmlValueKind::Atomic(XmlAtomicValue::DateTime(dt)), XmlTypeCode::GDay) => {
735 Some(Ok(XmlValue::new(
736 XmlTypeCode::GDay,
737 XmlValueKind::Atomic(XmlAtomicValue::GDay(GDayValue {
738 day: dt.day,
739 timezone: dt.timezone,
740 })),
741 )))
742 }
743 (XmlValueKind::Atomic(XmlAtomicValue::DateTime(dt)), XmlTypeCode::GMonth) => {
745 Some(Ok(XmlValue::new(
746 XmlTypeCode::GMonth,
747 XmlValueKind::Atomic(XmlAtomicValue::GMonth(GMonthValue {
748 month: dt.month,
749 timezone: dt.timezone,
750 })),
751 )))
752 }
753 (XmlValueKind::Atomic(XmlAtomicValue::Date(d)), XmlTypeCode::GYearMonth) => {
755 Some(Ok(XmlValue::new(
756 XmlTypeCode::GYearMonth,
757 XmlValueKind::Atomic(XmlAtomicValue::GYearMonth(GYearMonthValue {
758 year: d.year,
759 month: d.month,
760 timezone: d.timezone,
761 })),
762 )))
763 }
764 (XmlValueKind::Atomic(XmlAtomicValue::Date(d)), XmlTypeCode::GYear) => {
766 Some(Ok(XmlValue::new(
767 XmlTypeCode::GYear,
768 XmlValueKind::Atomic(XmlAtomicValue::GYear(GYearValue {
769 year: d.year,
770 timezone: d.timezone,
771 })),
772 )))
773 }
774 (XmlValueKind::Atomic(XmlAtomicValue::Date(d)), XmlTypeCode::GMonthDay) => {
776 Some(Ok(XmlValue::new(
777 XmlTypeCode::GMonthDay,
778 XmlValueKind::Atomic(XmlAtomicValue::GMonthDay(GMonthDayValue {
779 month: d.month,
780 day: d.day,
781 timezone: d.timezone,
782 })),
783 )))
784 }
785 (XmlValueKind::Atomic(XmlAtomicValue::Date(d)), XmlTypeCode::GDay) => {
787 Some(Ok(XmlValue::new(
788 XmlTypeCode::GDay,
789 XmlValueKind::Atomic(XmlAtomicValue::GDay(GDayValue {
790 day: d.day,
791 timezone: d.timezone,
792 })),
793 )))
794 }
795 (XmlValueKind::Atomic(XmlAtomicValue::Date(d)), XmlTypeCode::GMonth) => {
797 Some(Ok(XmlValue::new(
798 XmlTypeCode::GMonth,
799 XmlValueKind::Atomic(XmlAtomicValue::GMonth(GMonthValue {
800 month: d.month,
801 timezone: d.timezone,
802 })),
803 )))
804 }
805 (XmlValueKind::Atomic(XmlAtomicValue::Duration(d)), XmlTypeCode::YearMonthDuration) => {
807 Some(Ok(XmlValue::new(
808 XmlTypeCode::YearMonthDuration,
809 XmlValueKind::Atomic(XmlAtomicValue::YearMonthDuration(YearMonthDurationValue {
810 negative: d.negative,
811 years: d.years,
812 months: d.months,
813 })),
814 )))
815 }
816 (XmlValueKind::Atomic(XmlAtomicValue::Duration(d)), XmlTypeCode::DayTimeDuration) => {
818 Some(Ok(XmlValue::new(
819 XmlTypeCode::DayTimeDuration,
820 XmlValueKind::Atomic(XmlAtomicValue::DayTimeDuration(DayTimeDurationValue {
821 negative: d.negative,
822 days: d.days,
823 hours: d.hours,
824 minutes: d.minutes,
825 seconds: d.seconds,
826 })),
827 )))
828 }
829 (XmlValueKind::Atomic(XmlAtomicValue::YearMonthDuration(ym)), XmlTypeCode::Duration) => {
831 Some(Ok(XmlValue::new(
832 XmlTypeCode::Duration,
833 XmlValueKind::Atomic(XmlAtomicValue::Duration(DurationValue {
834 negative: ym.negative,
835 years: ym.years,
836 months: ym.months,
837 days: 0,
838 hours: 0,
839 minutes: 0,
840 seconds: Decimal::ZERO,
841 })),
842 )))
843 }
844 (XmlValueKind::Atomic(XmlAtomicValue::DayTimeDuration(dt)), XmlTypeCode::Duration) => {
846 Some(Ok(XmlValue::new(
847 XmlTypeCode::Duration,
848 XmlValueKind::Atomic(XmlAtomicValue::Duration(DurationValue {
849 negative: dt.negative,
850 years: 0,
851 months: 0,
852 days: dt.days,
853 hours: dt.hours,
854 minutes: dt.minutes,
855 seconds: dt.seconds,
856 })),
857 )))
858 }
859 (
863 XmlValueKind::Atomic(XmlAtomicValue::YearMonthDuration(_)),
864 XmlTypeCode::DayTimeDuration,
865 ) => Some(Ok(XmlValue::new(
866 XmlTypeCode::DayTimeDuration,
867 XmlValueKind::Atomic(XmlAtomicValue::DayTimeDuration(DayTimeDurationValue {
868 negative: false,
869 days: 0,
870 hours: 0,
871 minutes: 0,
872 seconds: Decimal::ZERO,
873 })),
874 ))),
875 (
879 XmlValueKind::Atomic(XmlAtomicValue::DayTimeDuration(_)),
880 XmlTypeCode::YearMonthDuration,
881 ) => Some(Ok(XmlValue::new(
882 XmlTypeCode::YearMonthDuration,
883 XmlValueKind::Atomic(XmlAtomicValue::YearMonthDuration(YearMonthDurationValue {
884 negative: false,
885 years: 0,
886 months: 0,
887 })),
888 ))),
889 _ => None,
890 }
891}
892
893fn cast_binary_cross(
895 value: &XmlValue,
896 target: XmlTypeCode,
897) -> Option<Result<XmlValue, XPathError>> {
898 match (&value.value, target) {
899 (XmlValueKind::Atomic(XmlAtomicValue::Base64Binary(bytes)), XmlTypeCode::HexBinary) => {
900 Some(Ok(XmlValue::new(
901 XmlTypeCode::HexBinary,
902 XmlValueKind::Atomic(XmlAtomicValue::HexBinary(bytes.clone())),
903 )))
904 }
905 (XmlValueKind::Atomic(XmlAtomicValue::HexBinary(bytes)), XmlTypeCode::Base64Binary) => {
906 Some(Ok(XmlValue::new(
907 XmlTypeCode::Base64Binary,
908 XmlValueKind::Atomic(XmlAtomicValue::Base64Binary(bytes.clone())),
909 )))
910 }
911 _ => None,
912 }
913}
914
915pub fn cast_to_integer_subtype(
920 value: &XmlValue,
921 target_type: XmlTypeCode,
922) -> Result<XmlValue, XPathError> {
923 let int_val = cast_to(value, XmlTypeCode::Integer)?;
925 let bigint = int_val
926 .as_integer()
927 .ok_or_else(|| XPathError::internal("Expected integer after cast"))?;
928
929 let (min, max): (i128, i128) = match target_type {
931 XmlTypeCode::Byte => (i8::MIN as i128, i8::MAX as i128),
932 XmlTypeCode::Short => (i16::MIN as i128, i16::MAX as i128),
933 XmlTypeCode::Int => (i32::MIN as i128, i32::MAX as i128),
934 XmlTypeCode::Long => (i64::MIN as i128, i64::MAX as i128),
935 XmlTypeCode::UnsignedByte => (0, u8::MAX as i128),
936 XmlTypeCode::UnsignedShort => (0, u16::MAX as i128),
937 XmlTypeCode::UnsignedInt => (0, u32::MAX as i128),
938 XmlTypeCode::UnsignedLong => (0, u64::MAX as i128),
939 XmlTypeCode::PositiveInteger => (1, i128::MAX),
940 XmlTypeCode::NonNegativeInteger => (0, i128::MAX),
941 XmlTypeCode::NegativeInteger => (i128::MIN, -1),
942 XmlTypeCode::NonPositiveInteger => (i128::MIN, 0),
943 XmlTypeCode::Integer => return Ok(int_val),
944 _ => {
945 return Err(XPathError::type_mismatch(
946 format!("{:?}", value.type_code),
947 format!("{:?}", target_type),
948 ))
949 }
950 };
951
952 let val_i128: i128 = bigint.to_string().parse().map_err(|_| {
954 XPathError::invalid_cast_value(bigint.to_string(), format!("{:?}", target_type))
955 })?;
956
957 if val_i128 < min || val_i128 > max {
958 return Err(XPathError::invalid_cast_value(
959 bigint.to_string(),
960 format!("{:?}", target_type),
961 ));
962 }
963
964 Ok(XmlValue::new(
965 target_type,
966 XmlValueKind::Atomic(XmlAtomicValue::Integer(bigint.clone())),
967 ))
968}
969
970#[cfg(test)]
971mod tests {
972 use super::*;
973
974 #[test]
975 fn test_cast_string_to_integer() {
976 let value = XmlValue::string("42");
977 let result = cast_to(&value, XmlTypeCode::Integer).unwrap();
978 assert_eq!(result.type_code, XmlTypeCode::Integer);
979 assert_eq!(result.as_integer().unwrap(), &BigInt::from(42));
980 }
981
982 #[test]
983 fn test_cast_string_to_decimal() {
984 let value = XmlValue::string("2.5");
985 let result = cast_to(&value, XmlTypeCode::Decimal).unwrap();
986 assert_eq!(result.type_code, XmlTypeCode::Decimal);
987 }
988
989 #[test]
990 fn test_cast_string_to_boolean() {
991 assert_eq!(
992 cast_to(&XmlValue::string("true"), XmlTypeCode::Boolean)
993 .unwrap()
994 .as_boolean(),
995 Some(true)
996 );
997 assert_eq!(
998 cast_to(&XmlValue::string("false"), XmlTypeCode::Boolean)
999 .unwrap()
1000 .as_boolean(),
1001 Some(false)
1002 );
1003 assert_eq!(
1004 cast_to(&XmlValue::string("1"), XmlTypeCode::Boolean)
1005 .unwrap()
1006 .as_boolean(),
1007 Some(true)
1008 );
1009 assert_eq!(
1010 cast_to(&XmlValue::string("0"), XmlTypeCode::Boolean)
1011 .unwrap()
1012 .as_boolean(),
1013 Some(false)
1014 );
1015 }
1016
1017 #[test]
1018 fn test_cast_invalid_string_to_boolean() {
1019 let result = cast_to(&XmlValue::string("yes"), XmlTypeCode::Boolean);
1020 assert!(result.is_err());
1021 }
1022
1023 #[test]
1024 fn test_cast_integer_to_double() {
1025 let value = XmlValue::integer(BigInt::from(42));
1026 let result = cast_to(&value, XmlTypeCode::Double).unwrap();
1027 assert_eq!(result.as_double(), Some(42.0));
1028 }
1029
1030 #[test]
1031 fn test_cast_double_to_integer() {
1032 let value = XmlValue::double(42.7);
1033 let result = cast_to(&value, XmlTypeCode::Integer).unwrap();
1034 assert_eq!(result.as_integer().unwrap(), &BigInt::from(42)); }
1036
1037 #[test]
1038 fn test_cast_nan_to_integer_fails() {
1039 let value = XmlValue::double(f64::NAN);
1040 let result = cast_to(&value, XmlTypeCode::Integer);
1041 assert!(result.is_err());
1042 }
1043
1044 #[test]
1045 fn test_cast_inf_to_decimal_fails() {
1046 let value = XmlValue::double(f64::INFINITY);
1047 let result = cast_to(&value, XmlTypeCode::Decimal);
1048 assert!(result.is_err());
1049 }
1050
1051 #[test]
1052 fn test_cast_same_type() {
1053 let value = XmlValue::string("hello");
1054 let result = cast_to(&value, XmlTypeCode::String).unwrap();
1055 assert_eq!(result.to_string_value(), "hello");
1056 }
1057
1058 #[test]
1059 fn test_instance_of() {
1060 assert!(instance_of(&XmlValue::string("test"), XmlTypeCode::String));
1061 assert!(instance_of(
1062 &XmlValue::integer(BigInt::from(1)),
1063 XmlTypeCode::Integer
1064 ));
1065 assert!(!instance_of(
1066 &XmlValue::string("test"),
1067 XmlTypeCode::Integer
1068 ));
1069
1070 assert!(instance_of(
1072 &XmlValue::string("test"),
1073 XmlTypeCode::AnyAtomicType
1074 ));
1075 }
1076
1077 #[test]
1078 fn test_castable() {
1079 assert!(castable(&XmlValue::string("42"), XmlTypeCode::Integer));
1080 assert!(!castable(
1081 &XmlValue::string("not a number"),
1082 XmlTypeCode::Integer
1083 ));
1084 }
1085
1086 #[test]
1087 fn test_treat_as_matching() {
1088 let value = XmlValue::string("test");
1089 let result = treat_as(&value, XmlTypeCode::String);
1090 assert!(result.is_ok());
1091 }
1092
1093 #[test]
1094 fn test_treat_as_non_matching() {
1095 let value = XmlValue::string("test");
1096 let result = treat_as(&value, XmlTypeCode::Integer);
1097 assert!(result.is_err());
1098 }
1099
1100 #[test]
1101 fn test_cast_special_float_values() {
1102 let inf = XmlValue::string("INF");
1103 let result = cast_to(&inf, XmlTypeCode::Float).unwrap();
1104 assert!(result.as_double().unwrap().is_infinite());
1105
1106 let nan = XmlValue::string("NaN");
1107 let result = cast_to(&nan, XmlTypeCode::Double).unwrap();
1108 assert!(result.as_double().unwrap().is_nan());
1109 }
1110
1111 #[test]
1112 fn test_cast_to_integer_subtype() {
1113 let value = XmlValue::string("100");
1114
1115 let result = cast_to_integer_subtype(&value, XmlTypeCode::Byte).unwrap();
1117 assert_eq!(result.type_code, XmlTypeCode::Byte);
1118
1119 let big = XmlValue::string("500");
1121 let result = cast_to_integer_subtype(&big, XmlTypeCode::Byte);
1122 assert!(result.is_err());
1123 }
1124
1125 #[test]
1126 fn test_cast_numeric_to_boolean() {
1127 assert_eq!(
1129 cast_to(&XmlValue::integer(BigInt::from(10)), XmlTypeCode::Boolean)
1130 .unwrap()
1131 .as_boolean(),
1132 Some(true)
1133 );
1134 assert_eq!(
1136 cast_to(&XmlValue::integer(BigInt::from(0)), XmlTypeCode::Boolean)
1137 .unwrap()
1138 .as_boolean(),
1139 Some(false)
1140 );
1141 assert_eq!(
1143 cast_to(&XmlValue::double(f64::NAN), XmlTypeCode::Boolean)
1144 .unwrap()
1145 .as_boolean(),
1146 Some(false)
1147 );
1148 assert_eq!(
1150 cast_to(&XmlValue::double(-0.0), XmlTypeCode::Boolean)
1151 .unwrap()
1152 .as_boolean(),
1153 Some(false)
1154 );
1155 assert_eq!(
1157 cast_to(&XmlValue::float(1.5), XmlTypeCode::Boolean)
1158 .unwrap()
1159 .as_boolean(),
1160 Some(true)
1161 );
1162 assert_eq!(
1164 cast_to(
1165 &XmlValue::decimal(Decimal::new(-11234, 4)),
1166 XmlTypeCode::Boolean
1167 )
1168 .unwrap()
1169 .as_boolean(),
1170 Some(true)
1171 );
1172 }
1173
1174 #[test]
1175 fn test_cast_datetime_to_date() {
1176 let dt = XmlValue::new(
1177 XmlTypeCode::DateTime,
1178 XmlValueKind::Atomic(XmlAtomicValue::DateTime(DateTimeValue {
1179 year: 1999,
1180 month: 5,
1181 day: 31,
1182 hour: 13,
1183 minute: 20,
1184 second: Decimal::ZERO,
1185 timezone: Some(crate::types::value::TimezoneOffset(-300)),
1186 })),
1187 );
1188 let result = cast_to(&dt, XmlTypeCode::Date).unwrap();
1189 assert_eq!(result.type_code, XmlTypeCode::Date);
1190 assert_eq!(result.to_string_value(), "1999-05-31-05:00");
1191 }
1192
1193 #[test]
1194 fn test_cast_datetime_to_time() {
1195 let dt = XmlValue::new(
1196 XmlTypeCode::DateTime,
1197 XmlValueKind::Atomic(XmlAtomicValue::DateTime(DateTimeValue {
1198 year: 1999,
1199 month: 5,
1200 day: 31,
1201 hour: 13,
1202 minute: 20,
1203 second: Decimal::ZERO,
1204 timezone: Some(crate::types::value::TimezoneOffset(-300)),
1205 })),
1206 );
1207 let result = cast_to(&dt, XmlTypeCode::Time).unwrap();
1208 assert_eq!(result.type_code, XmlTypeCode::Time);
1209 assert_eq!(result.to_string_value(), "13:20:00-05:00");
1210 }
1211
1212 #[test]
1213 fn test_cast_date_to_datetime() {
1214 let d = XmlValue::new(
1215 XmlTypeCode::Date,
1216 XmlValueKind::Atomic(XmlAtomicValue::Date(DateValue {
1217 year: 1999,
1218 month: 5,
1219 day: 31,
1220 timezone: Some(crate::types::value::TimezoneOffset::UTC),
1221 })),
1222 );
1223 let result = cast_to(&d, XmlTypeCode::DateTime).unwrap();
1224 assert_eq!(result.type_code, XmlTypeCode::DateTime);
1225 assert_eq!(result.to_string_value(), "1999-05-31T00:00:00Z");
1226 }
1227
1228 #[test]
1229 fn test_cast_datetime_to_gyear() {
1230 let dt = XmlValue::new(
1231 XmlTypeCode::DateTime,
1232 XmlValueKind::Atomic(XmlAtomicValue::DateTime(DateTimeValue {
1233 year: 1999,
1234 month: 5,
1235 day: 31,
1236 hour: 13,
1237 minute: 20,
1238 second: Decimal::ZERO,
1239 timezone: None,
1240 })),
1241 );
1242 let result = cast_to(&dt, XmlTypeCode::GYear).unwrap();
1243 assert_eq!(result.to_string_value(), "1999");
1244
1245 let result = cast_to(&dt, XmlTypeCode::GMonth).unwrap();
1246 assert_eq!(result.to_string_value(), "--05");
1247
1248 let result = cast_to(&dt, XmlTypeCode::GDay).unwrap();
1249 assert_eq!(result.to_string_value(), "---31");
1250
1251 let result = cast_to(&dt, XmlTypeCode::GMonthDay).unwrap();
1252 assert_eq!(result.to_string_value(), "--05-31");
1253
1254 let result = cast_to(&dt, XmlTypeCode::GYearMonth).unwrap();
1255 assert_eq!(result.to_string_value(), "1999-05");
1256 }
1257
1258 #[test]
1259 fn test_cast_duration_to_yearmonth() {
1260 let dur = XmlValue::new(
1261 XmlTypeCode::Duration,
1262 XmlValueKind::Atomic(XmlAtomicValue::Duration(DurationValue {
1263 negative: false,
1264 years: 1,
1265 months: 2,
1266 days: 3,
1267 hours: 10,
1268 minutes: 30,
1269 seconds: Decimal::new(23, 0),
1270 })),
1271 );
1272 let result = cast_to(&dur, XmlTypeCode::YearMonthDuration).unwrap();
1273 assert_eq!(result.to_string_value(), "P1Y2M");
1274
1275 let result = cast_to(&dur, XmlTypeCode::DayTimeDuration).unwrap();
1276 assert_eq!(result.to_string_value(), "P3DT10H30M23S");
1277 }
1278
1279 #[test]
1280 fn test_cast_binary_cross() {
1281 let b64 = XmlValue::new(
1283 XmlTypeCode::Base64Binary,
1284 XmlValueKind::Atomic(XmlAtomicValue::Base64Binary(vec![0xAB, 0xCD])),
1285 );
1286 let result = cast_to(&b64, XmlTypeCode::HexBinary).unwrap();
1287 assert_eq!(result.type_code, XmlTypeCode::HexBinary);
1288 assert_eq!(result.to_string_value(), "ABCD");
1289
1290 let hex = XmlValue::new(
1292 XmlTypeCode::HexBinary,
1293 XmlValueKind::Atomic(XmlAtomicValue::HexBinary(vec![0xFF, 0x00])),
1294 );
1295 let result = cast_to(&hex, XmlTypeCode::Base64Binary).unwrap();
1296 assert_eq!(result.type_code, XmlTypeCode::Base64Binary);
1297 }
1298
1299 #[test]
1300 fn test_cast_string_to_time_with_timezone() {
1301 let value = XmlValue::string("13:20:00-05:00");
1302 let result = cast_to(&value, XmlTypeCode::Time).unwrap();
1303 assert_eq!(result.type_code, XmlTypeCode::Time);
1304 assert_eq!(result.to_string_value(), "13:20:00-05:00");
1305 }
1306
1307 #[test]
1308 fn test_cast_string_to_yearmonth_duration_zero() {
1309 let value = XmlValue::string("P0Y0M");
1310 let result = cast_to(&value, XmlTypeCode::YearMonthDuration).unwrap();
1311 assert_eq!(result.to_string_value(), "P0M");
1312 }
1313}