1use core::{
14 convert::TryFrom,
15 fmt::{self, Write},
16 str::FromStr,
17};
18
19#[cfg(all(feature = "std", feature = "rand"))]
20use rand::thread_rng;
21
22#[cfg(feature = "rand")]
23use rand::{
24 distributions::{Distribution, Standard},
25 Rng,
26};
27
28#[cfg(feature = "serde")]
29use serde::*;
30
31#[derive(Debug, PartialEq, Eq)]
33#[non_exhaustive]
34pub enum ParseCpfError {
35 Empty,
36 InvalidCharacter(char, usize),
37 InvalidNumber,
38}
39
40impl fmt::Display for ParseCpfError {
41 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42 use ParseCpfError::*;
43 match self {
44 Empty => write!(f, "empty"),
45 InvalidCharacter(ch, offset) => {
46 write!(f, "invalid character `{ch}` at offset {offset}")
47 }
48 InvalidNumber => write!(f, "invalid CPF number"),
49 }
50 }
51}
52
53impl core::error::Error for ParseCpfError {}
54
55#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
58pub struct Cpf([u8; 11]);
59
60impl Cpf {
61 pub fn from_slice(slice: &[u8]) -> Result<Self, ParseCpfError> {
87 let mut numbers = [0; 11];
88 match slice.len() {
89 0 => return Err(ParseCpfError::Empty),
90 len @ (9 | 11) => numbers[..len].copy_from_slice(slice),
91 _ => return Err(ParseCpfError::InvalidNumber),
92 }
93
94 if numbers.iter().any(|&x| x > 9) {
96 return Err(ParseCpfError::InvalidNumber);
97 }
98
99 let first_number = numbers[0];
101 if slice.len() == 11 && numbers.iter().all(|&x| x == first_number) {
102 return Err(ParseCpfError::InvalidNumber);
103 }
104
105 for i in 0..=1 {
106 let remainder = calc_remainder(numbers, i);
107 let check_digit = numbers[9 + i];
108
109 if slice.len() < 11 {
110 numbers[9 + i] = remainder; } else if remainder != check_digit {
112 return Err(ParseCpfError::InvalidNumber);
113 }
114 }
115
116 Ok(Cpf(numbers))
117 }
118
119 #[inline]
130 pub fn as_bytes(&self) -> &[u8; 11] {
131 &self.0
132 }
133
134 #[cfg(all(feature = "std", feature = "rand"))]
145 #[inline]
146 pub fn generate() -> Self {
147 thread_rng().gen()
148 }
149}
150
151impl AsRef<[u8]> for Cpf {
152 #[inline]
153 fn as_ref(&self) -> &[u8] {
154 self.as_bytes()
155 }
156}
157
158impl From<Cpf> for [u8; 11] {
159 #[inline]
160 fn from(cpf: Cpf) -> [u8; 11] {
161 cpf.0
162 }
163}
164
165impl TryFrom<&[u8]> for Cpf {
166 type Error = ParseCpfError;
167
168 #[inline]
169 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
170 Self::from_slice(value)
171 }
172}
173
174impl TryFrom<&[u8; 11]> for Cpf {
175 type Error = ParseCpfError;
176
177 #[inline]
178 fn try_from(value: &[u8; 11]) -> Result<Self, Self::Error> {
179 Self::from_slice(value)
180 }
181}
182
183impl fmt::Debug for Cpf {
184 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 write!(f, "Cpf(\"{self}\")")
186 }
187}
188
189impl fmt::Display for Cpf {
190 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191 for (i, number) in self.0.iter().enumerate() {
192 match i {
193 3 | 6 => f.write_char('.')?,
194 9 => f.write_char('-')?,
195 _ => (),
196 }
197 number.fmt(f)?;
198 }
199 Ok(())
200 }
201}
202
203impl FromStr for Cpf {
204 type Err = ParseCpfError;
205
206 fn from_str(s: &str) -> Result<Self, Self::Err> {
207 let mut numbers = [0; 11];
208
209 if s.is_empty() {
210 return Err(ParseCpfError::Empty);
211 }
212
213 let mut i = 0;
215 let mut has_dot = false;
216 for (offset, ch) in s.chars().enumerate() {
217 match (ch, offset) {
218 ('0'..='9', _) => {
219 if i < 11 {
220 numbers[i] = unsafe { ch.to_digit(10).unwrap_unchecked() as u8 };
222 i += 1;
223 } else {
224 return Err(ParseCpfError::InvalidNumber);
225 }
226 }
227 ('.', 3 | 7) => has_dot = true,
228 ('-' | '/', 11) if has_dot => continue,
229 ('-' | '/', 9) if !has_dot => continue,
230 _ => return Err(ParseCpfError::InvalidCharacter(ch, offset)),
231 }
232 }
233
234 if i != 11 {
236 return Err(ParseCpfError::InvalidNumber);
237 }
238
239 let first_number = numbers[0];
241 if numbers.iter().all(|&x| x == first_number) {
242 return Err(ParseCpfError::InvalidNumber);
243 }
244
245 for i in 0..=1 {
246 let remainder = calc_remainder(numbers, i);
247 let check_digit = numbers[9 + i];
248
249 if remainder != check_digit {
250 return Err(ParseCpfError::InvalidNumber);
251 }
252 }
253
254 Ok(Cpf(numbers))
255 }
256}
257
258#[cfg(feature = "rand")]
259impl Distribution<Cpf> for Standard {
260 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Cpf {
261 let mut numbers = [0; 11];
262 for number in &mut numbers[..9] {
263 *number = rng.gen_range(0..=9);
264 }
265
266 for i in 0..=1 {
267 numbers[9 + i] = calc_remainder(numbers, i); }
269
270 Cpf(numbers)
271 }
272}
273
274#[cfg(feature = "serde")]
275impl Serialize for Cpf {
276 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
277 serializer.serialize_str(&self.to_string())
278 }
279}
280
281#[cfg(feature = "serde")]
282impl<'de> Deserialize<'de> for Cpf {
283 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
284 struct CpfStringVisitor;
285
286 impl<'vi> de::Visitor<'vi> for CpfStringVisitor {
287 type Value = Cpf;
288
289 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
290 write!(formatter, "a CPF string")
291 }
292
293 fn visit_str<E: de::Error>(self, value: &str) -> Result<Cpf, E> {
294 value.parse().map_err(E::custom)
295 }
296
297 fn visit_bytes<E: de::Error>(self, value: &[u8]) -> Result<Cpf, E> {
298 Cpf::try_from(value).map_err(E::custom)
299 }
300 }
301
302 deserializer.deserialize_str(CpfStringVisitor)
303 }
304}
305
306#[inline]
307fn calc_remainder(numbers: impl IntoIterator<Item = u8>, i: usize) -> u8 {
308 let remainder = numbers
309 .into_iter()
310 .take(9 + i)
312 .zip((2..=10 + i).rev())
314 .map(|(x, y)| u32::from(x) * y as u32)
315 .sum::<u32>()
316 * 10
317 % 11;
318
319 match remainder {
320 10 | 11 => 0,
321 _ => remainder as u8,
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 #[cfg(not(feature = "std"))]
328 use alloc::format;
329
330 use super::*;
331
332 #[test]
333 fn from_slice() {
334 let a = Cpf([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9]);
335 let b: [u8; 11] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9];
336 let c: [u8; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
337
338 assert_eq!(a, Cpf::from_slice(&b).unwrap());
339 assert_eq!(a, Cpf::from_slice(&c).unwrap());
340 }
341
342 #[test]
343 fn as_bytes() {
344 let a: [u8; 11] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9];
345 let b = Cpf([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9]);
346
347 assert_eq!(&a, b.as_bytes());
348 }
349
350 #[cfg(feature = "rand")]
351 #[test]
352 fn generate() {
353 let a = Cpf::generate();
354 let b = a.to_string().parse::<Cpf>().unwrap();
355
356 assert_eq!(a, b);
357 }
358
359 #[test]
360 fn as_ref() {
361 fn test_trait<T: AsRef<[u8]>>(b: T) {
362 let a: [u8; 11] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9];
363 assert_eq!(&a, b.as_ref());
364 }
365
366 let b = Cpf([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9]);
367
368 test_trait(b);
369 }
370
371 #[test]
372 fn from() {
373 let a: [u8; 11] = Cpf([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9]).into();
374 let b: [u8; 11] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9];
375
376 assert_eq!(a, b);
377 }
378
379 #[test]
380 fn try_from() {
381 let a: [u8; 11] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9];
382 let b = Cpf([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9]);
383
384 assert_eq!(Cpf::try_from(&a).unwrap(), b);
385 }
386
387 #[test]
388 fn cmp() {
389 let a = Cpf([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9]);
390 let b = Cpf([1, 2, 3, 4, 5, 6, 7, 9, 0, 3, 4]);
391
392 assert!(a < b);
393 }
394
395 #[test]
396 fn debug() {
397 let a = r#"Cpf("123.456.789-09")"#;
398 let b = Cpf([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9]);
399
400 assert_eq!(a, format!("{b:?}"));
401 }
402
403 #[test]
404 fn display() {
405 let a = "123.456.789-09";
406 let b = Cpf([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9]);
407
408 assert_eq!(a, format!("{b}"));
409 }
410
411 #[test]
412 fn from_str() {
413 let a = "123.456.789-09".parse::<Cpf>().unwrap();
414 let b = "123456789/09".parse::<Cpf>().unwrap();
415 let c = "12345678909".parse::<Cpf>().unwrap();
416
417 assert_eq!(a, b);
418 assert_eq!(a, c);
419 assert_eq!("".parse::<Cpf>(), Err(ParseCpfError::Empty));
420 assert_eq!(
421 "123-456-789-09".parse::<Cpf>(),
422 Err(ParseCpfError::InvalidCharacter('-', 3))
423 );
424 assert_eq!(
425 "123.456.789-10".parse::<Cpf>(),
426 Err(ParseCpfError::InvalidNumber)
427 );
428 assert_eq!(
429 "123.456.789-009".parse::<Cpf>(),
430 Err(ParseCpfError::InvalidNumber)
431 );
432 }
433
434 #[cfg(feature = "serde")]
435 #[test]
436 fn serialize() {
437 let cpf_str = "123.456.789-09";
438 let cpf = Cpf::from_str(cpf_str).unwrap();
439 serde_test::assert_tokens(&cpf, &[serde_test::Token::Str(cpf_str)]);
440 }
441}