1use crate::errors::XmlError;
13use alloc::format;
14use alloc::string::ToString;
15
16pub const LENGTH_UNLIMITED: i32 = -1;
21
22pub const DURATION_INFINITE_SEC: i32 = 0x7FFF_FFFF;
24
25pub const DURATION_INFINITE_NSEC: u32 = 0x7FFF_FFFF;
27
28pub const DURATION_ZERO_SEC: i32 = 0;
30
31pub const DURATION_ZERO_NSEC: u32 = 0;
33
34pub const TIME_INVALID_SEC: i32 = -1;
39
40pub const TIME_INVALID_NSEC: u32 = 0xFFFF_FFFF;
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub struct Duration {
59 pub sec: i32,
63 pub nanosec: u32,
66}
67
68impl Duration {
69 pub const INFINITE: Self = Self {
71 sec: DURATION_INFINITE_SEC,
72 nanosec: DURATION_INFINITE_NSEC,
73 };
74
75 pub const ZERO: Self = Self {
77 sec: DURATION_ZERO_SEC,
78 nanosec: DURATION_ZERO_NSEC,
79 };
80
81 #[must_use]
83 pub fn is_infinite(&self) -> bool {
84 self.sec == DURATION_INFINITE_SEC && self.nanosec == DURATION_INFINITE_NSEC
85 }
86}
87
88pub fn parse_bool(s: &str) -> Result<bool, XmlError> {
100 let t = s.trim();
101 match t {
102 "1" | "true" | "TRUE" => Ok(true),
103 "0" | "false" | "FALSE" => Ok(false),
104 _ => Err(XmlError::ValueOutOfRange(format!(
105 "boolean expected (true/false/1/0), got `{t}`"
106 ))),
107 }
108}
109
110pub fn parse_long(s: &str) -> Result<i32, XmlError> {
122 let t = s.trim();
123 if t == "LENGTH_UNLIMITED" {
124 return Ok(LENGTH_UNLIMITED);
125 }
126 if let Some(hex) = t.strip_prefix("0x").or_else(|| t.strip_prefix("0X")) {
127 let v = u32::from_str_radix(hex, 16)
128 .map_err(|e| XmlError::ValueOutOfRange(format!("hex long `{t}`: {e}")))?;
129 return Ok(i32::from_ne_bytes(v.to_ne_bytes()));
132 }
133 t.parse::<i32>()
134 .map_err(|e| XmlError::ValueOutOfRange(format!("long `{t}`: {e}")))
135}
136
137pub fn parse_ulong(s: &str) -> Result<u32, XmlError> {
144 let t = s.trim();
145 if let Some(hex) = t.strip_prefix("0x").or_else(|| t.strip_prefix("0X")) {
146 return u32::from_str_radix(hex, 16)
147 .map_err(|e| XmlError::ValueOutOfRange(format!("hex ulong `{t}`: {e}")));
148 }
149 t.parse::<u32>()
150 .map_err(|e| XmlError::ValueOutOfRange(format!("ulong `{t}`: {e}")))
151}
152
153pub fn parse_duration_sec(s: &str) -> Result<i32, XmlError> {
164 let t = s.trim();
165 if t == "DURATION_INFINITY" || t == "DURATION_INFINITE_SEC" {
166 return Ok(DURATION_INFINITE_SEC);
167 }
168 let v = t
169 .parse::<i64>()
170 .map_err(|e| XmlError::ValueOutOfRange(format!("duration_sec `{t}`: {e}")))?;
171 if !(0..=i64::from(DURATION_INFINITE_SEC)).contains(&v) {
172 return Err(XmlError::ValueOutOfRange(format!(
173 "duration_sec `{t}` outside 0..=0x7FFFFFFF"
174 )));
175 }
176 Ok(i32::try_from(v).unwrap_or(0))
178}
179
180pub fn parse_duration_nsec(s: &str) -> Result<u32, XmlError> {
192 let t = s.trim();
193 if t == "DURATION_INFINITY" || t == "DURATION_INFINITE_NSEC" {
194 return Ok(DURATION_INFINITE_NSEC);
195 }
196 let v = t
197 .parse::<u32>()
198 .map_err(|e| XmlError::ValueOutOfRange(format!("duration_nsec `{t}`: {e}")))?;
199 if v > 999_999_999 {
202 return Err(XmlError::ValueOutOfRange(format!(
203 "duration_nsec `{t}` exceeds 999_999_999"
204 )));
205 }
206 Ok(v)
207}
208
209pub fn parse_positive_long_unlimited(s: &str) -> Result<i32, XmlError> {
230 let t = s.trim();
231 if t == "LENGTH_UNLIMITED" {
232 return Ok(LENGTH_UNLIMITED);
233 }
234 let mut chars = t.chars();
237 let first = chars.next().ok_or_else(|| {
238 XmlError::ValueOutOfRange("positiveInteger_UNLIMITED: empty input".to_string())
239 })?;
240 if !('1'..='9').contains(&first) {
241 return Err(XmlError::ValueOutOfRange(format!(
242 "positiveInteger_UNLIMITED `{t}`: erste Ziffer muss 1..9 sein (Spec-Pattern)"
243 )));
244 }
245 if !chars.all(|c| c.is_ascii_digit()) {
246 return Err(XmlError::ValueOutOfRange(format!(
247 "positiveInteger_UNLIMITED `{t}`: nur ASCII-Dezimalziffern erlaubt"
248 )));
249 }
250 t.parse::<i32>()
251 .map_err(|e| XmlError::ValueOutOfRange(format!("positiveInteger_UNLIMITED `{t}`: {e}")))
252}
253
254pub fn parse_octet_sequence(s: &str) -> Result<alloc::vec::Vec<u8>, XmlError> {
276 let trimmed = s.trim();
277 if trimmed.is_empty() {
278 return Ok(alloc::vec::Vec::new());
279 }
280 if trimmed.len() > MAX_STRING_BYTES * 16 {
282 return Err(XmlError::LimitExceeded(format!(
283 "octet sequence ({} bytes) exceeds cap",
284 trimmed.len()
285 )));
286 }
287 let mut out = alloc::vec::Vec::new();
288 for token in trimmed.split(',') {
289 let tok = token.trim();
290 if tok.is_empty() {
291 return Err(XmlError::ValueOutOfRange(
292 "octet sequence: leeres Element (z.B. `1,,2` oder trailing comma)".to_string(),
293 ));
294 }
295 let v = if let Some(hex) = tok.strip_prefix("0x").or_else(|| tok.strip_prefix("0X")) {
296 u16::from_str_radix(hex, 16)
297 .map_err(|e| XmlError::ValueOutOfRange(format!("octet hex `{tok}`: {e}")))?
298 } else {
299 tok.parse::<u16>()
300 .map_err(|e| XmlError::ValueOutOfRange(format!("octet decimal `{tok}`: {e}")))?
301 };
302 let byte = u8::try_from(v)
303 .map_err(|_| XmlError::ValueOutOfRange(format!("octet `{tok}` outside 0..=255")))?;
304 out.push(byte);
305 }
306 Ok(out)
307}
308
309pub fn parse_enum<'a>(s: &str, whitelist: &[&'a str]) -> Result<&'a str, XmlError> {
316 let t = s.trim();
317 whitelist
318 .iter()
319 .find(|allowed| **allowed == t)
320 .copied()
321 .ok_or_else(|| XmlError::BadEnum(t.to_string()))
322}
323
324pub const MAX_STRING_BYTES: usize = 64 * 1024;
326
327pub fn parse_string(s: &str) -> Result<&str, XmlError> {
335 if s.len() > MAX_STRING_BYTES {
336 return Err(XmlError::LimitExceeded(format!(
337 "string ({} bytes) exceeds {MAX_STRING_BYTES}",
338 s.len()
339 )));
340 }
341 Ok(s)
342}
343
344#[cfg(test)]
345#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
346mod tests {
347 use super::*;
348
349 #[test]
352 fn bool_true_variants() {
353 assert!(parse_bool("true").expect("true"));
354 assert!(parse_bool("TRUE").expect("TRUE"));
355 assert!(parse_bool("1").expect("1"));
356 assert!(parse_bool(" true ").expect("trim"));
357 }
358
359 #[test]
360 fn bool_false_variants() {
361 assert!(!parse_bool("false").expect("false"));
362 assert!(!parse_bool("FALSE").expect("FALSE"));
363 assert!(!parse_bool("0").expect("0"));
364 }
365
366 #[test]
367 fn bool_invalid() {
368 let err = parse_bool("yes").expect_err("invalid");
369 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
370 }
371
372 #[test]
375 fn long_decimal() {
376 assert_eq!(parse_long("42").expect("dec"), 42);
377 assert_eq!(parse_long("-1").expect("neg"), -1);
378 assert_eq!(parse_long("2147483647").expect("max"), i32::MAX);
379 assert_eq!(parse_long("-2147483648").expect("min"), i32::MIN);
380 }
381
382 #[test]
383 fn long_hex() {
384 assert_eq!(parse_long("0xFF").expect("hex"), 0xFF);
385 assert_eq!(parse_long("0X80").expect("upper-X"), 0x80);
386 assert_eq!(parse_long("0x80000000").expect("hex-msb"), i32::MIN);
388 assert_eq!(parse_long("0x7FFFFFFF").expect("hex-max"), i32::MAX);
389 }
390
391 #[test]
392 fn long_length_unlimited_symbol() {
393 assert_eq!(parse_long("LENGTH_UNLIMITED").expect("symbol"), -1);
394 }
395
396 #[test]
397 fn long_invalid() {
398 assert!(parse_long("not-a-number").is_err());
399 assert!(parse_long("0xZZ").is_err());
400 }
401
402 #[test]
405 fn ulong_decimal_and_hex() {
406 assert_eq!(parse_ulong("0").expect("0"), 0);
407 assert_eq!(parse_ulong("4294967295").expect("max"), u32::MAX);
408 assert_eq!(parse_ulong("0xFFFFFFFF").expect("hex-max"), u32::MAX);
409 }
410
411 #[test]
412 fn ulong_invalid() {
413 assert!(parse_ulong("-1").is_err());
414 assert!(parse_ulong("4294967296").is_err());
415 }
416
417 #[test]
420 fn duration_sec_normal() {
421 assert_eq!(parse_duration_sec("0").expect("zero"), 0);
422 assert_eq!(parse_duration_sec("123").expect("normal"), 123);
423 }
424
425 #[test]
426 fn duration_sec_infinite_symbols() {
427 assert_eq!(
428 parse_duration_sec("DURATION_INFINITY").expect("infinity"),
429 DURATION_INFINITE_SEC
430 );
431 assert_eq!(
432 parse_duration_sec("DURATION_INFINITE_SEC").expect("infinite_sec"),
433 DURATION_INFINITE_SEC
434 );
435 }
436
437 #[test]
438 fn duration_sec_overflow() {
439 let err = parse_duration_sec("2147483648").expect_err("overflow");
441 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
442 }
443
444 #[test]
445 fn duration_nsec_normal() {
446 assert_eq!(
447 parse_duration_nsec("999999999").expect("max-nsec"),
448 999_999_999
449 );
450 assert_eq!(parse_duration_nsec("0").expect("zero"), 0);
451 }
452
453 #[test]
454 fn duration_nsec_infinite() {
455 assert_eq!(
456 parse_duration_nsec("DURATION_INFINITE_NSEC").expect("infinite"),
457 DURATION_INFINITE_NSEC
458 );
459 assert_eq!(
460 parse_duration_nsec("DURATION_INFINITY").expect("infinity"),
461 DURATION_INFINITE_NSEC
462 );
463 }
464
465 #[test]
466 fn duration_nsec_out_of_range() {
467 let err = parse_duration_nsec("1000000000").expect_err("oor");
468 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
469 }
470
471 #[test]
472 fn duration_constants() {
473 assert!(Duration::INFINITE.is_infinite());
474 assert!(!Duration::ZERO.is_infinite());
475 assert_eq!(Duration::ZERO.sec, 0);
476 assert_eq!(Duration::ZERO.nanosec, 0);
477 }
478
479 #[test]
482 fn enum_match() {
483 let wl = ["KEEP_LAST_HISTORY_QOS", "KEEP_ALL_HISTORY_QOS"];
484 assert_eq!(
485 parse_enum("KEEP_LAST_HISTORY_QOS", &wl).expect("match"),
486 "KEEP_LAST_HISTORY_QOS"
487 );
488 }
489
490 #[test]
491 fn enum_no_match() {
492 let wl = ["A", "B"];
493 let err = parse_enum("C", &wl).expect_err("no-match");
494 assert!(matches!(err, XmlError::BadEnum(_)));
495 }
496
497 #[test]
500 fn string_short_passes() {
501 assert_eq!(parse_string("hello").expect("ok"), "hello");
502 }
503
504 #[test]
505 fn string_too_long_rejected() {
506 let big = "x".repeat(MAX_STRING_BYTES + 1);
507 let err = parse_string(&big).expect_err("too-big");
508 assert!(matches!(err, XmlError::LimitExceeded(_)));
509 }
510
511 #[test]
514 fn spec_constants() {
515 assert_eq!(LENGTH_UNLIMITED, -1);
516 assert_eq!(DURATION_INFINITE_SEC, 0x7FFF_FFFF);
517 assert_eq!(DURATION_INFINITE_NSEC, 0x7FFF_FFFF);
518 }
519
520 #[test]
521 fn time_invalid_constants() {
522 assert_eq!(TIME_INVALID_SEC, -1);
524 assert_eq!(TIME_INVALID_NSEC, 0xFFFF_FFFF);
525 assert_ne!(TIME_INVALID_SEC, DURATION_INFINITE_SEC);
528 assert_ne!(TIME_INVALID_NSEC, DURATION_INFINITE_NSEC);
529 assert_ne!(TIME_INVALID_SEC, DURATION_ZERO_SEC);
530 }
531
532 #[test]
535 fn positive_unlimited_symbol_passes() {
536 assert_eq!(
537 parse_positive_long_unlimited("LENGTH_UNLIMITED").expect("symbol"),
538 LENGTH_UNLIMITED
539 );
540 }
541
542 #[test]
543 fn positive_unlimited_one_to_max_passes() {
544 assert_eq!(parse_positive_long_unlimited("1").expect("1"), 1);
545 assert_eq!(parse_positive_long_unlimited("42").expect("42"), 42);
546 assert_eq!(
547 parse_positive_long_unlimited("2147483647").expect("max"),
548 i32::MAX
549 );
550 }
551
552 #[test]
553 fn positive_unlimited_zero_rejected() {
554 let err = parse_positive_long_unlimited("0").expect_err("zero");
556 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
557 }
558
559 #[test]
560 fn positive_unlimited_negative_rejected() {
561 let err = parse_positive_long_unlimited("-1").expect_err("negative");
562 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
563 }
564
565 #[test]
566 fn positive_unlimited_leading_zero_rejected() {
567 let err = parse_positive_long_unlimited("01").expect_err("leading-zero");
569 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
570 }
571
572 #[test]
573 fn positive_unlimited_hex_rejected() {
574 let err = parse_positive_long_unlimited("0x10").expect_err("hex");
576 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
577 }
578
579 #[test]
580 fn positive_unlimited_overflow_rejected() {
581 let err = parse_positive_long_unlimited("2147483648").expect_err("overflow");
582 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
583 }
584
585 #[test]
586 fn positive_unlimited_empty_rejected() {
587 let err = parse_positive_long_unlimited("").expect_err("empty");
588 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
589 }
590
591 #[test]
594 fn octet_sequence_decimal_basic() {
595 let bytes = parse_octet_sequence("0,1,2,255").expect("ok");
596 assert_eq!(bytes, alloc::vec![0, 1, 2, 255]);
597 }
598
599 #[test]
600 fn octet_sequence_hex_basic() {
601 let bytes = parse_octet_sequence("0x00,0xFF,0x42").expect("ok");
602 assert_eq!(bytes, alloc::vec![0x00, 0xFF, 0x42]);
603 }
604
605 #[test]
606 fn octet_sequence_mixed_decimal_and_hex() {
607 let bytes = parse_octet_sequence("1,0x02,3,0x04").expect("mixed");
609 assert_eq!(bytes, alloc::vec![1, 2, 3, 4]);
610 }
611
612 #[test]
613 fn octet_sequence_whitespace_around_commas() {
614 let bytes = parse_octet_sequence(" 1 , 2 , 3 ").expect("trim");
615 assert_eq!(bytes, alloc::vec![1, 2, 3]);
616 }
617
618 #[test]
619 fn octet_sequence_empty_string_returns_empty_vec() {
620 let bytes = parse_octet_sequence("").expect("empty");
621 assert!(bytes.is_empty());
622 }
623
624 #[test]
625 fn octet_sequence_value_above_255_rejected() {
626 let err = parse_octet_sequence("0,256").expect_err("over");
627 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
628 }
629
630 #[test]
631 fn octet_sequence_negative_rejected() {
632 let err = parse_octet_sequence("0,-1").expect_err("negative");
633 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
634 }
635
636 #[test]
637 fn octet_sequence_trailing_comma_rejected() {
638 let err = parse_octet_sequence("1,2,").expect_err("trailing");
639 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
640 }
641
642 #[test]
643 fn octet_sequence_double_comma_rejected() {
644 let err = parse_octet_sequence("1,,2").expect_err("double");
645 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
646 }
647
648 #[test]
649 fn octet_sequence_non_numeric_token_rejected() {
650 let err = parse_octet_sequence("1,abc,3").expect_err("non-numeric");
651 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
652 }
653
654 #[test]
655 fn octet_sequence_hex_above_255_rejected() {
656 let err = parse_octet_sequence("0x100").expect_err("hex over");
657 assert!(matches!(err, XmlError::ValueOutOfRange(_)));
658 }
659}