1use std::convert::TryFrom;
2use std::fmt;
3use std::str::FromStr;
4use thiserror::Error;
5
6#[derive(Debug, Error, PartialEq)]
8pub enum BcdError {
9 #[error("invalid BCD nibble: {0:#X}")]
12 InvalidBcdNibble(u8),
13
14 #[error("input string contains non-digit character: {0}")]
16 NonDigitChar(char),
17
18 #[error("cannot convert BcdNumber to u64: number too large")]
21 Overflow,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct BcdNumber(Vec<u8>);
34
35impl BcdNumber {
36 pub fn from_u64(mut value: u64) -> Self {
48 if value == 0 {
49 return BcdNumber(vec![0x00]);
50 }
51
52 let mut digits = Vec::new();
53 while value > 0 {
54 let d = (value % 10) as u8;
55 digits.push(d);
56 value /= 10;
57 }
58 digits.reverse();
59
60 let mut bytes = Vec::new();
61 if digits.len() == 1 {
62 bytes.push(digits[0] & 0x0F);
63 } else if digits.len() % 2 == 1 {
64 let first = digits[0];
66 bytes.push(first);
67 for chunk in digits[1..].chunks(2) {
68 let hi = chunk[0];
69 let lo = chunk[1];
70 bytes.push((hi << 4) | lo);
71 }
72 } else {
73 for chunk in digits.chunks(2) {
75 let hi = chunk[0];
76 let lo = chunk[1];
77 bytes.push((hi << 4) | lo);
78 }
79 }
80
81 BcdNumber(bytes)
82 }
83
84 pub fn from_str_strict(s: &str) -> Result<Self, BcdError> {
99 let mut digits: Vec<u8> = Vec::new();
100 for c in s.chars() {
101 if !c.is_ascii_digit() {
102 return Err(BcdError::NonDigitChar(c));
103 }
104 digits.push(c as u8 - b'0');
105 }
106
107 while digits.len() > 1 && digits[0] == 0 {
108 digits.remove(0);
109 }
110
111 if digits.is_empty() {
112 digits.push(0);
113 }
114
115 let mut bytes = Vec::new();
116 if digits.len() == 1 {
117 bytes.push(digits[0] & 0x0F);
118 } else if digits.len() % 2 == 1 {
119 let first = digits[0];
120 bytes.push(first);
121 for chunk in digits[1..].chunks(2) {
122 bytes.push((chunk[0] << 4) | chunk[1]);
123 }
124 } else {
125 for chunk in digits.chunks(2) {
126 bytes.push((chunk[0] << 4) | chunk[1]);
127 }
128 }
129
130 Ok(BcdNumber(bytes))
131 }
132
133 pub fn to_u64(&self) -> Result<u64, BcdError> {
148 let mut result = 0u64;
149 for &b in &self.0 {
150 let hi = (b >> 4) & 0x0F;
151 let lo = b & 0x0F;
152
153 if hi > 9 || lo > 9 {
154 return Err(BcdError::InvalidBcdNibble(b));
155 }
156
157 let digit_val = (hi as u64) * 10 + (lo as u64);
158 let (res, overflow1) = result.overflowing_mul(100);
159 let (res, overflow2) = res.overflowing_add(digit_val);
160 if overflow1 || overflow2 {
161 return Err(BcdError::Overflow);
162 }
163 result = res;
164 }
165 Ok(result)
166 }
167
168 pub fn to_string(&self) -> String {
182 let mut s = String::new();
183 for &b in &self.0 {
184 let hi = (b >> 4) & 0x0F;
185 let lo = b & 0x0F;
186 s.push((b'0' + hi) as char);
187 s.push((b'0' + lo) as char);
188 }
189 s
190 }
191
192 pub fn get_digit(&self, index: usize) -> Option<u8> {
207 let total_digits = self.0.len() * 2;
208 if index >= total_digits {
209 return None;
210 }
211
212 let byte_index = index / 2;
213 let nibble_in_byte = index % 2;
214 let b = self.0[byte_index];
215 let digit = if nibble_in_byte == 0 {
216 (b >> 4) & 0x0F
217 } else {
218 b & 0x0F
219 };
220 Some(digit)
221 }
222}
223
224impl FromStr for BcdNumber {
225 type Err = BcdError;
226 fn from_str(s: &str) -> Result<Self, Self::Err> {
234 BcdNumber::from_str_strict(s)
235 }
236}
237
238impl fmt::Display for BcdNumber {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 for &b in &self.0 {
246 let hi = (b >> 4) & 0x0F;
247 let lo = b & 0x0F;
248 write!(f, "{}{}", hi, lo)?;
249 }
250 Ok(())
251 }
252}
253
254impl TryFrom<BcdNumber> for u64 {
255 type Error = BcdError;
256 fn try_from(value: BcdNumber) -> Result<Self, Self::Error> {
261 value.to_u64()
262 }
263}
264
265impl From<u64> for BcdNumber {
266 fn from(value: u64) -> Self {
268 BcdNumber::from_u64(value)
269 }
270}
271
272impl TryFrom<&[u8]> for BcdNumber {
273 type Error = BcdError;
274 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
281 let mut digits = Vec::new();
282 for &b in value {
283 let hi = (b >> 4) & 0x0F;
284 let lo = b & 0x0F;
285 if hi > 9 || lo > 9 {
286 return Err(BcdError::InvalidBcdNibble(b));
287 }
288 digits.push(hi);
289 digits.push(lo);
290 }
291
292 Ok(BcdNumber(value.to_vec()))
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use std::vec;
299
300 use super::*;
301
302 #[test]
303 fn test_from_u64() {
304 assert_eq!(BcdNumber::from_u64(0), BcdNumber(vec![0x00]));
305 assert_eq!(BcdNumber::from_u64(1), BcdNumber(vec![0x01]));
306 assert_eq!(BcdNumber::from_u64(99), BcdNumber(vec![0x99]));
307 assert_eq!(BcdNumber::from_u64(100), BcdNumber(vec![0x01, 0x00]));
308 assert_eq!(BcdNumber::from_u64(101), BcdNumber(vec![0x01, 0x01]));
309 assert_eq!(BcdNumber::from_u64(1000), BcdNumber(vec![0x10, 0x00]));
310 assert_eq!(BcdNumber::from_u64(1234), BcdNumber(vec![0x12, 0x34]));
311 assert_eq!(BcdNumber::from_u64(9999), BcdNumber(vec![0x99, 0x99]));
312 assert_eq!(
313 BcdNumber::from_u64(11010),
314 BcdNumber(vec![0x01, 0x10, 0x10])
315 );
316 }
317
318 #[test]
319 fn test_from_str_strict() {
320 assert_eq!(
321 BcdNumber::from_str_strict("0").unwrap(),
322 BcdNumber(vec![0x00])
323 );
324 assert_eq!(
325 BcdNumber::from_str_strict("1").unwrap(),
326 BcdNumber(vec![0x01])
327 );
328 assert_eq!(
329 BcdNumber::from_str_strict("99").unwrap(),
330 BcdNumber(vec![0x99])
331 );
332 assert_eq!(
333 BcdNumber::from_str_strict("100").unwrap(),
334 BcdNumber(vec![0x01, 0x00])
335 );
336 assert_eq!(
337 BcdNumber::from_str_strict("101").unwrap(),
338 BcdNumber(vec![0x01, 0x01])
339 );
340 assert_eq!(
341 BcdNumber::from_str_strict("1000").unwrap(),
342 BcdNumber(vec![0x10, 0x00])
343 );
344 assert_eq!(
345 BcdNumber::from_str_strict("1234").unwrap(),
346 BcdNumber(vec![0x12, 0x34])
347 );
348 assert_eq!(
349 BcdNumber::from_str_strict("9999").unwrap(),
350 BcdNumber(vec![0x99, 0x99])
351 );
352 assert_eq!(
353 BcdNumber::from_str_strict("11010").unwrap(),
354 BcdNumber(vec![0x01, 0x10, 0x10])
355 );
356 }
357
358 #[test]
359 fn test_from_str_strict_non_digit_char() {
360 assert!(matches!(
361 BcdNumber::from_str_strict("abcd"),
362 Err(BcdError::NonDigitChar('a'))
363 ));
364 }
365
366 #[test]
367 fn test_to_u64() {
368 assert_eq!(BcdNumber(vec![0x00]).to_u64().unwrap(), 0);
369 assert_eq!(BcdNumber(vec![0x01]).to_u64().unwrap(), 1);
370 assert_eq!(BcdNumber(vec![0x99]).to_u64().unwrap(), 99);
371 assert_eq!(BcdNumber(vec![0x01, 0x00]).to_u64().unwrap(), 100);
372 assert_eq!(BcdNumber(vec![0x01, 0x01]).to_u64().unwrap(), 101);
373 assert_eq!(BcdNumber(vec![0x10, 0x00]).to_u64().unwrap(), 1000);
374 assert_eq!(BcdNumber(vec![0x12, 0x34]).to_u64().unwrap(), 1234);
375 assert_eq!(BcdNumber(vec![0x99, 0x99]).to_u64().unwrap(), 9999);
376 assert_eq!(BcdNumber(vec![0x01, 0x10, 0x10]).to_u64().unwrap(), 11010);
377 }
378
379 #[test]
380 fn test_to_u64_invalid_bcd_nibble() {
381 assert!(matches!(
382 BcdNumber(vec![0x1A, 0xFF]).to_u64(),
383 Err(BcdError::InvalidBcdNibble(0x1A))
384 ));
385 }
386
387 #[test]
388 fn test_to_u64_overflow() {
389 assert!(matches!(
390 BcdNumber(vec![
391 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
392 ])
393 .to_u64(),
394 Err(BcdError::Overflow)
395 ));
396 }
397
398 #[test]
399 fn test_to_string() {
400 assert_eq!(BcdNumber(vec![0x0]).to_string(), "00");
401 assert_eq!(BcdNumber(vec![0x00]).to_string(), "00");
402 assert_eq!(BcdNumber(vec![0x1]).to_string(), "01");
403 assert_eq!(BcdNumber(vec![0x01]).to_string(), "01");
404 assert_eq!(BcdNumber(vec![0x99]).to_string(), "99");
405 assert_eq!(BcdNumber(vec![0x1, 0x0]).to_string(), "0100");
406 assert_eq!(BcdNumber(vec![0x01, 0x00]).to_string(), "0100");
407 assert_eq!(BcdNumber(vec![0x1, 0x1]).to_string(), "0101");
408 assert_eq!(BcdNumber(vec![0x01, 0x01]).to_string(), "0101");
409 assert_eq!(BcdNumber(vec![0x10, 0x00]).to_string(), "1000");
410 assert_eq!(BcdNumber(vec![0x12, 0x34]).to_string(), "1234");
411 assert_eq!(BcdNumber(vec![0x99, 0x99]).to_string(), "9999");
412 assert_eq!(BcdNumber(vec![0x01, 0x10, 0x10]).to_string(), "011010");
413 }
414
415 #[test]
416 fn test_get_digit() {
417 {
418 let b = BcdNumber(vec![0x0]);
419 assert_eq!(b.get_digit(0), Some(0));
420 assert_eq!(b.get_digit(1), Some(0));
421 assert_eq!(b.get_digit(2), None);
422 }
423 {
424 let b = BcdNumber(vec![0x00]);
425 assert_eq!(b.get_digit(0), Some(0));
426 assert_eq!(b.get_digit(1), Some(0));
427 assert_eq!(b.get_digit(2), None);
428 }
429 {
430 let b = BcdNumber(vec![0x1]);
431 assert_eq!(b.get_digit(0), Some(0));
432 assert_eq!(b.get_digit(1), Some(1));
433 assert_eq!(b.get_digit(2), None);
434 }
435 {
436 let b = BcdNumber(vec![0x01]);
437 assert_eq!(b.get_digit(0), Some(0));
438 assert_eq!(b.get_digit(1), Some(1));
439 assert_eq!(b.get_digit(2), None);
440 }
441 {
442 let b = BcdNumber(vec![0x99]);
443 assert_eq!(b.get_digit(0), Some(9));
444 assert_eq!(b.get_digit(1), Some(9));
445 assert_eq!(b.get_digit(2), None);
446 }
447 {
448 let b = BcdNumber(vec![0x01, 0x00]);
449 assert_eq!(b.get_digit(0), Some(0));
450 assert_eq!(b.get_digit(1), Some(1));
451 assert_eq!(b.get_digit(2), Some(0));
452 assert_eq!(b.get_digit(3), Some(0));
453 assert_eq!(b.get_digit(4), None);
454 }
455 {
456 let b = BcdNumber(vec![0x1, 0x00]);
457 assert_eq!(b.get_digit(0), Some(0));
458 assert_eq!(b.get_digit(1), Some(1));
459 assert_eq!(b.get_digit(2), Some(0));
460 assert_eq!(b.get_digit(3), Some(0));
461 assert_eq!(b.get_digit(4), None);
462 }
463 {
464 let b = BcdNumber(vec![0x01, 0x01]);
465 assert_eq!(b.get_digit(0), Some(0));
466 assert_eq!(b.get_digit(1), Some(1));
467 assert_eq!(b.get_digit(2), Some(0));
468 assert_eq!(b.get_digit(3), Some(1));
469 assert_eq!(b.get_digit(4), None);
470 }
471 {
472 let b = BcdNumber(vec![0x1, 0x01]);
473 assert_eq!(b.get_digit(0), Some(0));
474 assert_eq!(b.get_digit(1), Some(1));
475 assert_eq!(b.get_digit(2), Some(0));
476 assert_eq!(b.get_digit(3), Some(1));
477 assert_eq!(b.get_digit(4), None);
478 }
479 {
480 let b = BcdNumber(vec![0x10, 0x00]);
481 assert_eq!(b.get_digit(0), Some(1));
482 assert_eq!(b.get_digit(1), Some(0));
483 assert_eq!(b.get_digit(2), Some(0));
484 assert_eq!(b.get_digit(3), Some(0));
485 assert_eq!(b.get_digit(4), None);
486 }
487 {
488 let b = BcdNumber(vec![0x12, 0x34]);
489 assert_eq!(b.get_digit(0), Some(1));
490 assert_eq!(b.get_digit(1), Some(2));
491 assert_eq!(b.get_digit(2), Some(3));
492 assert_eq!(b.get_digit(3), Some(4));
493 assert_eq!(b.get_digit(4), None);
494 }
495 {
496 let b = BcdNumber(vec![0x99, 0x99]);
497 assert_eq!(b.get_digit(0), Some(9));
498 assert_eq!(b.get_digit(1), Some(9));
499 assert_eq!(b.get_digit(2), Some(9));
500 assert_eq!(b.get_digit(3), Some(9));
501 assert_eq!(b.get_digit(4), None);
502 }
503 {
504 let b = BcdNumber(vec![0x01, 0x10, 0x10]);
505 assert_eq!(b.get_digit(0), Some(0));
506 assert_eq!(b.get_digit(1), Some(1));
507 assert_eq!(b.get_digit(2), Some(1));
508 assert_eq!(b.get_digit(3), Some(0));
509 assert_eq!(b.get_digit(4), Some(1));
510 assert_eq!(b.get_digit(5), Some(0));
511 assert_eq!(b.get_digit(6), None);
512 }
513 }
514
515 #[test]
516 fn test_from_str() {
517 assert_eq!("0".parse::<BcdNumber>().unwrap(), BcdNumber(vec![0x00]));
518 assert_eq!("00".parse::<BcdNumber>().unwrap(), BcdNumber(vec![0x00]));
519 assert_eq!("1".parse::<BcdNumber>().unwrap(), BcdNumber(vec![0x01]));
520 assert_eq!("01".parse::<BcdNumber>().unwrap(), BcdNumber(vec![0x01]));
521 assert_eq!("99".parse::<BcdNumber>().unwrap(), BcdNumber(vec![0x99]));
522 assert_eq!(
523 "100".parse::<BcdNumber>().unwrap(),
524 BcdNumber(vec![0x01, 0x00])
525 );
526 assert_eq!(
527 "0100".parse::<BcdNumber>().unwrap(),
528 BcdNumber(vec![0x01, 0x00])
529 );
530 assert_eq!(
531 "101".parse::<BcdNumber>().unwrap(),
532 BcdNumber(vec![0x01, 0x01])
533 );
534 assert_eq!(
535 "0101".parse::<BcdNumber>().unwrap(),
536 BcdNumber(vec![0x01, 0x01])
537 );
538 assert_eq!(
539 "1234".parse::<BcdNumber>().unwrap(),
540 BcdNumber(vec![0x12, 0x34])
541 );
542 assert_eq!(
543 "9999".parse::<BcdNumber>().unwrap(),
544 BcdNumber(vec![0x99, 0x99])
545 );
546 assert_eq!(
547 "011010".parse::<BcdNumber>().unwrap(),
548 BcdNumber(vec![0x01, 0x10, 0x10])
549 );
550 assert_eq!(
551 "0001234".parse::<BcdNumber>().unwrap(),
552 BcdNumber(vec![0x12, 0x34])
553 );
554 }
555
556 #[test]
557 fn test_from_str_error() {
558 assert!(matches!(
559 BcdNumber::from_str("abcd"),
560 Err(BcdError::NonDigitChar('a'))
561 ));
562 assert!(matches!(
563 "abcd".parse::<BcdNumber>(),
564 Err(BcdError::NonDigitChar('a'))
565 ));
566 }
567
568 #[test]
569 fn test_try_from_u64() {
570 assert_eq!(u64::try_from(BcdNumber(vec![0x0])).unwrap(), 0);
571 assert_eq!(u64::try_from(BcdNumber(vec![0x00])).unwrap(), 0);
572 assert_eq!(u64::try_from(BcdNumber(vec![0x1])).unwrap(), 1);
573 assert_eq!(u64::try_from(BcdNumber(vec![0x01])).unwrap(), 1);
574 assert_eq!(u64::try_from(BcdNumber(vec![0x99])).unwrap(), 99);
575 assert_eq!(u64::try_from(BcdNumber(vec![0x1, 0x00])).unwrap(), 100);
576 assert_eq!(u64::try_from(BcdNumber(vec![0x01, 0x00])).unwrap(), 100);
577 assert_eq!(u64::try_from(BcdNumber(vec![0x1, 0x01])).unwrap(), 101);
578 assert_eq!(u64::try_from(BcdNumber(vec![0x01, 0x01])).unwrap(), 101);
579 assert_eq!(u64::try_from(BcdNumber(vec![0x10, 0x00])).unwrap(), 1000);
580 assert_eq!(u64::try_from(BcdNumber(vec![0x12, 0x34])).unwrap(), 1234);
581 assert_eq!(u64::try_from(BcdNumber(vec![0x99, 0x99])).unwrap(), 9999);
582 assert_eq!(
583 u64::try_from(BcdNumber(vec![0x01, 0x10, 0x10])).unwrap(),
584 11010
585 );
586 }
587
588 #[test]
589 fn test_try_from_u64_error() {
590 assert!(matches!(
591 u64::try_from(BcdNumber(vec![0x1A, 0xFF])),
592 Err(BcdError::InvalidBcdNibble(0x1A))
593 ));
594 }
595
596 #[test]
597 fn test_from() {
598 assert_eq!(BcdNumber::from(0), BcdNumber(vec![0x00]));
599 assert_eq!(BcdNumber::from(1), BcdNumber(vec![0x01]));
600 assert_eq!(BcdNumber::from(99), BcdNumber(vec![0x99]));
601 assert_eq!(BcdNumber::from(100), BcdNumber(vec![0x01, 0x00]));
602 assert_eq!(BcdNumber::from(101), BcdNumber(vec![0x01, 0x01]));
603 assert_eq!(BcdNumber::from(1000), BcdNumber(vec![0x10, 0x00]));
604 assert_eq!(BcdNumber::from(1234), BcdNumber(vec![0x12, 0x34]));
605 assert_eq!(BcdNumber::from(9999), BcdNumber(vec![0x99, 0x99]));
606 assert_eq!(BcdNumber::from(11010), BcdNumber(vec![0x01, 0x10, 0x10]));
607 }
608
609 #[test]
610 fn test_try_from_slice() {
611 assert_eq!(
612 BcdNumber::try_from(&[0x0][..]).unwrap(),
613 BcdNumber(vec![0x00])
614 );
615 assert_eq!(
616 BcdNumber::try_from(&[0x00][..]).unwrap(),
617 BcdNumber(vec![0x00])
618 );
619 assert_eq!(
620 BcdNumber::try_from(&[0x1][..]).unwrap(),
621 BcdNumber(vec![0x01])
622 );
623 assert_eq!(
624 BcdNumber::try_from(&[0x01][..]).unwrap(),
625 BcdNumber(vec![0x01])
626 );
627 assert_eq!(
628 BcdNumber::try_from(&[0x99][..]).unwrap(),
629 BcdNumber(vec![0x99])
630 );
631 assert_eq!(
632 BcdNumber::try_from(&[0x1, 0x00][..]).unwrap(),
633 BcdNumber(vec![0x01, 0x00])
634 );
635 assert_eq!(
636 BcdNumber::try_from(&[0x01, 0x00][..]).unwrap(),
637 BcdNumber(vec![0x01, 0x00])
638 );
639 assert_eq!(
640 BcdNumber::try_from(&[0x1, 0x01][..]).unwrap(),
641 BcdNumber(vec![0x01, 0x01])
642 );
643 assert_eq!(
644 BcdNumber::try_from(&[0x01, 0x01][..]).unwrap(),
645 BcdNumber(vec![0x01, 0x01])
646 );
647 assert_eq!(
648 BcdNumber::try_from(&[0x10, 0x00][..]).unwrap(),
649 BcdNumber(vec![0x10, 0x00])
650 );
651 assert_eq!(
652 BcdNumber::try_from(&[0x12, 0x34][..]).unwrap(),
653 BcdNumber(vec![0x12, 0x34])
654 );
655 assert_eq!(
656 BcdNumber::try_from(&[0x99, 0x99][..]).unwrap(),
657 BcdNumber(vec![0x99, 0x99])
658 );
659 assert_eq!(
660 BcdNumber::try_from(&[0x01, 0x10, 0x10][..]).unwrap(),
661 BcdNumber(vec![0x01, 0x10, 0x10])
662 );
663 }
664
665 #[test]
666 fn test_try_from_slice_invalid_bcd_nibble() {
667 assert!(matches!(
668 BcdNumber::try_from(&[0xA][..]),
669 Err(BcdError::InvalidBcdNibble(0xA))
670 ));
671 }
672
673 #[test]
674 fn test_format() {
675 let b = BcdNumber(vec![0x01]);
676 let s = format!("Hello, {} !!", b); println!("{}", s);
678 }
679}