@@ -291,16 +291,18 @@ impl FromStr for Iban {
291291 let ch = characters
292292 . next ( )
293293 . filter ( u8:: is_ascii_uppercase)
294+ . map ( char:: from)
294295 . ok_or ( ParseError :: CountryCode ) ?;
295- iban. push ( char :: from ( ch ) ) ;
296+ iban. push ( ch ) ;
296297 }
297298
298299 for _ in 0 ..2 {
299300 let ch = characters
300301 . next ( )
301302 . filter ( u8:: is_ascii_digit)
303+ . map ( char:: from)
302304 . ok_or ( ParseError :: CheckDigit ) ?;
303- iban. push ( char :: from ( ch ) ) ;
305+ iban. push ( ch ) ;
304306 }
305307
306308 let country_code = & iban[ ..2 ] ;
@@ -328,11 +330,7 @@ impl FromStr for Iban {
328330 . map_err ( |_| ParseError :: InvalidLength ) ?;
329331 }
330332
331- if validation. next ( ) . is_some ( ) {
332- return Err ( ParseError :: InvalidLength ) ;
333- }
334-
335- if expected_length != iban. len ( ) {
333+ if validation. next ( ) . is_some ( ) || expected_length != iban. len ( ) {
336334 return Err ( ParseError :: InvalidLength ) ;
337335 }
338336
@@ -414,52 +412,40 @@ impl Iban {
414412 country_code : & str ,
415413 rng : & mut R ,
416414 ) -> Result < Self , ParseError > {
417- let mut iban = ArrayString :: < IBAN_MAX_LENGTH > :: new ( ) ;
418- let mut country_code = country_code. as_bytes ( ) . iter ( ) . map ( u8:: to_ascii_uppercase) ;
415+ use arrayvec:: ArrayVec ;
419416
420- for _ in 0 ..2 {
421- let ch = country_code
422- . next ( )
423- . filter ( u8:: is_ascii_uppercase)
424- . ok_or ( ParseError :: CountryCode ) ?;
425- iban. push ( char:: from ( ch) ) ;
426- }
427-
428- if country_code. next ( ) . is_some ( ) || iban. len ( ) != 2 {
429- return Err ( ParseError :: UnknownCountry ) ;
430- }
417+ use crate :: util:: array_vec_to_string;
431418
432- iban. push_str ( "00" ) ;
419+ let mut country_code =
420+ ArrayString :: < 2 > :: from_str ( country_code) . map_err ( |_| ParseError :: CountryCode ) ?;
421+ country_code. make_ascii_uppercase ( ) ;
433422
434423 let & ( expected_length, validation, ..) = COUNTRIES
435- . get ( & iban [ .. 2 ] )
424+ . get ( & country_code )
436425 . ok_or ( ParseError :: UnknownCountry ) ?;
437426
438- let bban_chars = validation
439- . iter ( )
440- . flat_map ( | ( count , character_type ) | ( 0 .. * count ) . map ( move |_| character_type ) )
441- . skip ( 4 )
442- . map ( |character_type| char :: from ( character_type . rand ( rng ) ) ) ;
443-
444- for character in bban_chars {
445- iban . try_push ( character )
446- . map_err ( |_| ParseError :: InvalidLength ) ? ;
447- }
427+ let mut iban = ArrayVec :: < u8 , IBAN_MAX_LENGTH > :: new ( ) ;
428+ iban . extend ( country_code . as_bytes ( ) . iter ( ) . copied ( ) ) ;
429+ iban . extend ( b"00" . iter ( ) . copied ( ) ) ;
430+ iban . extend (
431+ validation
432+ . iter ( )
433+ . flat_map ( | ( count , character_type ) | ( 0 .. * count ) . map ( move |_| character_type ) )
434+ . skip ( 4 )
435+ . map ( |character_type| character_type . rand ( rng ) . to_ascii_uppercase ( ) ) ,
436+ ) ;
448437
449438 debug_assert_eq ! ( iban. len( ) , expected_length) ;
450439
451- let check_digits = 98 - calculate_checksum ( iban. as_bytes ( ) ) ;
452- #[ allow( clippy:: cast_possible_truncation) ]
453- let check_digits = [
454- b'0' + ( check_digits / 10 ) as u8 ,
455- b'0' + ( check_digits % 10 ) as u8 ,
456- ] ;
457-
458- // TODO: Figure out a way to swap out the characters without unsafe.
459- // SAFETY: All of the characters generated are ASCII, so there are no issues with character boundries.
460- unsafe { & mut iban. as_bytes_mut ( ) [ 2 ..4 ] } . copy_from_slice ( & check_digits) ;
440+ let check_digits = 98 - calculate_checksum ( & iban) ;
441+ // check_digits is between 2..98, as such dividing by 10 will always give only a single digit.
442+ iban[ 2 ..4 ] . copy_from_slice ( & [
443+ b'0' + ( ( check_digits / 10 ) & 0xFF ) as u8 ,
444+ b'0' + ( ( check_digits % 10 ) & 0xFF ) as u8 ,
445+ ] ) ;
461446
462- Ok ( Self ( iban) )
447+ // All characters of iban are ASCII, and as a subset of UTF-8 should always be valid in an ArrayString.
448+ Ok ( Self ( array_vec_to_string ( iban) ) )
463449 }
464450}
465451
@@ -854,7 +840,11 @@ mod tests {
854840 // Test that we can construct a random iban for every country
855841 // we support.
856842 for country in crate :: COUNTRIES . keys ( ) {
857- let _ = Iban :: rand ( country, & mut rng) ;
843+ let rand_iban = Iban :: rand ( country, & mut rng) . expect ( "generated iban" ) ;
844+
845+ let parsed_iban = Iban :: parse ( & rand_iban) . expect ( "iban parses" ) ;
846+
847+ assert_eq ! ( rand_iban, parsed_iban) ;
858848 }
859849 }
860850}
0 commit comments