1use alloc::string::String;
24use alloc::vec::Vec;
25use core::fmt;
26
27use crate::rand::{simple_seed, xorshift64};
28use crate::uf::State;
29
30const RG_MAX_LEN: usize = 14;
31const SP_BODY_LEN: u8 = 9;
32const SP_FORMATTED_LEN: u8 = 12;
33const SP_BASE_LEN: usize = 8;
34const SP_WEIGHTS: [u32; SP_BASE_LEN] = [9, 8, 7, 6, 5, 4, 3, 2];
35const SP_FORMATTED_DIGIT_POS: [usize; 9] = [0, 1, 3, 4, 5, 7, 8, 9, 11];
36
37const STRUCTURAL_MIN_LEN: u8 = 5;
38const STRUCTURAL_MAX_LEN: u8 = 14;
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum RgError {
42 InvalidLength,
43 InvalidCharacter,
44 InvalidFormat,
45 InvalidCheckDigit,
46 UnsupportedUfForGeneration,
47}
48
49impl fmt::Display for RgError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 f.write_str(match self {
52 Self::InvalidLength => "RG length is outside the accepted range for this UF",
53 Self::InvalidCharacter => "RG contains invalid characters",
54 Self::InvalidFormat => "RG format does not match the canonical mask for this UF",
55 Self::InvalidCheckDigit => "RG check digit is invalid",
56 Self::UnsupportedUfForGeneration => {
57 "RG generation is not supported for this UF (no verified algorithm)"
58 }
59 })
60 }
61}
62
63#[derive(Clone, Copy)]
65struct UfSpec {
66 body_len: Option<u8>,
67 has_check_digit: bool,
68 allow_x_terminator: bool,
69 separators: &'static [(u8, char)],
70 formatted_len: Option<u8>,
71}
72
73const STRUCTURAL_DEFAULT: UfSpec = UfSpec {
74 body_len: None,
75 has_check_digit: false,
76 allow_x_terminator: false,
77 separators: &[],
78 formatted_len: None,
79};
80
81const SP_SPEC: UfSpec = UfSpec {
82 body_len: Some(SP_BODY_LEN),
83 has_check_digit: true,
84 allow_x_terminator: true,
85 separators: &[(2, '.'), (5, '.'), (8, '-')],
86 formatted_len: Some(SP_FORMATTED_LEN),
87};
88
89const fn uf_spec(uf: State) -> UfSpec {
90 match uf {
91 State::SP => SP_SPEC,
92 _ => STRUCTURAL_DEFAULT,
93 }
94}
95
96#[derive(Clone, Copy, PartialEq, Eq, Hash)]
99pub struct Rg {
100 bytes: [u8; RG_MAX_LEN],
101 len: u8,
102 uf: State,
103}
104
105impl Rg {
106 pub fn as_str(&self) -> &str {
108 unsafe { core::str::from_utf8_unchecked(&self.bytes[..self.len as usize]) }
110 }
111
112 pub fn uf(&self) -> State {
114 self.uf
115 }
116
117 pub fn formatted(&self) -> String {
120 format_with_spec(self.as_str(), uf_spec(self.uf)).unwrap_or_else(|| self.as_str().into())
121 }
122
123 pub fn masked(&self) -> String {
128 let s = self.as_str();
129 let spec = uf_spec(self.uf);
130 if spec.has_check_digit && s.len() == SP_BODY_LEN as usize {
131 let mut out = String::with_capacity(SP_FORMATTED_LEN as usize);
132 out.push_str(&s[..2]);
133 out.push('.');
134 out.push_str("***");
135 out.push('.');
136 out.push_str("***");
137 out.push('-');
138 out.push('*');
139 out
140 } else {
141 let mut out = String::with_capacity(s.len());
142 for (i, _) in s.chars().enumerate() {
143 if i < 2 {
144 out.push(s.as_bytes()[i] as char);
145 } else {
146 out.push('*');
147 }
148 }
149 out
150 }
151 }
152
153 pub fn body(&self) -> &str {
158 let spec = uf_spec(self.uf);
159 if spec.has_check_digit && self.len as usize == SP_BODY_LEN as usize {
160 unsafe { core::str::from_utf8_unchecked(&self.bytes[..SP_BASE_LEN]) }
162 } else {
163 self.as_str()
164 }
165 }
166
167 pub fn check_digit(&self) -> Option<u8> {
170 let spec = uf_spec(self.uf);
171 if !spec.has_check_digit {
172 return None;
173 }
174 let last = self.bytes[self.len as usize - 1];
175 if last == b'X' {
176 Some(10)
177 } else {
178 Some(last - b'0')
179 }
180 }
181}
182
183impl AsRef<str> for Rg {
184 fn as_ref(&self) -> &str {
185 self.as_str()
186 }
187}
188
189impl fmt::Display for Rg {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 f.write_str(&self.formatted())
192 }
193}
194
195impl fmt::Debug for Rg {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 write!(f, "Rg({}, {})", self.uf.abbreviation(), self.formatted())
198 }
199}
200
201pub fn remove_symbols(rg: &str, uf: State) -> String {
205 let spec = uf_spec(uf);
206 let mut out = String::with_capacity(rg.len());
207 for c in rg.chars() {
208 if c.is_ascii_digit() {
209 out.push(c);
210 } else if spec.allow_x_terminator && (c == 'X' || c == 'x') {
211 out.push('X');
212 }
213 }
214 out
215}
216
217pub fn is_valid(rg: &str, uf: State) -> bool {
220 let spec = uf_spec(uf);
221 let raw = remove_symbols(rg, uf);
222 if !validate_body_length(&raw, spec) {
223 return false;
224 }
225 if !validate_charset(&raw, spec) {
226 return false;
227 }
228 if spec.has_check_digit {
229 return sp_check_digit_ok(&raw);
230 }
231 true
232}
233
234pub fn is_valid_strict(rg: &str, uf: State) -> Result<(), RgError> {
237 parse_strict(rg, uf).map(|_| ())
238}
239
240pub fn format_rg(rg: &str, uf: State) -> Option<String> {
243 let spec = uf_spec(uf);
244 let raw = remove_symbols(rg, uf);
245 if !validate_body_length(&raw, spec) {
246 return None;
247 }
248 Some(format_with_spec(&raw, spec).unwrap_or(raw))
249}
250
251pub fn compute_check_digit(base: &str, uf: State) -> Option<u8> {
254 if !matches!(uf, State::SP) {
255 return None;
256 }
257 let raw: Vec<u8> = base.bytes().filter(u8::is_ascii_digit).collect();
258 if raw.len() != SP_BASE_LEN {
259 return None;
260 }
261 Some(sp_check_digit(&raw))
262}
263
264pub fn parse_strict(raw: &str, uf: State) -> Result<Rg, RgError> {
266 let spec = uf_spec(uf);
267 let bytes = raw.as_bytes();
268
269 let body = if let Some(len) = spec.formatted_len.filter(|&l| bytes.len() == l as usize) {
270 let _ = len;
271 parse_formatted_sp(bytes)?
272 } else if let Some(body_len) = spec.body_len {
273 if bytes.len() != body_len as usize {
274 return Err(RgError::InvalidLength);
275 }
276 parse_unformatted(bytes, spec)?
277 } else {
278 if bytes.len() < STRUCTURAL_MIN_LEN as usize || bytes.len() > STRUCTURAL_MAX_LEN as usize {
279 return Err(RgError::InvalidLength);
280 }
281 parse_unformatted(bytes, spec)?
282 };
283
284 if spec.has_check_digit && !sp_check_digit_ok_bytes(&body) {
285 return Err(RgError::InvalidCheckDigit);
286 }
287
288 Ok(Rg::from_body(&body, uf))
289}
290
291pub fn generate(uf: State) -> Result<Rg, RgError> {
294 if !matches!(uf, State::SP) {
295 return Err(RgError::UnsupportedUfForGeneration);
296 }
297 let mut seed = simple_seed();
298 let mut digits = [0u8; SP_BASE_LEN];
299 for d in &mut digits {
300 seed = xorshift64(seed);
301 *d = (seed % 10) as u8;
302 }
303 let mut body = [0u8; 9];
304 for (i, &d) in digits.iter().enumerate() {
305 body[i] = d + b'0';
306 }
307 let dv = sp_check_digit(&digits);
308 body[8] = if dv == 10 { b'X' } else { b'0' + dv };
309 Ok(Rg::from_body(&body, State::SP))
310}
311
312impl Rg {
313 fn from_body(body: &[u8], uf: State) -> Self {
314 let mut bytes = [0u8; RG_MAX_LEN];
315 bytes[..body.len()].copy_from_slice(body);
316 Self {
317 bytes,
318 len: body.len() as u8,
319 uf,
320 }
321 }
322}
323
324fn validate_body_length(raw: &str, spec: UfSpec) -> bool {
325 match spec.body_len {
326 Some(n) => raw.len() == n as usize,
327 None => {
328 let n = raw.len();
329 n >= STRUCTURAL_MIN_LEN as usize && n <= STRUCTURAL_MAX_LEN as usize
330 }
331 }
332}
333
334fn validate_charset(raw: &str, spec: UfSpec) -> bool {
335 let bytes = raw.as_bytes();
336 if bytes.is_empty() {
337 return false;
338 }
339 if spec.allow_x_terminator {
340 let (last_idx, rest) = (bytes.len() - 1, &bytes[..bytes.len() - 1]);
341 if !rest.iter().all(u8::is_ascii_digit) {
342 return false;
343 }
344 let last = bytes[last_idx];
345 last.is_ascii_digit() || last == b'X'
346 } else {
347 bytes.iter().all(u8::is_ascii_digit)
348 }
349}
350
351fn parse_unformatted(bytes: &[u8], spec: UfSpec) -> Result<Vec<u8>, RgError> {
352 if !validate_charset(
353 unsafe { core::str::from_utf8_unchecked(bytes) },
355 spec,
356 ) {
357 return Err(RgError::InvalidCharacter);
358 }
359 Ok(bytes.to_vec())
360}
361
362fn parse_formatted_sp(bytes: &[u8]) -> Result<Vec<u8>, RgError> {
363 if bytes[2] != b'.' || bytes[6] != b'.' || bytes[10] != b'-' {
365 return Err(RgError::InvalidFormat);
366 }
367 let mut out = Vec::with_capacity(SP_BODY_LEN as usize);
368 for (i, &idx) in SP_FORMATTED_DIGIT_POS.iter().enumerate() {
369 let b = bytes[idx];
370 let last = i == SP_FORMATTED_DIGIT_POS.len() - 1;
371 if b.is_ascii_digit() || (last && b == b'X') {
372 out.push(b);
373 } else if last && b == b'x' {
374 out.push(b'X');
375 } else {
376 return Err(RgError::InvalidCharacter);
377 }
378 }
379 Ok(out)
380}
381
382fn format_with_spec(body: &str, spec: UfSpec) -> Option<String> {
383 if spec.separators.is_empty() {
384 return None;
385 }
386 let body_len = spec.body_len? as usize;
387 if body.len() != body_len {
388 return None;
389 }
390 let total = body_len + spec.separators.len();
391 let mut out = String::with_capacity(total);
392 let mut sep_iter = spec.separators.iter().peekable();
393 for (i, ch) in body.chars().enumerate() {
394 while let Some(&&(pos, sep_ch)) = sep_iter.peek() {
395 if pos as usize == i && i != 0 {
396 out.push(sep_ch);
397 sep_iter.next();
398 } else {
399 break;
400 }
401 }
402 out.push(ch);
403 }
404 Some(out)
405}
406
407fn sp_check_digit(digits: &[u8]) -> u8 {
408 let sum: u32 = digits
409 .iter()
410 .zip(SP_WEIGHTS.iter())
411 .map(|(&d, &w)| u32::from(d) * w)
412 .sum();
413 (sum % 11) as u8
414}
415
416fn sp_check_digit_ok(body: &str) -> bool {
417 sp_check_digit_ok_bytes(body.as_bytes())
418}
419
420fn sp_check_digit_ok_bytes(bytes: &[u8]) -> bool {
421 if bytes.len() != SP_BODY_LEN as usize {
422 return false;
423 }
424 let mut digits = [0u8; SP_BASE_LEN];
425 for (i, &b) in bytes[..SP_BASE_LEN].iter().enumerate() {
426 if !b.is_ascii_digit() {
427 return false;
428 }
429 digits[i] = b - b'0';
430 }
431 let expected = sp_check_digit(&digits);
432 let last = bytes[SP_BASE_LEN];
433 let actual = if last == b'X' {
434 10
435 } else if last.is_ascii_digit() {
436 last - b'0'
437 } else {
438 return false;
439 };
440 actual == expected
441}
442
443#[cfg(test)]
444mod tests {
445 use super::*;
446 use alloc::string::ToString;
447
448 #[test]
449 fn real_rg_29465327_2() {
450 assert!(is_valid("294653272", State::SP));
452 assert!(is_valid("29.465.327-2", State::SP));
453 let parsed = parse_strict("294653272", State::SP).unwrap();
454 assert_eq!(parsed.as_str(), "294653272");
455 assert_eq!(parsed.check_digit(), Some(2));
456 assert_eq!(parsed.formatted(), "29.465.327-2");
457 let parsed_fmt = parse_strict("29.465.327-2", State::SP).unwrap();
458 assert_eq!(parsed_fmt, parsed);
459 }
460
461 #[test]
462 fn real_rg_39406714_9() {
463 assert!(is_valid("394067149", State::SP));
465 assert!(is_valid("39.406.714-9", State::SP));
466 let parsed = parse_strict("394067149", State::SP).unwrap();
467 assert_eq!(parsed.as_str(), "394067149");
468 assert_eq!(parsed.check_digit(), Some(9));
469 assert_eq!(parsed.formatted(), "39.406.714-9");
470 let parsed_fmt = parse_strict("39.406.714-9", State::SP).unwrap();
471 assert_eq!(parsed_fmt, parsed);
472 }
473
474 #[test]
475 fn compute_check_digit_real_rgs() {
476 assert_eq!(compute_check_digit("29465327", State::SP), Some(2));
477 assert_eq!(compute_check_digit("39406714", State::SP), Some(9));
478 }
479
480 #[test]
481 fn format_real_rgs() {
482 assert_eq!(
483 format_rg("294653272", State::SP),
484 Some("29.465.327-2".into())
485 );
486 assert_eq!(
487 format_rg("394067149", State::SP),
488 Some("39.406.714-9".into())
489 );
490 }
491
492 #[test]
493 fn sp_check_digit_known_values() {
494 assert_eq!(sp_check_digit(&[1, 2, 3, 4, 5, 6, 7, 8]), 2);
496 assert_eq!(sp_check_digit(&[4, 4, 4, 4, 4, 4, 4, 4]), 0);
498 assert_eq!(sp_check_digit(&[1, 1, 1, 1, 1, 1, 1, 1]), 0);
500 assert_eq!(sp_check_digit(&[6, 0, 0, 0, 0, 0, 0, 0]), 10);
502 }
503
504 #[test]
505 fn is_valid_sp_accepts_valid() {
506 assert!(is_valid("123456782", State::SP));
507 assert!(is_valid("12.345.678-2", State::SP));
508 }
509
510 #[test]
511 fn is_valid_sp_rejects_wrong_dv() {
512 assert!(!is_valid("123456789", State::SP));
513 assert!(!is_valid("12.345.678-9", State::SP));
514 assert!(!is_valid("294653271", State::SP));
515 }
516
517 #[test]
518 fn is_valid_sp_x_terminator() {
519 assert!(is_valid("60000000X", State::SP));
520 assert!(is_valid("60.000.000-X", State::SP));
521 assert!(is_valid("60.000.000-x", State::SP));
522 }
523
524 #[test]
525 fn is_valid_sp_lenient_strips_garbage() {
526 let s = "123456782";
527 let garbage = alloc::format!("{}!{}@{}#{}", &s[0..2], &s[2..5], &s[5..8], &s[8..9]);
528 assert!(is_valid(&garbage, State::SP));
529 }
530
531 #[test]
532 fn is_valid_sp_rejects_wrong_length() {
533 assert!(!is_valid("", State::SP));
534 assert!(!is_valid("12345", State::SP));
535 assert!(!is_valid("1234567890", State::SP));
536 }
537
538 #[test]
539 fn structural_other_uf_accepts_any_digits_in_range() {
540 assert!(is_valid("12345", State::RJ));
541 assert!(is_valid("1234567890", State::MG));
542 assert!(is_valid("12345678901234", State::PR));
543 }
544
545 #[test]
546 fn structural_other_uf_rejects_too_short_or_long() {
547 assert!(!is_valid("1234", State::RJ));
548 assert!(!is_valid("123456789012345", State::RJ));
549 }
550
551 #[test]
552 fn structural_other_uf_rejects_letters() {
553 assert!(!is_valid("abcdef", State::RJ));
554 assert!(!is_valid("", State::RJ));
555 }
556
557 #[test]
558 fn strict_sp_accepts_unformatted() {
559 assert!(is_valid_strict("123456782", State::SP).is_ok());
560 assert!(is_valid_strict("294653272", State::SP).is_ok());
561 }
562
563 #[test]
564 fn strict_sp_accepts_formatted() {
565 assert!(is_valid_strict("12.345.678-2", State::SP).is_ok());
566 assert!(is_valid_strict("29.465.327-2", State::SP).is_ok());
567 }
568
569 #[test]
570 fn strict_sp_rejects_misplaced_separators() {
571 assert_eq!(
572 is_valid_strict("123.45.678-2", State::SP),
573 Err(RgError::InvalidFormat)
574 );
575 assert_eq!(
576 is_valid_strict("12.345.6782-", State::SP),
577 Err(RgError::InvalidFormat)
578 );
579 }
580
581 #[test]
582 fn strict_sp_rejects_garbage() {
583 assert_eq!(
584 is_valid_strict("12.345.678!2", State::SP),
585 Err(RgError::InvalidFormat)
586 );
587 }
588
589 #[test]
590 fn strict_sp_rejects_bad_dv() {
591 assert_eq!(
592 is_valid_strict("123456789", State::SP),
593 Err(RgError::InvalidCheckDigit)
594 );
595 }
596
597 #[test]
598 fn strict_sp_rejects_x_in_middle() {
599 assert_eq!(
600 is_valid_strict("1234X6782", State::SP),
601 Err(RgError::InvalidCharacter)
602 );
603 }
604
605 #[test]
606 fn strict_other_uf_accepts_digits_only() {
607 assert!(is_valid_strict("1234567", State::RJ).is_ok());
608 }
609
610 #[test]
611 fn strict_other_uf_rejects_separators() {
612 assert!(is_valid_strict("12.345.67", State::RJ).is_err());
613 }
614
615 #[test]
616 fn parse_sp_roundtrip() {
617 let parsed = parse_strict("123456782", State::SP).unwrap();
618 assert_eq!(parsed.as_str(), "123456782");
619 let parsed_fmt = parse_strict("12.345.678-2", State::SP).unwrap();
620 assert_eq!(parsed_fmt, parsed);
621 }
622
623 #[test]
624 fn parse_sp_x_terminator() {
625 let parsed = parse_strict("60.000.000-X", State::SP).unwrap();
626 assert_eq!(parsed.as_str(), "60000000X");
627 assert_eq!(parsed.check_digit(), Some(10));
628 }
629
630 #[test]
631 fn parse_other_uf_returns_digits() {
632 let parsed = parse_strict("1234567", State::RJ).unwrap();
633 assert_eq!(parsed.as_str(), "1234567");
634 assert_eq!(parsed.uf(), State::RJ);
635 assert_eq!(parsed.check_digit(), None);
636 }
637
638 #[test]
639 fn format_sp_inserts_separators() {
640 assert_eq!(
641 format_rg("123456782", State::SP),
642 Some("12.345.678-2".into())
643 );
644 assert_eq!(
645 format_rg("60000000X", State::SP),
646 Some("60.000.000-X".into())
647 );
648 }
649
650 #[test]
651 fn format_sp_passes_through_already_formatted() {
652 assert_eq!(
653 format_rg("12.345.678-2", State::SP),
654 Some("12.345.678-2".into())
655 );
656 }
657
658 #[test]
659 fn format_other_uf_returns_digits_unchanged() {
660 assert_eq!(format_rg("1234567", State::RJ), Some("1234567".into()));
661 }
662
663 #[test]
664 fn format_returns_none_on_bad_length() {
665 assert_eq!(format_rg("12", State::SP), None);
666 assert_eq!(format_rg("12", State::RJ), None);
667 }
668
669 #[test]
670 fn remove_symbols_sp_keeps_x() {
671 assert_eq!(remove_symbols("60.000.000-X", State::SP), "60000000X");
672 assert_eq!(remove_symbols("60.000.000-x", State::SP), "60000000X");
673 }
674
675 #[test]
676 fn remove_symbols_other_uf_drops_letters() {
677 assert_eq!(remove_symbols("12.345-67", State::RJ), "1234567");
678 assert_eq!(remove_symbols("X1234567", State::RJ), "1234567");
679 }
680
681 #[test]
682 fn compute_check_digit_sp() {
683 assert_eq!(compute_check_digit("12345678", State::SP), Some(2));
684 assert_eq!(compute_check_digit("44444444", State::SP), Some(0));
685 assert_eq!(compute_check_digit("60000000", State::SP), Some(10));
686 }
687
688 #[test]
689 fn compute_check_digit_rejects_other_uf() {
690 assert_eq!(compute_check_digit("1234567", State::RJ), None);
691 }
692
693 #[test]
694 fn compute_check_digit_rejects_bad_length() {
695 assert_eq!(compute_check_digit("1234567", State::SP), None);
696 assert_eq!(compute_check_digit("123456789", State::SP), None);
697 }
698
699 #[test]
700 fn generate_produces_valid() {
701 for _ in 0..100 {
702 let rg = generate(State::SP).unwrap();
703 assert!(is_valid(rg.as_str(), State::SP));
704 let parsed = parse_strict(rg.as_str(), State::SP).unwrap();
705 assert_eq!(parsed, rg);
706 }
707 }
708
709 #[test]
710 fn generate_format_roundtrip() {
711 for _ in 0..100 {
712 let rg = generate(State::SP).unwrap();
713 let formatted = rg.formatted();
714 let parsed = parse_strict(&formatted, State::SP).unwrap();
715 assert_eq!(parsed, rg);
716 }
717 }
718
719 #[test]
720 fn generate_ok_others_err() {
721 assert!(generate(State::SP).is_ok());
722 assert_eq!(
723 generate(State::RJ),
724 Err(RgError::UnsupportedUfForGeneration)
725 );
726 }
727
728 #[test]
729 fn masked_sp_real_rgs() {
730 let rg = parse_strict("294653272", State::SP).unwrap();
731 assert_eq!(rg.masked(), "29.***.***-*");
732 let rg = parse_strict("60000000X", State::SP).unwrap();
733 assert_eq!(rg.masked(), "60.***.***-*");
734 }
735
736 #[test]
737 fn masked_other_uf() {
738 let rg = parse_strict("1234567", State::RJ).unwrap();
739 assert_eq!(rg.masked(), "12*****");
740 }
741
742 #[test]
743 fn masked_generated_sp() {
744 let rg = generate(State::SP).unwrap();
745 let m = rg.masked();
746 assert!(m.starts_with(&rg.as_str()[..2]));
747 assert_eq!(m, alloc::format!("{}.***.***-*", &rg.as_str()[..2]));
748 }
749
750 #[test]
751 fn body_sp_real_rgs() {
752 let rg = parse_strict("294653272", State::SP).unwrap();
753 assert_eq!(rg.body(), "29465327");
754 let rg = parse_strict("60000000X", State::SP).unwrap();
755 assert_eq!(rg.body(), "60000000");
756 }
757
758 #[test]
759 fn body_other_uf_returns_full() {
760 let rg = parse_strict("1234567", State::RJ).unwrap();
761 assert_eq!(rg.body(), "1234567");
762 }
763
764 #[test]
765 fn rg_is_copy() {
766 let rg = generate(State::SP).unwrap();
767 let copy = rg;
768 assert_eq!(rg, copy);
769 }
770
771 #[test]
772 fn rg_as_ref_str() {
773 let rg = generate(State::SP).unwrap();
774 let r: &str = rg.as_ref();
775 assert_eq!(r, rg.as_str());
776 }
777
778 #[test]
779 fn debug_format_includes_uf() {
780 let rg = parse_strict("294653272", State::SP).unwrap();
781 let dbg = alloc::format!("{rg:?}");
782 assert!(dbg.starts_with("Rg(SP, "));
783 assert!(dbg.ends_with(')'));
784 }
785
786 #[test]
787 fn display_uses_formatted() {
788 let rg = parse_strict("294653272", State::SP).unwrap();
789 assert_eq!(rg.to_string(), "29.465.327-2");
790 }
791
792 #[test]
793 fn other_uf_display_passes_through() {
794 let rg = parse_strict("1234567", State::RJ).unwrap();
795 assert_eq!(rg.to_string(), "1234567");
796 }
797}