human_friendly_ids/
lib.rs1#![doc = include_str!("../README.md")]
2#![deny(clippy::all, clippy::pedantic)]
3#![allow(clippy::uninlined_format_args)]
4
5pub mod alphabet;
6pub mod error;
7pub mod id;
8
9pub use crate::id::Id;
10
11#[allow(
12 clippy::all,
13 clippy::pedantic,
14 unused_must_use,
15 reason = "It's a test, bro."
16)]
17#[cfg(test)]
18mod tests {
19 use std::convert::TryFrom;
20
21 use rand::Rng;
22
23 use super::*;
24 use crate::alphabet::GEN_ALPHABET;
25
26 #[test]
27 fn assert_largest_id_is_fixed() {
28 let largest = Id::max_length();
29 assert_eq!(largest, 838_488_366_986_797_801); const TEST_SIZE: usize = 1024 * 1024; let id = Id::new(TEST_SIZE);
36 assert_eq!(id.as_str().len(), TEST_SIZE);
37
38 let id_str = id.to_string();
40 let id_decoded: Id = id_str.parse().expect("Failed to decode UploadId");
41
42 assert_eq!(id_decoded.to_string(), id_str);
43 }
44
45 #[test]
46 fn test_decode() {
47 let test_string = String::from("wcfytxww4opin4jmjjes4ccfd");
48 let decoded = Id::try_from(test_string).expect("Failed to decode UploadId");
49 assert_eq!(
50 decoded.as_str(),
51 "wcfytxww4opin4jmjjes4ccfd",
52 "decoded value should be equal to input string"
53 );
54 }
55
56 #[test]
57 fn fuzz_generated_ids() {
58 for _ in 0_u64..10_000_u64 {
59 let id = Id::new(25);
60 println!("{}", id);
61 assert_eq!(id.as_str().len(), 25);
62
63 let id_str = id.to_string();
65 let id = Id::try_from(id_str.clone()).expect("Failed to decode UploadId");
66 assert_eq!(id.to_string(), id_str);
67 }
68 }
69
70 #[test]
71 fn fuzz_gen_alphabet_strings() {
72 let mut rng = rand::rng();
73 for _ in 0..100_000_u64 {
74 let string = (0..rng.random_range(2..25))
76 .map(|_| GEN_ALPHABET[rng.random_range(0..GEN_ALPHABET.len())])
77 .collect::<String>();
78
79 Id::try_from(string.clone());
81 }
82 }
83
84 #[test]
85 fn fuzz_random_strings() {
86 let mut rng = rand::rng();
87 for _ in 0..100_000_u64 {
88 let string = (0..rng.random_range(2..25))
90 .map(|_| rng.random_range(0..=255) as u8 as char)
91 .collect::<String>();
92
93 Id::try_from(string.clone());
95 }
96 }
97
98 #[test]
99 fn test_invalid_chars_error() {
100 let id = "abc123".to_string();
101 let result = Id::try_from(id);
102 assert!(result.is_err());
103 let err = result.expect_err("Should fail due to invalid characters");
104 assert_eq!(err.to_string(), "Invalid check bit");
105 }
106
107 #[test]
108 fn test_invalid_check_bit_error() {
109 let invalid_id = String::from("abbsyhbbb4tyxnnmrtjx4crom");
110 let result = Id::try_from(invalid_id);
111 assert!(result.is_err());
112 let err = result.expect_err("Should fail due to invalid check-bit");
113 assert_eq!(err.to_string(), "Invalid check bit");
114 }
115
116 #[test]
117 fn test_too_short_error() {
118 let invalid_id = String::from("aa");
119 let result = Id::try_from(invalid_id);
120 assert!(result.is_err());
121 let err = result.expect_err("Should fail due to invalid check-bit");
122 assert_eq!(err.to_string(), "ID length too short, minimum 3 characters");
123 }
124
125 #[test]
126 fn test_weird_unicode() {
127 let invalid_id = String::from("🦀🦀🦀");
128 let result = Id::try_from(invalid_id);
129 assert!(result.is_err());
130 let err = result.expect_err("Should fail due to invalid characters");
131 assert_eq!(err.to_string(), "Invalid character in ID");
132 }
133
134 #[test]
135 fn test_invalid_chars() {
136 let invalid_id = String::from("¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿gg");
137 let result = Id::try_from(invalid_id);
138 assert!(result.is_err());
139 let err = result.expect_err("Should fail due to invalid characters");
140 assert_eq!(err.to_string(), "Invalid character in ID");
141 }
142}