3131use core:: {
3232 convert:: TryInto ,
3333 fmt:: { Display , Formatter } ,
34+ ops:: Range ,
3435 str:: { self , FromStr } ,
3536} ;
3637
3738pub const LENGTH : usize = 11 ;
3839
3940pub ( crate ) type Bytes = [ u8 ; LENGTH ] ;
4041
41- /// Turkish citizenship ID number.
42+ /// Turkish citizenship ID number. The number is stored as ASCII digits
43+ /// "0".."9" in the structure.
4244#[ derive( Debug , Eq , PartialEq , Hash , Copy , Clone ) ]
4345pub struct TurkishId ( Bytes ) ;
4446
@@ -115,6 +117,8 @@ fn validate(str: &str) -> Result<(), Error> {
115117 return Err ( Error :: InvalidChecksum ) ;
116118 }
117119
120+ // we use euclidian remainder due to the possibility that the final
121+ // checksum wmight be negative.
118122 let first_checksum_computed = ( ( odd_sum * 7 ) - even_sum) . rem_euclid ( 10 ) ;
119123 if first_checksum_computed != first_checksum {
120124 return Err ( Error :: InvalidChecksum ) ;
@@ -134,6 +138,51 @@ impl Display for TurkishId {
134138 }
135139}
136140
141+ #[ derive( Debug , Eq , PartialEq ) ]
142+ pub enum FromSeqError {
143+ OutOfRange ,
144+ }
145+
146+ impl TurkishId {
147+ /// Generate a valid TurkishId from a sequence number by calculating
148+ /// checksums and building the buffer for it.
149+ ///
150+ /// # Arguments
151+ /// - seq - A number between 100,000,000 and 999,999,999
152+ ///
153+ /// # Returns
154+ /// A Result with `TurkishId` if the values are in range, otherwise
155+ /// `FromSeqError`
156+ pub fn from_seq ( seq : u32 ) -> Result < TurkishId , FromSeqError > {
157+ fn to_ascii ( digit : i32 ) -> u8 {
158+ digit as u8 + b'0'
159+ }
160+ const VALID_SEQ_RANGE : Range < u32 > = 100_000_000 ..1_000_000_000 ;
161+ if !VALID_SEQ_RANGE . contains ( & seq) {
162+ return Err ( FromSeqError :: OutOfRange ) ;
163+ }
164+ let mut d: Bytes = [ 0 ; LENGTH ] ;
165+ let mut odd_sum: i32 = 0 ;
166+ let mut even_sum: i32 = 0 ;
167+ let mut divisor = VALID_SEQ_RANGE . start ;
168+ for ( i, item) in d. iter_mut ( ) . enumerate ( ) . take ( 9 ) {
169+ let digit = ( seq / divisor % 10 ) as i32 ;
170+ if i % 2 == 0 {
171+ odd_sum += digit;
172+ } else {
173+ even_sum += digit;
174+ }
175+ * item = to_ascii ( digit) ;
176+ divisor /= 10 ;
177+ }
178+ let first_checksum = ( ( odd_sum * 7 ) - even_sum) . rem_euclid ( 10 ) ;
179+ let second_checksum = ( odd_sum + even_sum + first_checksum) % 10 ;
180+ d[ 9 ] = to_ascii ( first_checksum) ;
181+ d[ 10 ] = to_ascii ( second_checksum) ;
182+ Ok ( TurkishId ( d) )
183+ }
184+ }
185+
137186/// TurkishId can only be constructed from a string despite that it's stored
138187/// as a fixed-length byte array internally.
139188impl FromStr for TurkishId {
0 commit comments