dataforge/generators/
number.rs

1//! 数字生成器模块
2
3use once_cell::sync::Lazy;
4use rand::Rng;
5use rand::seq::SliceRandom;
6use std::fs;
7
8/// 从文件加载手机号码前缀
9static PHONE_PREFIXES_CN: Lazy<Vec<String>> =
10    Lazy::new(|| load_data_from_file("data/generators/number/phone_prefixes_cn.txt"));
11
12/// 从文件加载地区代码
13static AREA_CODES_CN: Lazy<Vec<String>> =
14    Lazy::new(|| load_data_from_file("data/generators/number/area_codes_cn.txt"));
15
16/// 从文件加载数据的辅助函数
17fn load_data_from_file(file_path: &str) -> Vec<String> {
18    match fs::read_to_string(file_path) {
19        Ok(content) => content
20            .lines()
21            .map(|line| line.trim().to_string())
22            .filter(|line| !line.is_empty())
23            .collect(),
24        Err(_) => {
25            // 如果文件不存在,返回默认数据
26            match file_path {
27                "data/generators/number/phone_prefixes_cn.txt" => vec![
28                    "130".to_string(),
29                    "131".to_string(),
30                    "132".to_string(),
31                    "133".to_string(),
32                    "134".to_string(),
33                    "135".to_string(),
34                ],
35                "data/generators/number/area_codes_cn.txt" => vec![
36                    "110000".to_string(),
37                    "120000".to_string(),
38                    "130000".to_string(),
39                    "140000".to_string(),
40                    "150000".to_string(),
41                    "210000".to_string(),
42                ],
43                _ => vec!["Default".to_string()],
44            }
45        }
46    }
47}
48
49/// 生成指定范围的整数
50pub fn integer(min: i64, max: i64) -> i64 {
51    let mut rng = rand::thread_rng();
52    rng.gen_range(min..=max)
53}
54
55/// 生成指定范围的浮点数
56pub fn float(min: f64, max: f64) -> f64 {
57    let mut rng = rand::thread_rng();
58    rng.gen_range(min..=max)
59}
60
61/// 生成指定精度的浮点数
62pub fn float_with_precision(min: f64, max: f64, precision: usize) -> f64 {
63    let value = float(min, max);
64    let multiplier = 10_f64.powi(precision as i32);
65    (value * multiplier).round() / multiplier
66}
67
68/// 生成正整数
69pub fn positive_integer(max: i64) -> i64 {
70    integer(1, max)
71}
72
73/// 生成负整数
74pub fn negative_integer(min: i64) -> i64 {
75    integer(min, -1)
76}
77
78/// 生成百分比(0-100)
79pub fn percentage() -> f64 {
80    float(0.0, 100.0)
81}
82
83/// 生成概率值(0.0-1.0)
84pub fn probability() -> f64 {
85    float(0.0, 1.0)
86}
87
88/// 生成货币金额
89pub fn currency(min: f64, max: f64) -> f64 {
90    float_with_precision(min, max, 2)
91}
92
93/// 生成年龄
94pub fn age() -> u8 {
95    let mut rng = rand::thread_rng();
96    rng.gen_range(1..=120)
97}
98
99/// 生成成人年龄(18-80)
100pub fn adult_age() -> u8 {
101    let mut rng = rand::thread_rng();
102    rng.gen_range(18..=80)
103}
104
105/// 生成儿童年龄(1-17)
106pub fn child_age() -> u8 {
107    let mut rng = rand::thread_rng();
108    rng.gen_range(1..=17)
109}
110
111/// 生成分数(0-100)
112pub fn score() -> u8 {
113    let mut rng = rand::thread_rng();
114    rng.gen_range(0..=100)
115}
116
117/// 生成及格分数(60-100)
118pub fn passing_score() -> u8 {
119    let mut rng = rand::thread_rng();
120    rng.gen_range(60..=100)
121}
122
123/// 生成不及格分数(0-59)
124pub fn failing_score() -> u8 {
125    let mut rng = rand::thread_rng();
126    rng.gen_range(0..=59)
127}
128
129/// 生成温度(摄氏度)
130pub fn temperature_celsius() -> f64 {
131    float_with_precision(-50.0, 50.0, 1)
132}
133
134/// 生成温度(华氏度)
135pub fn temperature_fahrenheit() -> f64 {
136    float_with_precision(-58.0, 122.0, 1)
137}
138
139/// 生成指定范围内的随机整数
140pub fn random_int(min: i32, max: i32) -> i32 {
141    let mut rng = rand::thread_rng();
142    rng.gen_range(min..=max)
143}
144
145/// 生成指定范围内的随机浮点数
146pub fn random_float(min: f64, max: f64) -> f64 {
147    let mut rng = rand::thread_rng();
148    rng.gen_range(min..=max)
149}
150
151/// 生成体重(公斤)
152pub fn weight_kg() -> f64 {
153    float_with_precision(40.0, 150.0, 1)
154}
155
156/// 生成BMI指数
157pub fn bmi() -> f64 {
158    float_with_precision(15.0, 40.0, 1)
159}
160
161/// 生成银行卡号(16位)
162pub fn credit_card_number() -> String {
163    let mut rng = rand::thread_rng();
164    (0..16)
165        .map(|_| rng.gen_range(0..=9).to_string())
166        .collect::<Vec<_>>()
167        .join("")
168}
169
170/// 生成手机号码(中国)
171pub fn phone_number_cn() -> String {
172    let mut rng = rand::thread_rng();
173    use rand::seq::SliceRandom;
174
175    let prefix = PHONE_PREFIXES_CN.choose(&mut rng).unwrap();
176    let suffix: String = (0..8).map(|_| rng.gen_range(0..=9).to_string()).collect();
177
178    format!("{}{}", prefix, suffix)
179}
180
181/// 生成青少年年龄(13-19岁)
182pub fn teenager_age() -> u8 {
183    let mut rng = rand::thread_rng();
184    rng.gen_range(13..=19)
185}
186
187/// 生成老年人年龄(60-100岁)
188pub fn senior_age() -> u8 {
189    let mut rng = rand::thread_rng();
190    rng.gen_range(60..=100)
191}
192
193/// 生成价格(0.01-9999.99)
194pub fn price() -> f64 {
195    let mut rng = rand::thread_rng();
196    rng.gen_range(0.01..=9999.99)
197}
198
199/// 生成工资(20000-1000000)
200pub fn salary() -> u32 {
201    let mut rng = rand::thread_rng();
202    rng.gen_range(20000..=1000000)
203}
204
205/// 生成电话号码(通用)
206pub fn phone_number() -> String {
207    phone_number_cn()
208}
209
210/// 生成信用卡号(别名)
211pub fn credit_card() -> String {
212    credit_card_number()
213}
214
215/// 生成银行卡号(中国)
216pub fn bank_card_cn() -> String {
217    let mut rng = rand::thread_rng();
218    let prefixes = ["6225", "6227", "6228", "6229", "6230"]; // 常见银行卡前缀
219    let prefix = prefixes.choose(&mut rng).unwrap();
220    let remaining_digits: String = (0..12)
221        .map(|_| rng.gen_range(0..=9).to_string())
222        .collect();
223    format!("{}{}", prefix, remaining_digits)
224}
225
226/// 生成身份证号码(中国,18位)
227pub fn id_card_cn() -> String {
228    let mut rng = rand::thread_rng();
229    use rand::seq::SliceRandom;
230
231    // 地区代码(前6位)
232    let area_code = AREA_CODES_CN.choose(&mut rng).unwrap();
233
234    // 出生日期(8位)
235    let year = rng.gen_range(1950..=2005);
236    let month = rng.gen_range(1..=12);
237    let day = rng.gen_range(1..=28);
238    let birth_date = format!("{:04}{:02}{:02}", year, month, day);
239
240    // 顺序码(3位)
241    let sequence = format!("{:03}", rng.gen_range(1..=999));
242
243    // 前17位
244    let first_17 = format!("{}{}{}", area_code, birth_date, sequence);
245
246    // 校验码(第18位)
247    let check_digit = calculate_id_check_digit(&first_17);
248
249    format!("{}{}", first_17, check_digit)
250}
251
252/// 计算身份证校验码
253fn calculate_id_check_digit(first_17: &str) -> char {
254    let weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
255    let check_chars = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
256
257    let sum: usize = first_17
258        .chars()
259        .enumerate()
260        .map(|(i, c)| c.to_digit(10).unwrap() as usize * weights[i])
261        .sum();
262
263    check_chars[sum % 11]
264}
265
266#[cfg(test)]
267mod tests {
268    use super::*;
269
270    #[test]
271    fn test_integer() {
272        let num = integer(1, 10);
273        assert!(num >= 1 && num <= 10);
274    }
275
276    #[test]
277    fn test_float_with_precision() {
278        let num = float_with_precision(1.0, 2.0, 2);
279        let decimal_places = format!("{:.10}", num)
280            .split('.')
281            .nth(1)
282            .unwrap()
283            .trim_end_matches('0')
284            .len();
285        assert!(decimal_places <= 2);
286    }
287
288    #[test]
289    fn test_adult_age() {
290        let age = adult_age();
291        assert!(age >= 18 && age <= 80);
292    }
293
294    #[test]
295    fn test_phone_number_cn() {
296        let phone = phone_number_cn();
297        assert_eq!(phone.len(), 11);
298        assert!(phone.starts_with('1'));
299    }
300
301    #[test]
302    fn test_id_card_cn() {
303        let id = id_card_cn();
304        assert_eq!(id.len(), 18);
305        assert!(id.chars().take(17).all(|c| c.is_ascii_digit()));
306    }
307
308    #[test]
309    fn test_credit_card_number() {
310        let card = credit_card_number();
311        assert_eq!(card.len(), 16);
312        assert!(card.chars().all(|c| c.is_ascii_digit()));
313    }
314}