1mod dict;
55
56use dict::*;
57use rand::Rng;
58use serde_json::{json, Map, Value};
59use vld::prelude::VldParse;
60
61pub fn fake_value(schema: &Value) -> Value {
65 FakeGen::new().value(schema)
66}
67
68pub fn fake_json(schema: &Value) -> String {
70 serde_json::to_string_pretty(&fake_value(schema)).expect("serialisation cannot fail")
71}
72
73pub fn fake_many(schema: &Value, count: usize) -> Vec<Value> {
75 let mut gen = FakeGen::new();
76 (0..count).map(|_| gen.value(schema)).collect()
77}
78
79pub fn fake_parsed<T: VldParse>(schema: &Value) -> T {
85 let val = fake_value(schema);
86 T::vld_parse_value(&val).unwrap_or_else(|e| {
87 panic!(
88 "vld_fake::fake_parsed: generated value failed validation.\nValue: {}\nError: {:?}",
89 serde_json::to_string_pretty(&val).unwrap_or_default(),
90 e,
91 )
92 })
93}
94
95pub fn try_fake_parsed<T: VldParse>(schema: &Value) -> Result<T, vld::error::VldError> {
97 let val = fake_value(schema);
98 T::vld_parse_value(&val)
99}
100
101pub fn fake_value_seeded(schema: &Value, seed: u64) -> Value {
103 use rand::SeedableRng;
104 let rng = rand::rngs::StdRng::seed_from_u64(seed);
105 FakeGen::with_rng(rng).value(schema)
106}
107
108pub struct FakeGen<R: Rng> {
112 rng: R,
113 depth: usize,
115 counter: u64,
117}
118
119const MAX_DEPTH: usize = 12;
120
121impl FakeGen<rand::rngs::ThreadRng> {
122 pub fn new() -> Self {
124 Self {
125 rng: rand::thread_rng(),
126 depth: 0,
127 counter: 0,
128 }
129 }
130}
131
132impl Default for FakeGen<rand::rngs::ThreadRng> {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138impl<R: Rng> FakeGen<R> {
139 pub fn with_rng(rng: R) -> Self {
141 Self {
142 rng,
143 depth: 0,
144 counter: 0,
145 }
146 }
147
148 fn next_id(&mut self) -> u64 {
149 self.counter += 1;
150 self.counter
151 }
152
153 fn pick<'a, T>(&mut self, items: &'a [T]) -> &'a T {
154 &items[self.rng.gen_range(0..items.len())]
155 }
156
157 pub fn value(&mut self, schema: &Value) -> Value {
161 self.value_with_hint(schema, None)
162 }
163
164 pub fn value_with_hint(&mut self, schema: &Value, hint: Option<&str>) -> Value {
167 if self.depth > MAX_DEPTH {
168 return Value::Null;
169 }
170 self.depth += 1;
171 let result = self.dispatch(schema, hint);
172 self.depth -= 1;
173 result
174 }
175
176 fn dispatch(&mut self, schema: &Value, hint: Option<&str>) -> Value {
177 let obj = match schema.as_object() {
178 Some(o) => o,
179 None => return Value::Null,
180 };
181
182 if let Some(c) = obj.get("const") {
184 return c.clone();
185 }
186 if let Some(en) = obj.get("enum").and_then(Value::as_array) {
187 if en.is_empty() {
188 return Value::Null;
189 }
190 let idx = self.rng.gen_range(0..en.len());
191 return en[idx].clone();
192 }
193
194 if let Some(variants) = obj
196 .get("oneOf")
197 .or_else(|| obj.get("anyOf"))
198 .and_then(Value::as_array)
199 {
200 let concrete: Vec<&Value> = variants
201 .iter()
202 .filter(|v| v.get("type").and_then(Value::as_str) != Some("null"))
203 .collect();
204 if !concrete.is_empty() {
205 let idx = self.rng.gen_range(0..concrete.len());
206 return self.value_with_hint(concrete[idx], hint);
207 }
208 if !variants.is_empty() {
209 let idx = self.rng.gen_range(0..variants.len());
210 return self.value_with_hint(&variants[idx], hint);
211 }
212 return Value::Null;
213 }
214
215 if let Some(all) = obj.get("allOf").and_then(Value::as_array) {
217 return self.merge_all_of(all);
218 }
219
220 let ty = obj.get("type").and_then(Value::as_str).unwrap_or("string");
222
223 match ty {
224 "string" => self.gen_string(obj, hint),
225 "integer" => self.gen_integer(obj, hint),
226 "number" => self.gen_number(obj, hint),
227 "boolean" => self.gen_boolean(),
228 "array" => self.gen_array(obj, hint),
229 "object" => self.gen_object(obj, hint),
230 "null" => Value::Null,
231 _ => Value::Null,
232 }
233 }
234
235 fn gen_string(&mut self, obj: &Map<String, Value>, hint: Option<&str>) -> Value {
240 let format = obj.get("format").and_then(Value::as_str);
241
242 if let Some(fmt) = format {
244 return Value::String(self.gen_string_format(fmt, obj));
245 }
246
247 if let Some(h) = hint {
249 if let Some(v) = self.gen_string_by_hint(h, obj) {
250 return v;
251 }
252 }
253
254 self.gen_readable_string(obj)
256 }
257
258 fn gen_string_by_hint(&mut self, hint: &str, obj: &Map<String, Value>) -> Option<Value> {
260 let h = hint.to_ascii_lowercase();
261 let h = h.as_str();
262
263 let result: Option<String> = match h {
265 "first_name" | "firstname" | "given_name" | "givenname" => {
267 Some(self.pick(FIRST_NAMES).to_string())
268 }
269 "last_name" | "lastname" | "surname" | "family_name" | "familyname" => {
270 Some(self.pick(LAST_NAMES).to_string())
271 }
272 "name" | "full_name" | "fullname" | "display_name" | "displayname" | "user_name"
273 | "author" | "author_name" => {
274 let first = self.pick(FIRST_NAMES);
275 let last = self.pick(LAST_NAMES);
276 Some(format!("{first} {last}"))
277 }
278 "username" | "login" | "handle" | "nick" | "nickname" => {
279 let first = self.pick(FIRST_NAMES).to_lowercase();
280 let n: u16 = self.rng.gen_range(1..999);
281 Some(format!("{first}{n}"))
282 }
283
284 "email" | "email_address" | "emailaddress" | "mail" => Some(self.gen_email()),
286 "phone" | "phone_number" | "phonenumber" | "tel" | "telephone" | "mobile" | "cell" => {
287 Some(self.gen_phone())
288 }
289
290 "city" | "town" => Some(self.pick(CITIES).to_string()),
292 "country" => Some(self.pick(COUNTRIES).to_string()),
293 "state" | "province" | "region" => Some(self.pick(STATES).to_string()),
294 "street" | "street_address" | "address_line" | "address_line1" | "address"
295 | "address1" | "line1" => Some(self.gen_street_address()),
296 "zip" | "zipcode" | "zip_code" | "postal" | "postal_code" | "postalcode" => {
297 Some(self.gen_zip())
298 }
299 "latitude" | "lat" => {
300 let v: f64 = self.rng.gen_range(-90.0..=90.0);
301 Some(format!("{:.6}", v))
302 }
303 "longitude" | "lng" | "lon" => {
304 let v: f64 = self.rng.gen_range(-180.0..=180.0);
305 Some(format!("{:.6}", v))
306 }
307
308 "company" | "company_name" | "companyname" | "organization" | "organisation"
310 | "org" | "employer" => Some(self.pick(COMPANIES).to_string()),
311 "department" | "team" | "division" => Some(self.pick(DEPARTMENTS).to_string()),
312 "job_title" | "jobtitle" | "position" | "role" | "title" | "occupation" => {
313 Some(self.pick(JOB_TITLES).to_string())
314 }
315
316 "url" | "website" | "homepage" | "link" | "site" | "uri" | "href" => {
318 Some(self.gen_url())
319 }
320 "domain" | "domain_name" | "domainname" | "host" | "hostname" => {
321 Some(self.gen_hostname())
322 }
323 "ip" | "ip_address" | "ipaddress" | "ipv4" => Some(self.gen_ipv4()),
324 "ipv6" => Some(self.gen_ipv6()),
325 "user_agent" | "useragent" | "ua" => Some(self.gen_user_agent()),
326 "mac" | "mac_address" | "macaddress" => Some(self.gen_mac_address()),
327
328 "id" | "uid" | "uuid" | "guid" => Some(self.gen_uuid()),
330 "slug" => Some(self.gen_slug()),
331 "token" | "api_key" | "apikey" | "secret" | "access_token" | "refresh_token" => {
332 Some(self.gen_token(32))
333 }
334 "password" | "pass" | "pwd" | "secret_key" => Some(self.gen_password()),
335
336 "description" | "desc" | "bio" | "about" | "summary" | "overview" => {
338 Some(self.gen_sentence_range(8, 20))
339 }
340 "comment" | "note" | "notes" | "message" | "body" | "content" | "text" => {
341 Some(self.gen_sentence_range(5, 15))
342 }
343
344 "product" | "product_name" | "productname" | "item" | "item_name" => {
346 let adj = self.pick(ADJECTIVES);
347 let noun = self.pick(PRODUCT_NOUNS);
348 Some(format!("{adj} {noun}"))
349 }
350 "brand" => Some(self.pick(COMPANIES).to_string()),
351 "sku" | "product_code" | "productcode" | "code" | "barcode" => {
352 let prefix: String = (0..3)
353 .map(|_| self.rng.gen_range(b'A'..=b'Z') as char)
354 .collect();
355 let num: u32 = self.rng.gen_range(10000..99999);
356 Some(format!("{prefix}-{num}"))
357 }
358 "category" | "genre" | "type" | "kind" | "group" => {
359 Some(self.pick(CATEGORIES).to_string())
360 }
361 "tag" | "label" => Some(self.pick(TAGS).to_string()),
362
363 "color" | "colour" => Some(self.pick(COLORS).to_string()),
365 "hex_color" | "hexcolor" | "color_hex" => Some(format!(
366 "#{:02x}{:02x}{:02x}",
367 self.rng.gen_range(0u8..=255),
368 self.rng.gen_range(0u8..=255),
369 self.rng.gen_range(0u8..=255),
370 )),
371
372 "currency" | "currency_code" => Some(self.pick(CURRENCIES).to_string()),
374 "locale" | "lang" | "language" => Some(self.pick(LOCALES).to_string()),
375 "timezone" | "tz" | "time_zone" => Some(self.pick(TIMEZONES).to_string()),
376 "mime" | "mime_type" | "mimetype" | "content_type" | "contenttype" => {
377 Some(self.pick(MIME_TYPES).to_string())
378 }
379 "file_name" | "filename" => {
380 let word = self.pick(WORDS).to_lowercase();
381 let ext = self.pick(FILE_EXTENSIONS);
382 Some(format!("{word}.{ext}"))
383 }
384 "extension" | "ext" | "file_ext" | "file_extension" => {
385 Some(self.pick(FILE_EXTENSIONS).to_string())
386 }
387 "version" | "semver" => {
388 let major = self.rng.gen_range(0u8..10);
389 let minor = self.rng.gen_range(0u8..30);
390 let patch = self.rng.gen_range(0u16..100);
391 Some(format!("{major}.{minor}.{patch}"))
392 }
393 "credit_card" | "creditcard" | "card_number" | "cardnumber" | "cc" => {
394 Some(self.gen_credit_card())
395 }
396 "isbn" => Some(self.gen_isbn()),
397 "ssn" => {
398 let a: u16 = self.rng.gen_range(100..999);
399 let b: u8 = self.rng.gen_range(10..99);
400 let c: u16 = self.rng.gen_range(1000..9999);
401 Some(format!("{a}-{b}-{c}"))
402 }
403
404 _ => None,
405 };
406
407 if let Some(mut s) = result {
409 let min = obj.get("minLength").and_then(Value::as_u64).unwrap_or(0) as usize;
410 let max = obj
411 .get("maxLength")
412 .and_then(Value::as_u64)
413 .map(|v| v as usize);
414
415 while s.len() < min {
417 s.push('x');
418 }
419 if let Some(mx) = max {
421 if s.len() > mx {
422 s.truncate(mx);
423 }
424 }
425 Some(Value::String(s))
426 } else {
427 None
428 }
429 }
430
431 fn gen_readable_string(&mut self, obj: &Map<String, Value>) -> Value {
433 let min_len = obj.get("minLength").and_then(Value::as_u64).unwrap_or(1) as usize;
434 let max_len = obj
435 .get("maxLength")
436 .and_then(Value::as_u64)
437 .unwrap_or(min_len.max(1) as u64 + 30) as usize;
438 let max_len = max_len.max(min_len);
439
440 let target = if min_len == max_len {
442 min_len
443 } else {
444 self.rng.gen_range(min_len..=max_len)
445 };
446
447 let mut s = String::new();
448 let capitalize_first = true;
449
450 loop {
451 if s.len() >= target {
452 break;
453 }
454 let word = self.pick(WORDS);
455 if !s.is_empty() {
456 if s.len() + 1 + word.len() > max_len {
458 let remaining = max_len - s.len();
460 if remaining > 1 {
461 s.push(' ');
462 let filler: String = WORDS
463 .iter()
464 .filter(|w| w.len() < remaining)
465 .take(1)
466 .map(|w| w.to_string())
467 .next()
468 .unwrap_or_else(|| {
469 (0..remaining - 1)
470 .map(|_| {
471 LOWER_ALPHA[self.rng.gen_range(0..LOWER_ALPHA.len())]
472 as char
473 })
474 .collect()
475 });
476 s.push_str(&filler);
477 }
478 break;
479 }
480 s.push(' ');
481 }
482 s.push_str(word);
483 }
484
485 if capitalize_first && !s.is_empty() {
487 let mut chars = s.chars();
488 s = chars.next().unwrap().to_uppercase().chain(chars).collect();
489 }
490
491 while s.len() < min_len {
493 s.push('a');
494 }
495
496 if s.len() > max_len {
498 s.truncate(max_len);
499 }
500
501 Value::String(s)
502 }
503
504 fn gen_string_format(&mut self, fmt: &str, obj: &Map<String, Value>) -> String {
507 match fmt {
508 "email" => self.gen_email(),
509 "uuid" => self.gen_uuid(),
510 "uri" | "url" => self.gen_url(),
511 "ipv4" => self.gen_ipv4(),
512 "ipv6" => self.gen_ipv6(),
513 "hostname" => self.gen_hostname(),
514 "date" | "iso-date" => self.gen_date(),
515 "time" | "iso-time" => self.gen_time(),
516 "date-time" | "iso-datetime" => self.gen_datetime(),
517 "base64" => self.gen_base64(),
518 "cuid2" => self.gen_cuid2(),
519 "ulid" => self.gen_ulid(),
520 "nanoid" => self.gen_nanoid(),
521 "emoji" => self.gen_emoji(),
522 "phone" => self.gen_phone(),
523 "credit-card" => self.gen_credit_card(),
524 "mac-address" | "mac" => self.gen_mac_address(),
525 "color" | "hex-color" => self.gen_hex_color(),
526 "semver" => {
527 let major = self.rng.gen_range(0u8..10);
528 let minor = self.rng.gen_range(0u8..30);
529 let patch = self.rng.gen_range(0u16..100);
530 format!("{major}.{minor}.{patch}")
531 }
532 "slug" => self.gen_slug(),
533 _ => {
534 let min = obj.get("minLength").and_then(Value::as_u64).unwrap_or(1) as usize;
536 let max = obj
537 .get("maxLength")
538 .and_then(Value::as_u64)
539 .unwrap_or(min as u64 + 20) as usize;
540 let target = self.rng.gen_range(min..=max.max(min));
541 let mut s = String::new();
542 while s.len() < target {
543 if !s.is_empty() {
544 s.push(' ');
545 }
546 let idx = self.rng.gen_range(0..WORDS.len());
547 s.push_str(WORDS[idx]);
548 }
549 s.truncate(max.max(min));
550 while s.len() < min {
551 s.push('a');
552 }
553 s
554 }
555 }
556 }
557
558 fn gen_email(&mut self) -> String {
559 let first = self.pick(FIRST_NAMES).to_lowercase();
560 let last = self.pick(LAST_NAMES).to_lowercase();
561 let n: u16 = self.rng.gen_range(1..99);
562 let domain = self.pick(EMAIL_DOMAINS);
563 match self.rng.gen_range(0..4) {
565 0 => format!("{first}.{last}@{domain}"),
566 1 => format!("{first}{last}{n}@{domain}"),
567 2 => format!("{first}_{last}@{domain}"),
568 _ => format!("{}_{n}@{domain}", &first[..1.min(first.len())]),
569 }
570 }
571
572 fn gen_uuid(&mut self) -> String {
573 let hex = |n: usize, rng: &mut R| -> String {
574 (0..n)
575 .map(|_| HEX[rng.gen_range(0..HEX.len())] as char)
576 .collect()
577 };
578 format!(
579 "{}-{}-4{}-{}{}-{}",
580 hex(8, &mut self.rng),
581 hex(4, &mut self.rng),
582 hex(3, &mut self.rng),
583 HEX_89AB[self.rng.gen_range(0..4)] as char,
584 hex(3, &mut self.rng),
585 hex(12, &mut self.rng),
586 )
587 }
588
589 fn gen_url(&mut self) -> String {
590 let tld = self.pick(TLDS);
591 let word = self.pick(WORDS).to_lowercase();
592 let path_word = self.pick(WORDS).to_lowercase();
593 let scheme = if self.rng.gen_bool(0.9) {
594 "https"
595 } else {
596 "http"
597 };
598 match self.rng.gen_range(0..4) {
599 0 => format!("{scheme}://www.{word}.{tld}/{path_word}"),
600 1 => format!("{scheme}://{word}.{tld}"),
601 2 => {
602 let slug = self.gen_slug();
603 format!("{scheme}://{word}.{tld}/blog/{slug}")
604 }
605 _ => {
606 let id: u32 = self.rng.gen_range(1..99999);
607 format!("{scheme}://{word}.{tld}/item/{id}")
608 }
609 }
610 }
611
612 fn gen_ipv4(&mut self) -> String {
613 let a = self.rng.gen_range(1u8..=254);
614 let b = self.rng.gen_range(0u8..=255);
615 let c = self.rng.gen_range(0u8..=255);
616 let d = self.rng.gen_range(1u8..=254);
617 format!("{a}.{b}.{c}.{d}")
618 }
619
620 fn gen_ipv6(&mut self) -> String {
621 let groups: Vec<String> = (0..8)
622 .map(|_| format!("{:04x}", self.rng.gen_range(0u16..=0xffff)))
623 .collect();
624 groups.join(":")
625 }
626
627 fn gen_hostname(&mut self) -> String {
628 let sub = self.pick(WORDS).to_lowercase();
629 let tld = self.pick(TLDS);
630 let word = self.pick(WORDS).to_lowercase();
631 format!("{sub}.{word}.{tld}")
632 }
633
634 fn gen_date(&mut self) -> String {
635 let y = self.rng.gen_range(2000..=2030);
636 let m = self.rng.gen_range(1..=12);
637 let d = self.rng.gen_range(1..=days_in_month(m));
638 format!("{y:04}-{m:02}-{d:02}")
639 }
640
641 fn gen_time(&mut self) -> String {
642 let h = self.rng.gen_range(0..24);
643 let m = self.rng.gen_range(0..60);
644 let s = self.rng.gen_range(0..60);
645 format!("{h:02}:{m:02}:{s:02}")
646 }
647
648 fn gen_datetime(&mut self) -> String {
649 let date = self.gen_date();
650 let time = self.gen_time();
651 format!("{date}T{time}Z")
652 }
653
654 fn gen_base64(&mut self) -> String {
655 let byte_len = self.rng.gen_range(8..32);
656 let bytes: Vec<u8> = (0..byte_len).map(|_| self.rng.gen()).collect();
657 base64_encode(&bytes)
658 }
659
660 fn gen_cuid2(&mut self) -> String {
661 let first = LOWER_ALPHA[self.rng.gen_range(0..LOWER_ALPHA.len())] as char;
662 let rest: String = (0..23)
663 .map(|_| LOWER_DIGIT[self.rng.gen_range(0..LOWER_DIGIT.len())] as char)
664 .collect();
665 format!("{first}{rest}")
666 }
667
668 fn gen_ulid(&mut self) -> String {
669 (0..26)
670 .map(|_| CROCKFORD[self.rng.gen_range(0..CROCKFORD.len())] as char)
671 .collect()
672 }
673
674 fn gen_nanoid(&mut self) -> String {
675 let len = self.rng.gen_range(10..22);
676 (0..len)
677 .map(|_| NANOID_ALPHA[self.rng.gen_range(0..NANOID_ALPHA.len())] as char)
678 .collect()
679 }
680
681 fn gen_emoji(&mut self) -> String {
682 self.pick(EMOJIS).to_string()
683 }
684
685 fn gen_phone(&mut self) -> String {
686 let area: u16 = self.rng.gen_range(200..999);
687 let prefix: u16 = self.rng.gen_range(200..999);
688 let line: u16 = self.rng.gen_range(1000..9999);
689 match self.rng.gen_range(0..3) {
690 0 => format!("+1 ({area}) {prefix}-{line}"),
691 1 => format!("+44 {area} {prefix} {line}"),
692 _ => format!("+7 {area} {prefix}-{line}"),
693 }
694 }
695
696 fn gen_credit_card(&mut self) -> String {
697 let mut digits = vec![4u8];
699 for _ in 1..15 {
700 digits.push(self.rng.gen_range(0..10));
701 }
702 let check = luhn_check_digit(&digits);
704 digits.push(check);
705 let s: String = digits.iter().map(|d| (b'0' + d) as char).collect();
706 format!("{}-{}-{}-{}", &s[0..4], &s[4..8], &s[8..12], &s[12..16])
707 }
708
709 fn gen_isbn(&mut self) -> String {
710 let mut digits: Vec<u8> = (0..12).map(|_| self.rng.gen_range(0..10)).collect();
711 digits[0] = 9;
712 digits[1] = 7;
713 digits[2] = if self.rng.gen_bool(0.5) { 8 } else { 9 };
714 let sum: u8 = digits
715 .iter()
716 .enumerate()
717 .map(|(i, &d)| if i % 2 == 0 { d } else { d * 3 })
718 .sum();
719 let check = (10 - (sum % 10)) % 10;
720 digits.push(check);
721 let s: String = digits.iter().map(|d| (b'0' + d) as char).collect();
722 format!(
723 "{}-{}-{}-{}-{}",
724 &s[0..3],
725 &s[3..4],
726 &s[4..8],
727 &s[8..12],
728 &s[12..13]
729 )
730 }
731
732 fn gen_mac_address(&mut self) -> String {
733 let octets: Vec<String> = (0..6)
734 .map(|_| format!("{:02X}", self.rng.gen_range(0u8..=255)))
735 .collect();
736 octets.join(":")
737 }
738
739 fn gen_hex_color(&mut self) -> String {
740 format!(
741 "#{:02x}{:02x}{:02x}",
742 self.rng.gen_range(0u8..=255),
743 self.rng.gen_range(0u8..=255),
744 self.rng.gen_range(0u8..=255),
745 )
746 }
747
748 fn gen_user_agent(&mut self) -> String {
749 let browsers = [
750 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
751 "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
752 "Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0",
753 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
754 ];
755 self.pick(&browsers).to_string()
756 }
757
758 fn gen_slug(&mut self) -> String {
759 let count = self.rng.gen_range(2..5);
760 let words: Vec<String> = (0..count)
761 .map(|_| self.pick(WORDS).to_lowercase())
762 .collect();
763 words.join("-")
764 }
765
766 fn gen_token(&mut self, len: usize) -> String {
767 (0..len)
768 .map(|_| TOKEN_ALPHA[self.rng.gen_range(0..TOKEN_ALPHA.len())] as char)
769 .collect()
770 }
771
772 fn gen_password(&mut self) -> String {
773 let len = self.rng.gen_range(10..18);
774 let mut s: String = (0..len)
775 .map(|_| PASSWORD_ALPHA[self.rng.gen_range(0..PASSWORD_ALPHA.len())] as char)
776 .collect();
777 let positions: Vec<usize> = (0..s.len()).collect();
779 if s.len() >= 4 {
780 let bytes = unsafe { s.as_bytes_mut() };
781 bytes[positions[0]] = b'A' + self.rng.gen_range(0..26);
782 bytes[positions[1]] = b'a' + self.rng.gen_range(0..26);
783 bytes[positions[2]] = b'0' + self.rng.gen_range(0..10);
784 let specials = b"!@#$%&*";
785 bytes[positions[3]] = specials[self.rng.gen_range(0..specials.len())];
786 }
787 s
788 }
789
790 fn gen_street_address(&mut self) -> String {
791 let num: u16 = self.rng.gen_range(1..9999);
792 let street = self.pick(STREET_NAMES);
793 let suffix = self.pick(STREET_SUFFIXES);
794 format!("{num} {street} {suffix}")
795 }
796
797 fn gen_zip(&mut self) -> String {
798 let n: u32 = self.rng.gen_range(10000..99999);
799 format!("{n}")
800 }
801
802 fn gen_sentence_range(&mut self, min_words: usize, max_words: usize) -> String {
803 let count = self.rng.gen_range(min_words..=max_words);
804 let mut words: Vec<String> = (0..count)
805 .map(|_| self.pick(LOREM_WORDS).to_string())
806 .collect();
807 if let Some(first) = words.first_mut() {
808 let mut chars = first.chars();
809 *first = chars.next().unwrap().to_uppercase().chain(chars).collect();
810 }
811 let mut sentence = words.join(" ");
812 sentence.push('.');
813 sentence
814 }
815
816 fn gen_integer(&mut self, obj: &Map<String, Value>, hint: Option<&str>) -> Value {
821 let min = self.num_as_i64(obj, "minimum", "exclusiveMinimum", 1);
822 let max = self.num_as_i64_max(obj, "maximum", "exclusiveMaximum", 1);
823
824 let (min, max) = if min > max { (max, min) } else { (min, max) };
825
826 let (min, max) = if let Some(h) = hint {
828 self.adjust_int_range_by_hint(h, min, max, obj)
829 } else {
830 (min, max)
831 };
832
833 let mut val = self.rng.gen_range(min..=max);
834
835 if let Some(m) = obj
837 .get("multipleOf")
838 .and_then(|v| v.as_i64().or_else(|| v.as_f64().map(|f| f as i64)))
839 {
840 if m > 0 {
841 val = (val / m) * m;
842 if val < min {
843 val += m;
844 }
845 }
846 }
847
848 json!(val)
849 }
850
851 fn adjust_int_range_by_hint(
852 &self,
853 hint: &str,
854 schema_min: i64,
855 schema_max: i64,
856 obj: &Map<String, Value>,
857 ) -> (i64, i64) {
858 let h = hint.to_ascii_lowercase();
859 let has_min = obj.contains_key("minimum") || obj.contains_key("exclusiveMinimum");
860 let has_max = obj.contains_key("maximum") || obj.contains_key("exclusiveMaximum");
861
862 if has_min && has_max {
863 return (schema_min, schema_max);
864 }
865
866 match h.as_str() {
867 "age" if !has_max => (schema_min, schema_max.min(99)),
868 "port" if !has_max => (schema_min.max(1024), schema_max.min(65535)),
869 "year" if !has_min => (2000.max(schema_min), schema_max.min(2030)),
870 "quantity" | "qty" | "count" if !has_max => (schema_min, schema_max.min(100)),
871 "rating" | "score" if !has_max => (schema_min, schema_max.min(10)),
872 _ => (schema_min, schema_max),
873 }
874 }
875
876 fn num_as_i64(&self, obj: &Map<String, Value>, key: &str, ex_key: &str, offset: i64) -> i64 {
877 if let Some(v) = obj
878 .get(key)
879 .and_then(|v| v.as_i64().or_else(|| v.as_f64().map(|f| f as i64)))
880 {
881 return v;
882 }
883 if let Some(v) = obj
884 .get(ex_key)
885 .and_then(|v| v.as_i64().or_else(|| v.as_f64().map(|f| f as i64)))
886 {
887 return v + offset;
888 }
889 0
890 }
891
892 fn num_as_i64_max(
893 &self,
894 obj: &Map<String, Value>,
895 key: &str,
896 ex_key: &str,
897 offset: i64,
898 ) -> i64 {
899 if let Some(v) = obj
900 .get(key)
901 .and_then(|v| v.as_i64().or_else(|| v.as_f64().map(|f| f as i64)))
902 {
903 return v;
904 }
905 if let Some(v) = obj
906 .get(ex_key)
907 .and_then(|v| v.as_i64().or_else(|| v.as_f64().map(|f| f as i64)))
908 {
909 return v - offset;
910 }
911 1000
912 }
913
914 fn gen_number(&mut self, obj: &Map<String, Value>, hint: Option<&str>) -> Value {
919 if obj.get("type").and_then(Value::as_str) == Some("integer") {
920 return self.gen_integer(obj, hint);
921 }
922
923 if let Some(h) = hint {
925 let has_min = obj.contains_key("minimum") || obj.contains_key("exclusiveMinimum");
926 let has_max = obj.contains_key("maximum") || obj.contains_key("exclusiveMaximum");
927 if !has_min && !has_max {
928 if let Some(val) = self.gen_number_by_hint(h) {
929 return json!(val);
930 }
931 }
932 }
933
934 let min = obj
935 .get("minimum")
936 .and_then(Value::as_f64)
937 .or_else(|| {
938 obj.get("exclusiveMinimum")
939 .and_then(Value::as_f64)
940 .map(|v| v + 0.01)
941 })
942 .unwrap_or(0.0);
943 let max = obj
944 .get("maximum")
945 .and_then(Value::as_f64)
946 .or_else(|| {
947 obj.get("exclusiveMaximum")
948 .and_then(Value::as_f64)
949 .map(|v| v - 0.01)
950 })
951 .unwrap_or(min + 1000.0);
952
953 let max = if max < min { min + 1.0 } else { max };
954
955 let val = min + self.rng.gen::<f64>() * (max - min);
956
957 let val = (val * 100.0).round() / 100.0;
959
960 json!(val)
961 }
962
963 fn gen_number_by_hint(&mut self, hint: &str) -> Option<f64> {
965 let h = hint.to_ascii_lowercase();
966 match h.as_str() {
967 "latitude" | "lat" => {
968 let v: f64 = self.rng.gen_range(-90.0..=90.0);
969 Some((v * 1_000_000.0).round() / 1_000_000.0)
970 }
971 "longitude" | "lng" | "lon" => {
972 let v: f64 = self.rng.gen_range(-180.0..=180.0);
973 Some((v * 1_000_000.0).round() / 1_000_000.0)
974 }
975 "altitude" | "elevation" | "alt" => {
976 let v: f64 = self.rng.gen_range(0.0..=8848.0);
977 Some((v * 100.0).round() / 100.0)
978 }
979 "price" | "cost" | "amount" | "total" | "subtotal" | "fee" | "balance" | "payment" => {
980 let v: f64 = self.rng.gen_range(0.01..=9999.99);
981 Some((v * 100.0).round() / 100.0)
982 }
983 "tax" | "discount" | "vat" => {
984 let v: f64 = self.rng.gen_range(0.0..=30.0);
985 Some((v * 100.0).round() / 100.0)
986 }
987 "rating" | "score" => {
988 let v: f64 = self.rng.gen_range(1.0..=5.0);
989 Some((v * 10.0).round() / 10.0)
990 }
991 "weight" | "mass" => {
992 let v: f64 = self.rng.gen_range(0.1..=1000.0);
993 Some((v * 100.0).round() / 100.0)
994 }
995 "temperature" | "temp" => {
996 let v: f64 = self.rng.gen_range(-40.0..=50.0);
997 Some((v * 10.0).round() / 10.0)
998 }
999 "percentage" | "percent" | "progress" => {
1000 let v: f64 = self.rng.gen_range(0.0..=100.0);
1001 Some((v * 100.0).round() / 100.0)
1002 }
1003 "speed" | "velocity" => {
1004 let v: f64 = self.rng.gen_range(0.0..=300.0);
1005 Some((v * 100.0).round() / 100.0)
1006 }
1007 "distance" | "radius" => {
1008 let v: f64 = self.rng.gen_range(0.1..=10000.0);
1009 Some((v * 100.0).round() / 100.0)
1010 }
1011 "area" => {
1012 let v: f64 = self.rng.gen_range(1.0..=100000.0);
1013 Some((v * 100.0).round() / 100.0)
1014 }
1015 _ => None,
1016 }
1017 }
1018
1019 fn gen_boolean(&mut self) -> Value {
1024 Value::Bool(self.rng.gen())
1025 }
1026
1027 fn gen_array(&mut self, obj: &Map<String, Value>, _hint: Option<&str>) -> Value {
1032 let min_items = obj.get("minItems").and_then(Value::as_u64).unwrap_or(0) as usize;
1033 let max_items = obj
1034 .get("maxItems")
1035 .and_then(Value::as_u64)
1036 .unwrap_or(min_items.max(1) as u64 + 3) as usize;
1037 let max_items = max_items.max(min_items);
1038
1039 let count = if min_items == max_items {
1040 min_items
1041 } else {
1042 self.rng.gen_range(min_items..=max_items)
1043 };
1044
1045 if let Some(prefix) = obj.get("prefixItems").and_then(Value::as_array) {
1047 let mut arr: Vec<Value> = prefix.iter().map(|s| self.value(s)).collect();
1048 if count > arr.len() {
1049 if let Some(items) = obj.get("items") {
1050 for _ in arr.len()..count {
1051 arr.push(self.value(items));
1052 }
1053 }
1054 }
1055 return Value::Array(arr);
1056 }
1057
1058 let unique = obj
1060 .get("uniqueItems")
1061 .and_then(Value::as_bool)
1062 .unwrap_or(false);
1063
1064 let items_schema = obj
1065 .get("items")
1066 .cloned()
1067 .unwrap_or(json!({"type": "string"}));
1068
1069 if unique {
1070 let mut arr = Vec::new();
1071 let mut attempts = 0;
1072 while arr.len() < count && attempts < count * 10 {
1073 let v = self.value(&items_schema);
1074 if !arr.contains(&v) {
1075 arr.push(v);
1076 }
1077 attempts += 1;
1078 }
1079 Value::Array(arr)
1080 } else {
1081 let arr: Vec<Value> = (0..count).map(|_| self.value(&items_schema)).collect();
1082 Value::Array(arr)
1083 }
1084 }
1085
1086 fn gen_object(&mut self, obj: &Map<String, Value>, hint: Option<&str>) -> Value {
1091 let has_properties = obj
1092 .get("properties")
1093 .and_then(Value::as_object)
1094 .is_some_and(|p| !p.is_empty());
1095 let has_required = obj
1096 .get("required")
1097 .and_then(Value::as_array)
1098 .is_some_and(|r| !r.is_empty());
1099
1100 if !has_properties && !has_required {
1103 if let Some(h) = hint {
1104 if let Some(tmpl) = self.gen_object_template(h) {
1105 return tmpl;
1106 }
1107 }
1108 }
1109
1110 let mut result = Map::new();
1111
1112 let required: Vec<String> = obj
1113 .get("required")
1114 .and_then(Value::as_array)
1115 .map(|arr| {
1116 arr.iter()
1117 .filter_map(Value::as_str)
1118 .map(String::from)
1119 .collect()
1120 })
1121 .unwrap_or_default();
1122
1123 if let Some(props) = obj.get("properties").and_then(Value::as_object) {
1124 for (key, prop_schema) in props {
1125 if required.contains(key) || self.rng.gen_bool(0.7) {
1126 result.insert(key.clone(), self.value_with_hint(prop_schema, Some(key)));
1127 }
1128 }
1129 }
1130
1131 if let Some(additional) = obj.get("additionalProperties") {
1133 if additional.is_object() {
1134 let extra_count = self.rng.gen_range(0..3usize);
1135 for _ in 0..extra_count {
1136 let key = format!("{}_{}", self.pick(WORDS), self.next_id());
1137 result.insert(key, self.value(additional));
1138 }
1139 }
1140 }
1141
1142 Value::Object(result)
1143 }
1144
1145 fn gen_object_template(&mut self, hint: &str) -> Option<Value> {
1148 let h = hint.to_ascii_lowercase();
1149 match h.as_str() {
1150 "address" | "billing_address" | "billingaddress" | "shipping_address"
1152 | "shippingaddress" | "home_address" | "work_address" | "mailing_address" => {
1153 Some(self.template_address())
1154 }
1155
1156 "location" | "geo" | "geolocation" | "coordinates" | "coords" | "position" | "gps" => {
1158 Some(self.template_geo())
1159 }
1160
1161 "place" | "venue" | "point_of_interest" | "poi" => Some(self.template_place()),
1162
1163 "person" | "user" | "author" | "owner" | "creator" | "contact" | "sender"
1165 | "recipient" | "assignee" | "reviewer" | "member" | "employee" | "customer"
1166 | "client" | "patient" => Some(self.template_person()),
1167 "profile" | "user_profile" | "account" => Some(self.template_profile()),
1168
1169 "company" | "organization" | "organisation" | "employer" | "business" => {
1171 Some(self.template_company())
1172 }
1173
1174 "product" | "item" | "goods" | "merchandise" => Some(self.template_product()),
1176
1177 "price" | "money" | "amount" | "cost" | "payment" | "transaction" => {
1179 Some(self.template_money())
1180 }
1181
1182 "period" | "date_range" | "daterange" | "time_range" | "timerange" | "duration"
1184 | "schedule" | "availability" => Some(self.template_date_range()),
1185
1186 "config" | "configuration" | "settings" | "options" | "preferences" => {
1188 Some(self.template_config())
1189 }
1190 "metadata" | "meta" | "extra" | "attributes" | "props" | "info" => {
1191 Some(self.template_metadata())
1192 }
1193
1194 "image" | "photo" | "picture" | "avatar" | "thumbnail" | "media" => {
1196 Some(self.template_image())
1197 }
1198
1199 "dimensions" | "size" | "resolution" => Some(self.template_dimensions()),
1201
1202 _ => None,
1203 }
1204 }
1205
1206 fn template_address(&mut self) -> Value {
1209 let num: u16 = self.rng.gen_range(1..9999);
1210 let street = self.pick(STREET_NAMES);
1211 let suffix = self.pick(STREET_SUFFIXES);
1212 let city = self.pick(CITIES);
1213 let state = self.pick(STATES);
1214 let country = self.pick(COUNTRIES);
1215 let zip: u32 = self.rng.gen_range(10000..99999);
1216 let lat: f64 = self.rng.gen_range(-90.0..=90.0);
1217 let lng: f64 = self.rng.gen_range(-180.0..=180.0);
1218
1219 json!({
1220 "street": format!("{num} {street} {suffix}"),
1221 "city": city,
1222 "state": state,
1223 "country": country,
1224 "zip": format!("{zip}"),
1225 "latitude": (lat * 1_000_000.0).round() / 1_000_000.0,
1226 "longitude": (lng * 1_000_000.0).round() / 1_000_000.0,
1227 })
1228 }
1229
1230 fn template_geo(&mut self) -> Value {
1231 let lat: f64 = self.rng.gen_range(-90.0..=90.0);
1232 let lng: f64 = self.rng.gen_range(-180.0..=180.0);
1233 let alt: f64 = self.rng.gen_range(0.0..=8848.0);
1234 let accuracy: f64 = self.rng.gen_range(1.0..=100.0);
1235
1236 json!({
1237 "latitude": (lat * 1_000_000.0).round() / 1_000_000.0,
1238 "longitude": (lng * 1_000_000.0).round() / 1_000_000.0,
1239 "altitude": (alt * 100.0).round() / 100.0,
1240 "accuracy": (accuracy * 100.0).round() / 100.0,
1241 })
1242 }
1243
1244 fn template_place(&mut self) -> Value {
1245 let city = self.pick(CITIES);
1246 let country = self.pick(COUNTRIES);
1247 let lat: f64 = self.rng.gen_range(-90.0..=90.0);
1248 let lng: f64 = self.rng.gen_range(-180.0..=180.0);
1249 let category = self.pick(PLACE_CATEGORIES);
1250 let adj = self.pick(ADJECTIVES);
1251 let noun = self.pick(PLACE_NOUNS);
1252
1253 json!({
1254 "name": format!("{adj} {noun}"),
1255 "category": category,
1256 "city": city,
1257 "country": country,
1258 "latitude": (lat * 1_000_000.0).round() / 1_000_000.0,
1259 "longitude": (lng * 1_000_000.0).round() / 1_000_000.0,
1260 })
1261 }
1262
1263 fn template_person(&mut self) -> Value {
1264 let first = self.pick(FIRST_NAMES);
1265 let last = self.pick(LAST_NAMES);
1266 let email = self.gen_email();
1267 let phone = self.gen_phone();
1268
1269 json!({
1270 "first_name": first,
1271 "last_name": last,
1272 "email": email,
1273 "phone": phone,
1274 })
1275 }
1276
1277 fn template_profile(&mut self) -> Value {
1278 let first = self.pick(FIRST_NAMES);
1279 let last = self.pick(LAST_NAMES);
1280 let email = self.gen_email();
1281 let phone = self.gen_phone();
1282 let username = format!("{}{}", first.to_lowercase(), self.rng.gen_range(1u16..999));
1283 let job = self.pick(JOB_TITLES);
1284 let company = self.pick(COMPANIES);
1285 let city = self.pick(CITIES);
1286 let bio = self.gen_sentence_range(8, 16);
1287 let avatar_id: u32 = self.rng.gen_range(1..999);
1288
1289 json!({
1290 "username": username,
1291 "first_name": first,
1292 "last_name": last,
1293 "email": email,
1294 "phone": phone,
1295 "job_title": job,
1296 "company": company,
1297 "city": city,
1298 "bio": bio,
1299 "avatar_url": format!("https://i.pravatar.cc/300?u={avatar_id}"),
1300 })
1301 }
1302
1303 fn template_company(&mut self) -> Value {
1304 let name = self.pick(COMPANIES);
1305 let department = self.pick(DEPARTMENTS);
1306 let industry = self.pick(INDUSTRIES);
1307 let employees: u32 = self.rng.gen_range(10..50000);
1308 let founded: u16 = self.rng.gen_range(1950..2024);
1309 let city = self.pick(CITIES);
1310 let country = self.pick(COUNTRIES);
1311 let website = self.gen_url();
1312
1313 json!({
1314 "name": name,
1315 "industry": industry,
1316 "department": department,
1317 "employees": employees,
1318 "founded": founded,
1319 "city": city,
1320 "country": country,
1321 "website": website,
1322 })
1323 }
1324
1325 fn template_product(&mut self) -> Value {
1326 let adj = self.pick(ADJECTIVES);
1327 let noun = self.pick(PRODUCT_NOUNS);
1328 let price: f64 = self.rng.gen_range(0.99..9999.99);
1329 let category = self.pick(CATEGORIES);
1330 let sku_prefix: String = (0..3)
1331 .map(|_| self.rng.gen_range(b'A'..=b'Z') as char)
1332 .collect();
1333 let sku_num: u32 = self.rng.gen_range(10000..99999);
1334 let rating: f64 = self.rng.gen_range(1.0..5.0);
1335
1336 json!({
1337 "name": format!("{adj} {noun}"),
1338 "sku": format!("{sku_prefix}-{sku_num}"),
1339 "price": (price * 100.0).round() / 100.0,
1340 "currency": self.pick(CURRENCIES),
1341 "category": category,
1342 "rating": (rating * 10.0).round() / 10.0,
1343 "in_stock": self.rng.gen_bool(0.8),
1344 })
1345 }
1346
1347 fn template_money(&mut self) -> Value {
1348 let amount: f64 = self.rng.gen_range(0.01..99999.99);
1349 let currency = self.pick(CURRENCIES);
1350
1351 json!({
1352 "amount": (amount * 100.0).round() / 100.0,
1353 "currency": currency,
1354 })
1355 }
1356
1357 fn template_date_range(&mut self) -> Value {
1358 let start = self.gen_datetime();
1359 let end = self.gen_datetime();
1360
1361 json!({
1362 "start": start,
1363 "end": end,
1364 })
1365 }
1366
1367 fn template_config(&mut self) -> Value {
1368 let env = self.pick(&["development", "staging", "production", "test"]);
1369 let port: u16 = self.rng.gen_range(3000..9999);
1370 let host = self.gen_hostname();
1371
1372 json!({
1373 "environment": env,
1374 "host": host,
1375 "port": port,
1376 "debug": self.rng.gen_bool(0.3),
1377 "log_level": self.pick(&["debug", "info", "warn", "error"]),
1378 })
1379 }
1380
1381 fn template_metadata(&mut self) -> Value {
1382 let created = self.gen_datetime();
1383 let updated = self.gen_datetime();
1384 let version = format!(
1385 "{}.{}.{}",
1386 self.rng.gen_range(0u8..10),
1387 self.rng.gen_range(0u8..30),
1388 self.rng.gen_range(0u16..100)
1389 );
1390
1391 json!({
1392 "created_at": created,
1393 "updated_at": updated,
1394 "version": version,
1395 "source": self.pick(&["api", "web", "mobile", "import", "sync"]),
1396 })
1397 }
1398
1399 fn template_image(&mut self) -> Value {
1400 let w: u16 = self.rng.gen_range(100..4000);
1401 let h: u16 = self.rng.gen_range(100..4000);
1402 let ext = self.pick(&["jpg", "png", "webp", "gif"]);
1403 let id: u32 = self.rng.gen_range(1..99999);
1404
1405 json!({
1406 "url": format!("https://picsum.photos/{w}/{h}?random={id}"),
1407 "width": w,
1408 "height": h,
1409 "format": ext,
1410 "size_bytes": self.rng.gen_range(10_000u32..10_000_000),
1411 })
1412 }
1413
1414 fn template_dimensions(&mut self) -> Value {
1415 let w: f64 = self.rng.gen_range(1.0..1000.0);
1416 let h: f64 = self.rng.gen_range(1.0..1000.0);
1417 let d: f64 = self.rng.gen_range(1.0..500.0);
1418 let unit = self.pick(&["mm", "cm", "in", "px", "m"]);
1419
1420 json!({
1421 "width": (w * 100.0).round() / 100.0,
1422 "height": (h * 100.0).round() / 100.0,
1423 "depth": (d * 100.0).round() / 100.0,
1424 "unit": unit,
1425 })
1426 }
1427
1428 fn merge_all_of(&mut self, schemas: &[Value]) -> Value {
1433 let mut merged = Map::new();
1434 let mut required: Vec<String> = Vec::new();
1435
1436 for s in schemas {
1437 if let Some(obj) = s.as_object() {
1438 if let Some(props) = obj.get("properties").and_then(Value::as_object) {
1439 for (k, v) in props {
1440 merged
1441 .entry("properties")
1442 .or_insert_with(|| json!({}))
1443 .as_object_mut()
1444 .unwrap()
1445 .insert(k.clone(), v.clone());
1446 }
1447 }
1448 if let Some(req) = obj.get("required").and_then(Value::as_array) {
1449 for r in req {
1450 if let Some(s) = r.as_str() {
1451 if !required.contains(&s.to_string()) {
1452 required.push(s.to_string());
1453 }
1454 }
1455 }
1456 }
1457 if let Some(ty) = obj.get("type") {
1458 merged.insert("type".into(), ty.clone());
1459 }
1460 }
1461 }
1462
1463 if !required.is_empty() {
1464 merged.insert(
1465 "required".into(),
1466 Value::Array(required.into_iter().map(Value::String).collect()),
1467 );
1468 }
1469
1470 self.value(&Value::Object(merged))
1471 }
1472}
1473
1474fn days_in_month(m: u32) -> u32 {
1479 match m {
1480 2 => 28,
1481 4 | 6 | 9 | 11 => 30,
1482 _ => 31,
1483 }
1484}
1485
1486fn luhn_check_digit(digits: &[u8]) -> u8 {
1487 let mut sum: u16 = 0;
1488 for (i, &d) in digits.iter().rev().enumerate() {
1489 let mut v = d as u16;
1490 if i % 2 == 0 {
1491 v *= 2;
1492 if v > 9 {
1493 v -= 9;
1494 }
1495 }
1496 sum += v;
1497 }
1498 ((10 - (sum % 10)) % 10) as u8
1499}
1500
1501const LOWER_ALPHA: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
1502const LOWER_DIGIT: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
1503const HEX: &[u8] = b"0123456789abcdef";
1504const HEX_89AB: &[u8] = b"89ab";
1505const CROCKFORD: &[u8] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
1506const NANOID_ALPHA: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
1507const TOKEN_ALPHA: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1508const PASSWORD_ALPHA: &[u8] =
1509 b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%&*";
1510
1511fn base64_encode(data: &[u8]) -> String {
1512 const TABLE: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1513 let mut out = String::with_capacity((data.len() + 2) / 3 * 4);
1514 for chunk in data.chunks(3) {
1515 let b0 = chunk[0] as u32;
1516 let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 };
1517 let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 };
1518 let triple = (b0 << 16) | (b1 << 8) | b2;
1519 out.push(TABLE[((triple >> 18) & 0x3F) as usize] as char);
1520 out.push(TABLE[((triple >> 12) & 0x3F) as usize] as char);
1521 if chunk.len() > 1 {
1522 out.push(TABLE[((triple >> 6) & 0x3F) as usize] as char);
1523 } else {
1524 out.push('=');
1525 }
1526 if chunk.len() > 2 {
1527 out.push(TABLE[(triple & 0x3F) as usize] as char);
1528 } else {
1529 out.push('=');
1530 }
1531 }
1532 out
1533}
1534
1535pub trait FakeData: Sized {
1550 fn fake() -> Self;
1552
1553 fn fake_many(count: usize) -> Vec<Self>;
1555
1556 fn fake_seeded(seed: u64) -> Self;
1558
1559 fn try_fake() -> Result<Self, vld::error::VldError>;
1562}
1563
1564#[macro_export]
1589macro_rules! impl_fake {
1590 ($ty:ty) => {
1591 impl $crate::FakeData for $ty {
1592 fn fake() -> Self {
1593 let schema = <$ty>::json_schema();
1594 $crate::fake_parsed::<$ty>(&schema)
1595 }
1596
1597 fn fake_many(count: usize) -> Vec<Self> {
1598 let schema = <$ty>::json_schema();
1599 let mut gen = $crate::FakeGen::new();
1600 (0..count)
1601 .map(|_| {
1602 let val = gen.value(&schema);
1603 <$ty as ::vld::prelude::VldParse>::vld_parse_value(&val)
1604 .expect("vld_fake: generated value failed validation")
1605 })
1606 .collect()
1607 }
1608
1609 fn fake_seeded(seed: u64) -> Self {
1610 let schema = <$ty>::json_schema();
1611 let val = $crate::fake_value_seeded(&schema, seed);
1612 <$ty as ::vld::prelude::VldParse>::vld_parse_value(&val)
1613 .expect("vld_fake: generated value failed validation")
1614 }
1615
1616 fn try_fake() -> Result<Self, ::vld::error::VldError> {
1617 let schema = <$ty>::json_schema();
1618 $crate::try_fake_parsed::<$ty>(&schema)
1619 }
1620 }
1621 };
1622}
1623
1624pub mod prelude {
1627 pub use crate::{
1628 fake_json, fake_many, fake_parsed, fake_value, fake_value_seeded, impl_fake,
1629 try_fake_parsed, FakeData, FakeGen,
1630 };
1631}