1use num_bigint::BigInt;
17use rust_decimal::Decimal;
18
19#[cfg(feature = "xsd11")]
20use super::sequence::{ItemType, SequenceType};
21use super::validators::{ValidationError, ValidatorRegistry};
22use super::value::{XmlAtomicValue, XmlValue, XmlValueKind};
23use super::{PrimitiveTypeCode, XmlTypeCode};
24
25#[derive(Debug, Clone)]
27pub enum ConversionError {
28 TypeMismatch { from: XmlTypeCode, to: XmlTypeCode },
30 InvalidValue {
32 value: String,
33 target_type: &'static str,
34 message: String,
35 },
36 Overflow {
38 value: String,
39 target_type: &'static str,
40 },
41 EmptySequence,
43 Validation(ValidationError),
45}
46
47impl std::fmt::Display for ConversionError {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 Self::TypeMismatch { from, to } => {
51 write!(f, "XPTY0004: Cannot convert {:?} to {:?}", from, to)
52 }
53 Self::InvalidValue {
54 value,
55 target_type,
56 message,
57 } => {
58 write!(
59 f,
60 "FORG0001: Invalid {} value '{}': {}",
61 target_type, value, message
62 )
63 }
64 Self::Overflow { value, target_type } => {
65 write!(
66 f,
67 "FOAR0002: Overflow converting '{}' to {}",
68 value, target_type
69 )
70 }
71 Self::EmptySequence => {
72 write!(f, "XPTY0004: Empty sequence where value expected")
73 }
74 Self::Validation(e) => write!(f, "{}", e),
75 }
76 }
77}
78
79impl std::error::Error for ConversionError {}
80
81impl From<ValidationError> for ConversionError {
82 fn from(e: ValidationError) -> Self {
83 Self::Validation(e)
84 }
85}
86
87pub type ConversionResult<T> = Result<T, ConversionError>;
89
90pub struct TypeConverter {
94 validators: ValidatorRegistry,
95}
96
97impl TypeConverter {
98 pub fn new() -> Self {
100 Self {
101 validators: ValidatorRegistry::new(),
102 }
103 }
104
105 pub fn with_validators(validators: ValidatorRegistry) -> Self {
107 Self { validators }
108 }
109
110 pub fn cast(&self, value: &XmlValue, target: XmlTypeCode) -> ConversionResult<XmlValue> {
114 if value.type_code == target {
116 return Ok(value.clone());
117 }
118
119 if value.is_untyped() {
121 let str_val = value.to_string_value();
122 return self
123 .validators
124 .validate(target, &str_val)
125 .map_err(ConversionError::from);
126 }
127
128 if !can_cast(value.type_code, target) {
130 return Err(ConversionError::TypeMismatch {
131 from: value.type_code,
132 to: target,
133 });
134 }
135
136 self.perform_cast(value, target)
138 }
139
140 pub fn to_string(&self, value: &XmlValue) -> String {
142 value.to_string_value()
143 }
144
145 pub fn to_boolean(&self, value: &XmlValue) -> ConversionResult<bool> {
147 match &value.value {
148 XmlValueKind::Atomic(XmlAtomicValue::Boolean(b)) => Ok(*b),
149 XmlValueKind::Atomic(XmlAtomicValue::String(s)) => Ok(!s.is_empty()),
150 XmlValueKind::Atomic(XmlAtomicValue::Integer(i)) => Ok(*i != BigInt::from(0)),
151 XmlValueKind::Atomic(XmlAtomicValue::Decimal(d)) => Ok(!d.is_zero()),
152 XmlValueKind::Atomic(XmlAtomicValue::Float(f)) => Ok(*f != 0.0 && !f.is_nan()),
153 XmlValueKind::Atomic(XmlAtomicValue::Double(d)) => Ok(*d != 0.0 && !d.is_nan()),
154 XmlValueKind::UntypedAtomic(s) => Ok(!s.is_empty()),
155 _ => Err(ConversionError::TypeMismatch {
156 from: value.type_code,
157 to: XmlTypeCode::Boolean,
158 }),
159 }
160 }
161
162 pub fn to_double(&self, value: &XmlValue) -> ConversionResult<f64> {
164 match &value.value {
165 XmlValueKind::Atomic(XmlAtomicValue::Double(d)) => Ok(*d),
166 XmlValueKind::Atomic(XmlAtomicValue::Float(f)) => Ok(*f as f64),
167 XmlValueKind::Atomic(XmlAtomicValue::Decimal(d)) => {
168 d.to_string()
169 .parse()
170 .map_err(|_| ConversionError::Overflow {
171 value: d.to_string(),
172 target_type: "double",
173 })
174 }
175 XmlValueKind::Atomic(XmlAtomicValue::Integer(i)) => {
176 i.to_string()
177 .parse()
178 .map_err(|_| ConversionError::Overflow {
179 value: i.to_string(),
180 target_type: "double",
181 })
182 }
183 XmlValueKind::Atomic(XmlAtomicValue::Boolean(b)) => Ok(if *b { 1.0 } else { 0.0 }),
184 XmlValueKind::UntypedAtomic(s) => {
185 s.trim().parse().map_err(|_| ConversionError::InvalidValue {
186 value: s.clone(),
187 target_type: "double",
188 message: "Not a valid number".to_string(),
189 })
190 }
191 _ => Err(ConversionError::TypeMismatch {
192 from: value.type_code,
193 to: XmlTypeCode::Double,
194 }),
195 }
196 }
197
198 pub fn to_decimal(&self, value: &XmlValue) -> ConversionResult<Decimal> {
200 match &value.value {
201 XmlValueKind::Atomic(XmlAtomicValue::Decimal(d)) => Ok(*d),
202 XmlValueKind::Atomic(XmlAtomicValue::Integer(i)) => {
203 i.to_string()
204 .parse()
205 .map_err(|_| ConversionError::Overflow {
206 value: i.to_string(),
207 target_type: "decimal",
208 })
209 }
210 XmlValueKind::Atomic(XmlAtomicValue::Float(f)) => {
211 if f.is_nan() || f.is_infinite() {
212 return Err(ConversionError::InvalidValue {
213 value: f.to_string(),
214 target_type: "decimal",
215 message: "Cannot convert NaN or Infinity to decimal".to_string(),
216 });
217 }
218 Decimal::try_from(*f).map_err(|_| ConversionError::Overflow {
219 value: f.to_string(),
220 target_type: "decimal",
221 })
222 }
223 XmlValueKind::Atomic(XmlAtomicValue::Double(d)) => {
224 if d.is_nan() || d.is_infinite() {
225 return Err(ConversionError::InvalidValue {
226 value: d.to_string(),
227 target_type: "decimal",
228 message: "Cannot convert NaN or Infinity to decimal".to_string(),
229 });
230 }
231 Decimal::try_from(*d).map_err(|_| ConversionError::Overflow {
232 value: d.to_string(),
233 target_type: "decimal",
234 })
235 }
236 XmlValueKind::Atomic(XmlAtomicValue::Boolean(b)) => {
237 Ok(if *b { Decimal::ONE } else { Decimal::ZERO })
238 }
239 XmlValueKind::UntypedAtomic(s) => {
240 s.trim().parse().map_err(|_| ConversionError::InvalidValue {
241 value: s.clone(),
242 target_type: "decimal",
243 message: "Not a valid decimal".to_string(),
244 })
245 }
246 _ => Err(ConversionError::TypeMismatch {
247 from: value.type_code,
248 to: XmlTypeCode::Decimal,
249 }),
250 }
251 }
252
253 pub fn to_integer(&self, value: &XmlValue) -> ConversionResult<BigInt> {
255 match &value.value {
256 XmlValueKind::Atomic(XmlAtomicValue::Integer(i)) => Ok(i.clone()),
257 XmlValueKind::Atomic(XmlAtomicValue::Decimal(d)) => {
258 let truncated = d.trunc();
260 truncated
261 .to_string()
262 .parse()
263 .map_err(|_| ConversionError::Overflow {
264 value: d.to_string(),
265 target_type: "integer",
266 })
267 }
268 XmlValueKind::Atomic(XmlAtomicValue::Float(f)) => {
269 if f.is_nan() || f.is_infinite() {
270 return Err(ConversionError::InvalidValue {
271 value: f.to_string(),
272 target_type: "integer",
273 message: "Cannot convert NaN or Infinity to integer".to_string(),
274 });
275 }
276 let truncated = f.trunc();
277 (truncated as i64)
278 .to_string()
279 .parse()
280 .map_err(|_| ConversionError::Overflow {
281 value: f.to_string(),
282 target_type: "integer",
283 })
284 }
285 XmlValueKind::Atomic(XmlAtomicValue::Double(d)) => {
286 if d.is_nan() || d.is_infinite() {
287 return Err(ConversionError::InvalidValue {
288 value: d.to_string(),
289 target_type: "integer",
290 message: "Cannot convert NaN or Infinity to integer".to_string(),
291 });
292 }
293 let truncated = d.trunc();
294 (truncated as i64)
295 .to_string()
296 .parse()
297 .map_err(|_| ConversionError::Overflow {
298 value: d.to_string(),
299 target_type: "integer",
300 })
301 }
302 XmlValueKind::Atomic(XmlAtomicValue::Boolean(b)) => {
303 Ok(if *b { BigInt::from(1) } else { BigInt::from(0) })
304 }
305 XmlValueKind::UntypedAtomic(s) => {
306 s.trim().parse().map_err(|_| ConversionError::InvalidValue {
307 value: s.clone(),
308 target_type: "integer",
309 message: "Not a valid integer".to_string(),
310 })
311 }
312 _ => Err(ConversionError::TypeMismatch {
313 from: value.type_code,
314 to: XmlTypeCode::Integer,
315 }),
316 }
317 }
318
319 #[cfg(feature = "xsd11")]
321 pub fn matches(&self, value: &XmlValue, seq_type: &SequenceType) -> bool {
322 match &seq_type.item_type {
323 ItemType::AnyItem => true,
324 ItemType::AtomicType(XmlTypeCode::AnyAtomicType) => value.is_atomic(),
325 ItemType::AtomicType(code) => {
326 if value.type_code == *code {
327 return true;
328 }
329 derives_from(value.type_code, *code)
331 }
332 ItemType::AnyNode => false, _ => false,
334 }
335 }
336
337 pub fn promote_numeric(&self, left: XmlTypeCode, right: XmlTypeCode) -> Option<XmlTypeCode> {
341 if !left.is_numeric() || !right.is_numeric() {
342 return None;
343 }
344
345 if left == XmlTypeCode::Double || right == XmlTypeCode::Double {
351 Some(XmlTypeCode::Double)
352 } else if left == XmlTypeCode::Float || right == XmlTypeCode::Float {
353 Some(XmlTypeCode::Float)
354 } else if left == XmlTypeCode::Decimal || right == XmlTypeCode::Decimal {
355 Some(XmlTypeCode::Decimal)
356 } else {
357 Some(XmlTypeCode::Integer)
358 }
359 }
360
361 pub fn promote_to(&self, value: &XmlValue, target: XmlTypeCode) -> ConversionResult<XmlValue> {
363 if value.type_code == target {
364 return Ok(value.clone());
365 }
366
367 match target {
368 XmlTypeCode::Double => {
369 let d = self.to_double(value)?;
370 Ok(XmlValue::double(d))
371 }
372 XmlTypeCode::Float => {
373 let d = self.to_double(value)?;
374 Ok(XmlValue::float(d as f32))
375 }
376 XmlTypeCode::Decimal => {
377 let d = self.to_decimal(value)?;
378 Ok(XmlValue::decimal(d))
379 }
380 XmlTypeCode::Integer => {
381 let i = self.to_integer(value)?;
382 Ok(XmlValue::integer(i))
383 }
384 _ => Err(ConversionError::TypeMismatch {
385 from: value.type_code,
386 to: target,
387 }),
388 }
389 }
390
391 fn perform_cast(&self, value: &XmlValue, target: XmlTypeCode) -> ConversionResult<XmlValue> {
393 let str_val = value.to_string_value();
394
395 match target {
396 XmlTypeCode::Double => {
398 let d = self.to_double(value)?;
399 Ok(XmlValue::double(d))
400 }
401 XmlTypeCode::Float => {
402 let d = self.to_double(value)?;
403 Ok(XmlValue::float(d as f32))
404 }
405 XmlTypeCode::Decimal => {
406 let d = self.to_decimal(value)?;
407 Ok(XmlValue::decimal(d))
408 }
409 XmlTypeCode::Integer => {
410 let i = self.to_integer(value)?;
411 Ok(XmlValue::integer(i))
412 }
413 XmlTypeCode::Boolean => {
414 let b = self.to_boolean(value)?;
415 Ok(XmlValue::boolean(b))
416 }
417 XmlTypeCode::String => Ok(XmlValue::string(str_val)),
418 XmlTypeCode::UntypedAtomic => Ok(XmlValue::untyped(str_val)),
419 _ => self
421 .validators
422 .validate(target, &str_val)
423 .map_err(ConversionError::from),
424 }
425 }
426}
427
428impl Default for TypeConverter {
429 fn default() -> Self {
430 Self::new()
431 }
432}
433
434fn can_cast(from: XmlTypeCode, to: XmlTypeCode) -> bool {
436 if from == to {
438 return true;
439 }
440
441 let from_prim = PrimitiveTypeCode::from_type_code(from);
443 let to_prim = PrimitiveTypeCode::from_type_code(to);
444
445 match (from_prim, to_prim) {
446 (Some(PrimitiveTypeCode::String), _) => true,
448
449 (_, Some(PrimitiveTypeCode::String)) => true,
451
452 (Some(p1), Some(p2)) if p1.is_numeric() && p2.is_numeric() => true,
454
455 (Some(PrimitiveTypeCode::Boolean), Some(p)) if p.is_numeric() => true,
457 (Some(p), Some(PrimitiveTypeCode::Boolean)) if p.is_numeric() => true,
458
459 (Some(PrimitiveTypeCode::DateTime), Some(PrimitiveTypeCode::Date)) => true,
461 (Some(PrimitiveTypeCode::DateTime), Some(PrimitiveTypeCode::Time)) => true,
462 (Some(PrimitiveTypeCode::Date), Some(PrimitiveTypeCode::DateTime)) => true,
463
464 (Some(PrimitiveTypeCode::Duration), Some(PrimitiveTypeCode::Duration)) => true,
466
467 (Some(PrimitiveTypeCode::HexBinary), Some(PrimitiveTypeCode::Base64Binary)) => true,
469 (Some(PrimitiveTypeCode::Base64Binary), Some(PrimitiveTypeCode::HexBinary)) => true,
470
471 _ if from == XmlTypeCode::UntypedAtomic => true,
473
474 _ if to == XmlTypeCode::UntypedAtomic => true,
476
477 _ if from_prim == to_prim => true,
479
480 _ => false,
481 }
482}
483
484#[cfg(feature = "xsd11")]
486fn derives_from(derived: XmlTypeCode, base: XmlTypeCode) -> bool {
487 if derived == base {
488 return true;
489 }
490
491 match base {
493 XmlTypeCode::AnySimpleType => true,
494 XmlTypeCode::AnyAtomicType => {
495 derived != XmlTypeCode::AnySimpleType && derived != XmlTypeCode::AnyType
496 }
497
498 XmlTypeCode::String => matches!(
500 derived,
501 XmlTypeCode::NormalizedString
502 | XmlTypeCode::Token
503 | XmlTypeCode::Language
504 | XmlTypeCode::NmToken
505 | XmlTypeCode::Name
506 | XmlTypeCode::NCName
507 | XmlTypeCode::Id
508 | XmlTypeCode::IdRef
509 | XmlTypeCode::Entity
510 ),
511 XmlTypeCode::NormalizedString => matches!(
512 derived,
513 XmlTypeCode::Token
514 | XmlTypeCode::Language
515 | XmlTypeCode::NmToken
516 | XmlTypeCode::Name
517 | XmlTypeCode::NCName
518 | XmlTypeCode::Id
519 | XmlTypeCode::IdRef
520 | XmlTypeCode::Entity
521 ),
522 XmlTypeCode::Token => matches!(
523 derived,
524 XmlTypeCode::Language
525 | XmlTypeCode::NmToken
526 | XmlTypeCode::Name
527 | XmlTypeCode::NCName
528 | XmlTypeCode::Id
529 | XmlTypeCode::IdRef
530 | XmlTypeCode::Entity
531 ),
532 XmlTypeCode::Name => matches!(
533 derived,
534 XmlTypeCode::NCName | XmlTypeCode::Id | XmlTypeCode::IdRef | XmlTypeCode::Entity
535 ),
536 XmlTypeCode::NCName => matches!(
537 derived,
538 XmlTypeCode::Id | XmlTypeCode::IdRef | XmlTypeCode::Entity
539 ),
540
541 XmlTypeCode::Decimal => matches!(
543 derived,
544 XmlTypeCode::Integer
545 | XmlTypeCode::NonPositiveInteger
546 | XmlTypeCode::NegativeInteger
547 | XmlTypeCode::Long
548 | XmlTypeCode::Int
549 | XmlTypeCode::Short
550 | XmlTypeCode::Byte
551 | XmlTypeCode::NonNegativeInteger
552 | XmlTypeCode::UnsignedLong
553 | XmlTypeCode::UnsignedInt
554 | XmlTypeCode::UnsignedShort
555 | XmlTypeCode::UnsignedByte
556 | XmlTypeCode::PositiveInteger
557 ),
558 XmlTypeCode::Integer => matches!(
559 derived,
560 XmlTypeCode::NonPositiveInteger
561 | XmlTypeCode::NegativeInteger
562 | XmlTypeCode::Long
563 | XmlTypeCode::Int
564 | XmlTypeCode::Short
565 | XmlTypeCode::Byte
566 | XmlTypeCode::NonNegativeInteger
567 | XmlTypeCode::UnsignedLong
568 | XmlTypeCode::UnsignedInt
569 | XmlTypeCode::UnsignedShort
570 | XmlTypeCode::UnsignedByte
571 | XmlTypeCode::PositiveInteger
572 ),
573 XmlTypeCode::Long => matches!(
574 derived,
575 XmlTypeCode::Int | XmlTypeCode::Short | XmlTypeCode::Byte
576 ),
577 XmlTypeCode::Int => matches!(derived, XmlTypeCode::Short | XmlTypeCode::Byte),
578 XmlTypeCode::Short => matches!(derived, XmlTypeCode::Byte),
579 XmlTypeCode::NonNegativeInteger => matches!(
580 derived,
581 XmlTypeCode::UnsignedLong
582 | XmlTypeCode::UnsignedInt
583 | XmlTypeCode::UnsignedShort
584 | XmlTypeCode::UnsignedByte
585 | XmlTypeCode::PositiveInteger
586 ),
587 XmlTypeCode::UnsignedLong => matches!(
588 derived,
589 XmlTypeCode::UnsignedInt | XmlTypeCode::UnsignedShort | XmlTypeCode::UnsignedByte
590 ),
591 XmlTypeCode::UnsignedInt => {
592 matches!(
593 derived,
594 XmlTypeCode::UnsignedShort | XmlTypeCode::UnsignedByte
595 )
596 }
597 XmlTypeCode::UnsignedShort => matches!(derived, XmlTypeCode::UnsignedByte),
598 XmlTypeCode::NonPositiveInteger => matches!(derived, XmlTypeCode::NegativeInteger),
599
600 XmlTypeCode::Duration => {
602 matches!(
603 derived,
604 XmlTypeCode::YearMonthDuration | XmlTypeCode::DayTimeDuration
605 )
606 }
607
608 XmlTypeCode::DateTime => matches!(derived, XmlTypeCode::DateTimeStamp),
610
611 _ => false,
612 }
613}
614
615pub trait IntoXmlValue {
617 fn into_xml_value(self) -> XmlValue;
618}
619
620impl IntoXmlValue for bool {
621 fn into_xml_value(self) -> XmlValue {
622 XmlValue::boolean(self)
623 }
624}
625
626impl IntoXmlValue for i32 {
627 fn into_xml_value(self) -> XmlValue {
628 XmlValue::integer(BigInt::from(self))
629 }
630}
631
632impl IntoXmlValue for i64 {
633 fn into_xml_value(self) -> XmlValue {
634 XmlValue::integer(BigInt::from(self))
635 }
636}
637
638impl IntoXmlValue for f64 {
639 fn into_xml_value(self) -> XmlValue {
640 XmlValue::double(self)
641 }
642}
643
644impl IntoXmlValue for f32 {
645 fn into_xml_value(self) -> XmlValue {
646 XmlValue::float(self)
647 }
648}
649
650impl IntoXmlValue for Decimal {
651 fn into_xml_value(self) -> XmlValue {
652 XmlValue::decimal(self)
653 }
654}
655
656impl IntoXmlValue for BigInt {
657 fn into_xml_value(self) -> XmlValue {
658 XmlValue::integer(self)
659 }
660}
661
662impl IntoXmlValue for String {
663 fn into_xml_value(self) -> XmlValue {
664 XmlValue::string(self)
665 }
666}
667
668impl IntoXmlValue for &str {
669 fn into_xml_value(self) -> XmlValue {
670 XmlValue::string(self)
671 }
672}
673
674#[cfg(test)]
679mod tests {
680 use super::*;
681
682 #[test]
683 fn test_to_boolean() {
684 let converter = TypeConverter::new();
685
686 assert!(converter.to_boolean(&XmlValue::boolean(true)).unwrap());
687 assert!(!converter.to_boolean(&XmlValue::boolean(false)).unwrap());
688 assert!(converter.to_boolean(&XmlValue::string("hello")).unwrap());
689 assert!(!converter.to_boolean(&XmlValue::string("")).unwrap());
690 assert!(converter
691 .to_boolean(&XmlValue::integer(BigInt::from(1)))
692 .unwrap());
693 assert!(!converter
694 .to_boolean(&XmlValue::integer(BigInt::from(0)))
695 .unwrap());
696 assert!(converter.to_boolean(&XmlValue::double(1.5)).unwrap());
697 assert!(!converter.to_boolean(&XmlValue::double(0.0)).unwrap());
698 assert!(!converter.to_boolean(&XmlValue::double(f64::NAN)).unwrap());
699 }
700
701 #[test]
702 fn test_to_double() {
703 let converter = TypeConverter::new();
704
705 assert_eq!(converter.to_double(&XmlValue::double(2.5)).unwrap(), 2.5);
706 assert_eq!(converter.to_double(&XmlValue::float(2.5)).unwrap(), 2.5);
707 assert_eq!(
708 converter
709 .to_double(&XmlValue::integer(BigInt::from(42)))
710 .unwrap(),
711 42.0
712 );
713 assert_eq!(
714 converter
715 .to_double(&XmlValue::decimal(Decimal::new(123, 1)))
716 .unwrap(),
717 12.3
718 );
719 assert_eq!(converter.to_double(&XmlValue::boolean(true)).unwrap(), 1.0);
720 assert_eq!(converter.to_double(&XmlValue::boolean(false)).unwrap(), 0.0);
721 }
722
723 #[test]
724 fn test_to_integer() {
725 let converter = TypeConverter::new();
726
727 assert_eq!(
728 converter
729 .to_integer(&XmlValue::integer(BigInt::from(42)))
730 .unwrap(),
731 BigInt::from(42)
732 );
733 assert_eq!(
734 converter.to_integer(&XmlValue::double(3.7)).unwrap(),
735 BigInt::from(3) );
737 assert_eq!(
738 converter
739 .to_integer(&XmlValue::decimal(Decimal::new(99, 1)))
740 .unwrap(),
741 BigInt::from(9) );
743 }
744
745 #[test]
746 fn test_cast_string_to_integer() {
747 let converter = TypeConverter::new();
748 let str_val = XmlValue::untyped("42");
749
750 let result = converter.cast(&str_val, XmlTypeCode::Integer).unwrap();
751 assert_eq!(result.type_code, XmlTypeCode::Integer);
752 assert_eq!(result.as_integer(), Some(&BigInt::from(42)));
753 }
754
755 #[test]
756 fn test_cast_numeric_promotion() {
757 let converter = TypeConverter::new();
758
759 let int_val = XmlValue::integer(BigInt::from(42));
761 let result = converter.cast(&int_val, XmlTypeCode::Double).unwrap();
762 assert_eq!(result.as_double(), Some(42.0));
763
764 let dbl_val = XmlValue::double(2.5);
766 let result = converter.cast(&dbl_val, XmlTypeCode::String).unwrap();
767 assert!(result.to_string_value().starts_with("2.5"));
768 }
769
770 #[test]
771 fn test_promote_numeric() {
772 let converter = TypeConverter::new();
773
774 assert_eq!(
776 converter.promote_numeric(XmlTypeCode::Integer, XmlTypeCode::Integer),
777 Some(XmlTypeCode::Integer)
778 );
779
780 assert_eq!(
782 converter.promote_numeric(XmlTypeCode::Integer, XmlTypeCode::Decimal),
783 Some(XmlTypeCode::Decimal)
784 );
785
786 assert_eq!(
788 converter.promote_numeric(XmlTypeCode::Decimal, XmlTypeCode::Float),
789 Some(XmlTypeCode::Float)
790 );
791
792 assert_eq!(
794 converter.promote_numeric(XmlTypeCode::Float, XmlTypeCode::Double),
795 Some(XmlTypeCode::Double)
796 );
797
798 assert_eq!(
800 converter.promote_numeric(XmlTypeCode::String, XmlTypeCode::Integer),
801 None
802 );
803 }
804
805 #[cfg(feature = "xsd11")]
806 #[test]
807 fn test_derives_from() {
808 assert!(derives_from(XmlTypeCode::Integer, XmlTypeCode::Decimal));
810 assert!(derives_from(XmlTypeCode::Long, XmlTypeCode::Integer));
811 assert!(derives_from(XmlTypeCode::Int, XmlTypeCode::Long));
812 assert!(derives_from(XmlTypeCode::Short, XmlTypeCode::Int));
813 assert!(derives_from(XmlTypeCode::Byte, XmlTypeCode::Short));
814
815 assert!(derives_from(
817 XmlTypeCode::NormalizedString,
818 XmlTypeCode::String
819 ));
820 assert!(derives_from(
821 XmlTypeCode::Token,
822 XmlTypeCode::NormalizedString
823 ));
824 assert!(derives_from(XmlTypeCode::NCName, XmlTypeCode::Name));
825
826 assert!(derives_from(
828 XmlTypeCode::YearMonthDuration,
829 XmlTypeCode::Duration
830 ));
831 assert!(derives_from(
832 XmlTypeCode::DayTimeDuration,
833 XmlTypeCode::Duration
834 ));
835
836 assert!(!derives_from(XmlTypeCode::String, XmlTypeCode::Integer));
838 assert!(!derives_from(XmlTypeCode::Decimal, XmlTypeCode::Integer));
839 }
840
841 #[test]
842 fn test_can_cast() {
843 assert!(can_cast(XmlTypeCode::String, XmlTypeCode::String));
845
846 assert!(can_cast(XmlTypeCode::String, XmlTypeCode::Integer));
848 assert!(can_cast(XmlTypeCode::Integer, XmlTypeCode::String));
849
850 assert!(can_cast(XmlTypeCode::Integer, XmlTypeCode::Double));
852 assert!(can_cast(XmlTypeCode::Decimal, XmlTypeCode::Float));
853
854 assert!(can_cast(XmlTypeCode::Boolean, XmlTypeCode::Integer));
856 assert!(can_cast(XmlTypeCode::Double, XmlTypeCode::Boolean));
857
858 assert!(can_cast(XmlTypeCode::UntypedAtomic, XmlTypeCode::Date));
860 assert!(can_cast(XmlTypeCode::DateTime, XmlTypeCode::UntypedAtomic));
861 }
862
863 #[test]
864 fn test_into_xml_value() {
865 assert_eq!(true.into_xml_value().as_boolean(), Some(true));
866 assert_eq!(42i32.into_xml_value().as_integer(), Some(&BigInt::from(42)));
867 assert_eq!(2.5f64.into_xml_value().as_double(), Some(2.5));
868 assert_eq!("hello".into_xml_value().as_string(), Some("hello"));
869 }
870}