Skip to main content

robinpath_modules/modules/
faker_mod.rs

1use robinpath::{RobinPath, Value};
2use std::sync::atomic::{AtomicU64, Ordering};
3use std::sync::Arc;
4
5fn epoch_time_nanos() -> u64 {
6    std::time::SystemTime::now()
7        .duration_since(std::time::UNIX_EPOCH)
8        .unwrap_or_default()
9        .as_nanos() as u64
10}
11
12fn next_random(seed: &AtomicU64) -> u64 {
13    let mut s = seed.load(Ordering::Relaxed);
14    s ^= s << 13;
15    s ^= s >> 7;
16    s ^= s << 17;
17    // Mix in some entropy from time
18    s = s.wrapping_add(epoch_time_nanos());
19    seed.store(s, Ordering::Relaxed);
20    s
21}
22
23fn random_range(seed: &AtomicU64, min: u64, max: u64) -> u64 {
24    if max <= min {
25        return min;
26    }
27    min + next_random(seed) % (max - min)
28}
29
30fn pick_from(seed: &AtomicU64, items: &[&str]) -> String {
31    items[random_range(seed, 0, items.len() as u64) as usize].to_string()
32}
33
34const FIRST_NAMES: &[&str] = &[
35    "James", "Mary", "Robert", "Patricia", "John", "Jennifer", "Michael", "Linda",
36    "David", "Elizabeth", "William", "Barbara", "Richard", "Susan", "Joseph", "Jessica",
37    "Thomas", "Sarah", "Christopher", "Karen", "Daniel", "Lisa", "Matthew", "Nancy",
38    "Anthony", "Betty", "Mark", "Margaret", "Donald", "Sandra", "Steven", "Ashley",
39    "Paul", "Kimberly", "Andrew", "Emily", "Joshua", "Donna", "Kenneth", "Michelle",
40    "Kevin", "Carol", "Brian", "Amanda", "George", "Dorothy", "Timothy", "Melissa",
41];
42
43const LAST_NAMES: &[&str] = &[
44    "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
45    "Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez", "Wilson", "Anderson",
46    "Thomas", "Taylor", "Moore", "Jackson", "Martin", "Lee", "Perez", "Thompson",
47    "White", "Harris", "Sanchez", "Clark", "Ramirez", "Lewis", "Robinson", "Walker",
48    "Young", "Allen", "King", "Wright", "Scott", "Torres", "Nguyen", "Hill", "Flores",
49];
50
51const CITIES: &[&str] = &[
52    "New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia",
53    "San Antonio", "San Diego", "Dallas", "San Jose", "Austin", "Jacksonville",
54    "Fort Worth", "Columbus", "Charlotte", "San Francisco", "Indianapolis", "Seattle",
55    "Denver", "Washington", "Nashville", "Oklahoma City", "Portland", "Las Vegas",
56];
57
58const COUNTRIES: &[&str] = &[
59    "United States", "Canada", "United Kingdom", "Australia", "Germany", "France",
60    "Japan", "Brazil", "India", "Mexico", "South Korea", "Italy", "Spain", "Netherlands",
61    "Sweden", "Norway", "Denmark", "Finland", "Switzerland", "Argentina", "Poland",
62];
63
64const STREETS: &[&str] = &[
65    "Main St", "Oak Ave", "Elm St", "Park Blvd", "Cedar Ln", "Maple Dr",
66    "Pine St", "Washington Ave", "Lake Rd", "Hill St", "River Rd", "Forest Ave",
67];
68
69const COMPANIES: &[&str] = &[
70    "Acme Corp", "Globex", "Initech", "Umbrella Corp", "Stark Industries",
71    "Wayne Enterprises", "Cyberdyne", "Tyrell Corp", "Soylent Corp", "Weyland-Yutani",
72    "Oscorp", "LexCorp", "Massive Dynamic", "InGen", "Aperture Science",
73];
74
75const LOREM_WORDS: &[&str] = &[
76    "lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit",
77    "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore",
78    "magna", "aliqua", "enim", "ad", "minim", "veniam", "quis", "nostrud",
79    "exercitation", "ullamco", "laboris", "nisi", "aliquip", "ex", "ea", "commodo",
80    "consequat", "duis", "aute", "irure", "in", "reprehenderit", "voluptate",
81    "velit", "esse", "cillum", "fugiat", "nulla", "pariatur", "excepteur",
82    "sint", "occaecat", "cupidatat", "non", "proident", "sunt", "culpa",
83];
84
85const WORDS: &[&str] = &[
86    "ability", "accept", "across", "action", "activity", "agree", "almost", "among",
87    "animal", "answer", "appear", "area", "arrive", "article", "attack", "author",
88    "basket", "battle", "beauty", "become", "before", "begin", "behind", "believe",
89    "camera", "career", "center", "change", "choice", "circle", "class", "clear",
90    "danger", "debate", "decide", "demand", "design", "detail", "dinner", "direct",
91    "effect", "effort", "energy", "engine", "enough", "entire", "escape", "expect",
92];
93
94pub fn register(rp: &mut RobinPath) {
95    let seed = Arc::new(AtomicU64::new(epoch_time_nanos()));
96
97    let sd = seed.clone();
98    rp.register_builtin("faker.seed", move |args, _| {
99        let s = args.first().map(|v| v.to_number() as u64).unwrap_or(12345);
100        sd.store(s, Ordering::Relaxed);
101        Ok(Value::Bool(true))
102    });
103
104    let sd = seed.clone();
105    rp.register_builtin("faker.name", move |_args, _| {
106        Ok(Value::String(format!("{} {}", pick_from(&sd, FIRST_NAMES), pick_from(&sd, LAST_NAMES))))
107    });
108
109    let sd = seed.clone();
110    rp.register_builtin("faker.firstName", move |_args, _| {
111        Ok(Value::String(pick_from(&sd, FIRST_NAMES)))
112    });
113
114    let sd = seed.clone();
115    rp.register_builtin("faker.lastName", move |_args, _| {
116        Ok(Value::String(pick_from(&sd, LAST_NAMES)))
117    });
118
119    let sd = seed.clone();
120    rp.register_builtin("faker.email", move |_args, _| {
121        let first = pick_from(&sd, FIRST_NAMES).to_lowercase();
122        let last = pick_from(&sd, LAST_NAMES).to_lowercase();
123        let domains = ["gmail.com", "yahoo.com", "outlook.com", "example.com", "mail.com"];
124        let domain = pick_from(&sd, &domains);
125        Ok(Value::String(format!("{}.{}@{}", first, last, domain)))
126    });
127
128    let sd = seed.clone();
129    rp.register_builtin("faker.phone", move |_args, _| {
130        let area = random_range(&sd, 200, 999);
131        let mid = random_range(&sd, 100, 999);
132        let end = random_range(&sd, 1000, 9999);
133        Ok(Value::String(format!("({}) {}-{}", area, mid, end)))
134    });
135
136    let sd = seed.clone();
137    rp.register_builtin("faker.address", move |_args, _| {
138        let num = random_range(&sd, 1, 9999);
139        let street = pick_from(&sd, STREETS);
140        Ok(Value::String(format!("{} {}", num, street)))
141    });
142
143    let sd = seed.clone();
144    rp.register_builtin("faker.city", move |_args, _| {
145        Ok(Value::String(pick_from(&sd, CITIES)))
146    });
147
148    let sd = seed.clone();
149    rp.register_builtin("faker.country", move |_args, _| {
150        Ok(Value::String(pick_from(&sd, COUNTRIES)))
151    });
152
153    let sd = seed.clone();
154    rp.register_builtin("faker.zipCode", move |_args, _| {
155        let zip = random_range(&sd, 10000, 99999);
156        Ok(Value::String(format!("{}", zip)))
157    });
158
159    let sd = seed.clone();
160    rp.register_builtin("faker.company", move |_args, _| {
161        Ok(Value::String(pick_from(&sd, COMPANIES)))
162    });
163
164    let sd = seed.clone();
165    rp.register_builtin("faker.lorem", move |args, _| {
166        let count = args.first().map(|v| v.to_number() as usize).unwrap_or(10);
167        let words: Vec<String> = (0..count).map(|_| pick_from(&sd, LOREM_WORDS)).collect();
168        Ok(Value::String(words.join(" ")))
169    });
170
171    let sd = seed.clone();
172    rp.register_builtin("faker.number", move |args, _| {
173        let min = args.first().map(|v| v.to_number() as i64).unwrap_or(0);
174        let max = args.get(1).map(|v| v.to_number() as i64).unwrap_or(100);
175        let val = if max > min {
176            min + (next_random(&sd) as i64).abs() % (max - min)
177        } else {
178            min
179        };
180        Ok(Value::Number(val as f64))
181    });
182
183    let sd = seed.clone();
184    rp.register_builtin("faker.float", move |args, _| {
185        let min = args.first().map(|v| v.to_number()).unwrap_or(0.0);
186        let max = args.get(1).map(|v| v.to_number()).unwrap_or(1.0);
187        let frac = (next_random(&sd) % 10000) as f64 / 10000.0;
188        Ok(Value::Number(min + frac * (max - min)))
189    });
190
191    let sd = seed.clone();
192    rp.register_builtin("faker.boolean", move |_args, _| {
193        Ok(Value::Bool(next_random(&sd) % 2 == 0))
194    });
195
196    let sd = seed.clone();
197    rp.register_builtin("faker.date", move |_args, _| {
198        let year = random_range(&sd, 2000, 2030);
199        let month = random_range(&sd, 1, 13);
200        let day = random_range(&sd, 1, 29);
201        Ok(Value::String(format!("{:04}-{:02}-{:02}", year, month, day)))
202    });
203
204    let sd = seed.clone();
205    rp.register_builtin("faker.uuid", move |_args, _| {
206        let hex_chars = "0123456789abcdef";
207        let hex: Vec<u8> = hex_chars.as_bytes().to_vec();
208        let mut uuid = String::with_capacity(36);
209        let pattern = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
210        for c in pattern.chars() {
211            match c {
212                'x' => uuid.push(hex[random_range(&sd, 0, 16) as usize] as char),
213                'y' => uuid.push(hex[(random_range(&sd, 0, 4) + 8) as usize] as char),
214                '-' | '4' => uuid.push(c),
215                _ => uuid.push(c),
216            }
217        }
218        Ok(Value::String(uuid))
219    });
220
221    let sd = seed.clone();
222    rp.register_builtin("faker.pick", move |args, _| {
223        let arr = args.first().cloned().unwrap_or(Value::Null);
224        if let Value::Array(items) = &arr {
225            if items.is_empty() {
226                return Ok(Value::Null);
227            }
228            let idx = random_range(&sd, 0, items.len() as u64) as usize;
229            Ok(items[idx].clone())
230        } else {
231            Ok(Value::Null)
232        }
233    });
234
235    let sd = seed.clone();
236    rp.register_builtin("faker.shuffle", move |args, _| {
237        let arr = args.first().cloned().unwrap_or(Value::Null);
238        if let Value::Array(mut items) = arr {
239            let n = items.len();
240            for i in (1..n).rev() {
241                let j = random_range(&sd, 0, (i + 1) as u64) as usize;
242                items.swap(i, j);
243            }
244            Ok(Value::Array(items))
245        } else {
246            Ok(Value::Array(vec![]))
247        }
248    });
249
250    let sd = seed.clone();
251    rp.register_builtin("faker.paragraph", move |args, _| {
252        let sentences = args.first().map(|v| v.to_number() as usize).unwrap_or(3);
253        let mut parts = Vec::new();
254        for _ in 0..sentences {
255            let word_count = random_range(&sd, 5, 15) as usize;
256            let words: Vec<String> = (0..word_count).map(|_| pick_from(&sd, WORDS)).collect();
257            let mut sentence = words.join(" ");
258            if let Some(first) = sentence.get_mut(0..1) {
259                first.make_ascii_uppercase();
260            }
261            sentence.push('.');
262            parts.push(sentence);
263        }
264        Ok(Value::String(parts.join(" ")))
265    });
266
267    let sd = seed.clone();
268    rp.register_builtin("faker.sentence", move |_args, _| {
269        let word_count = random_range(&sd, 5, 12) as usize;
270        let words: Vec<String> = (0..word_count).map(|_| pick_from(&sd, WORDS)).collect();
271        let mut sentence = words.join(" ");
272        if let Some(first) = sentence.get_mut(0..1) {
273            first.make_ascii_uppercase();
274        }
275        sentence.push('.');
276        Ok(Value::String(sentence))
277    });
278
279    let sd = seed.clone();
280    rp.register_builtin("faker.word", move |_args, _| {
281        Ok(Value::String(pick_from(&sd, WORDS)))
282    });
283
284    let sd = seed.clone();
285    rp.register_builtin("faker.color", move |_args, _| {
286        let r = random_range(&sd, 0, 256);
287        let g = random_range(&sd, 0, 256);
288        let b = random_range(&sd, 0, 256);
289        Ok(Value::String(format!("#{:02x}{:02x}{:02x}", r, g, b)))
290    });
291
292    let sd = seed.clone();
293    rp.register_builtin("faker.ip", move |_args, _| {
294        Ok(Value::String(format!(
295            "{}.{}.{}.{}",
296            random_range(&sd, 1, 255),
297            random_range(&sd, 0, 255),
298            random_range(&sd, 0, 255),
299            random_range(&sd, 1, 255)
300        )))
301    });
302
303    let sd = seed.clone();
304    rp.register_builtin("faker.url", move |_args, _| {
305        let domains = ["example.com", "test.org", "sample.net", "demo.io", "fake.dev"];
306        let paths = ["page", "post", "article", "item", "resource", "data", "api"];
307        let domain = pick_from(&sd, &domains);
308        let path = pick_from(&sd, &paths);
309        let id = random_range(&sd, 1, 1000);
310        Ok(Value::String(format!("https://{}/{}/{}", domain, path, id)))
311    });
312
313    let sd = seed.clone();
314    rp.register_builtin("faker.avatar", move |_args, _| {
315        let id = random_range(&sd, 1, 100);
316        Ok(Value::String(format!(
317            "https://i.pravatar.cc/150?img={}",
318            id
319        )))
320    });
321}