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.
Files changed (302) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rubocop.yml +40 -0
  4. data/.yardopts +18 -0
  5. data/ACCESS_DOCS.md +116 -0
  6. data/ADVANCED_FEATURES_SUMMARY.md +245 -0
  7. data/CHANGELOG.md +498 -0
  8. data/CODE_OF_CONDUCT.md +122 -0
  9. data/CONTRIBUTING.md +307 -0
  10. data/DOCUMENTATION.md +204 -0
  11. data/DOCUMENTATION_GUIDE.md +194 -0
  12. data/LICENSE +22 -0
  13. data/README.md +1458 -0
  14. data/Rakefile +128 -0
  15. data/benchmark/README.md +591 -0
  16. data/benchmark/batch_generation.rb +63 -0
  17. data/benchmark/engine_comparison.rb +295 -0
  18. data/benchmark/single_record.rb +108 -0
  19. data/benchmark/streaming.rb +74 -0
  20. data/docs/ACTIVERECORD_INFERENCE.md +524 -0
  21. data/docs/API_SERVER.md +278 -0
  22. data/docs/CONFIG_FILE.md +315 -0
  23. data/docs/DATA_CONTRACTS.md +312 -0
  24. data/docs/ENHANCED_REPL.md +304 -0
  25. data/docs/FACTORY_BOT.md +271 -0
  26. data/docs/GITHUB_ACTION.md +420 -0
  27. data/docs/GRAPHQL_EXPORT.md +303 -0
  28. data/docs/GRAPHQL_FEDERATION.md +651 -0
  29. data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
  30. data/docs/LSP.md +189 -0
  31. data/docs/MIGRATION_GENERATOR.md +547 -0
  32. data/docs/MOCK_SERVER.md +280 -0
  33. data/docs/NATIVE_ENGINE.md +298 -0
  34. data/docs/OPENAPI_EXPORT.md +252 -0
  35. data/docs/PERFORMANCE_MODE.md +228 -0
  36. data/docs/PERSONAS.md +547 -0
  37. data/docs/PROPERTY_TESTING.md +284 -0
  38. data/docs/PROTOBUF_EXPORT.md +276 -0
  39. data/docs/QUALITY_METRICS.md +555 -0
  40. data/docs/QUICK_REFERENCE.md +88 -0
  41. data/docs/RAILS_ENGINE.md +404 -0
  42. data/docs/RAILS_INTEGRATION.md +454 -0
  43. data/docs/README.md +175 -0
  44. data/docs/REPL_QUICK_REFERENCE.md +149 -0
  45. data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
  46. data/docs/SCENARIOS.md +576 -0
  47. data/docs/SECURITY_TESTING.md +318 -0
  48. data/docs/SNAPSHOT_TESTING.md +308 -0
  49. data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
  50. data/docs/TERRAFORM_EXPORT.md +447 -0
  51. data/docs/TIME_TRAVEL.md +507 -0
  52. data/docs/USAGE_GUIDE.md +980 -0
  53. data/docs/WEBHOOK_SIMULATOR.md +579 -0
  54. data/docs/advanced_features.md +183 -0
  55. data/docs/api_reference.md +153 -0
  56. data/docs/behaviors.md +138 -0
  57. data/docs/best_practices.md +191 -0
  58. data/docs/dsl_reference.md +172 -0
  59. data/docs/examples.md +162 -0
  60. data/docs/generation_modes.md +125 -0
  61. data/docs/getting_started.md +92 -0
  62. data/docs/index.html +177 -0
  63. data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
  64. data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
  65. data/docs/troubleshooting.md +215 -0
  66. data/docs/type_reference.md +224 -0
  67. data/examples/hostile_mode_demo.rb +64 -0
  68. data/exe/synthra +6 -0
  69. data/ext/synthra_native/Cargo.lock +972 -0
  70. data/ext/synthra_native/Cargo.toml +46 -0
  71. data/ext/synthra_native/extconf.rb +41 -0
  72. data/ext/synthra_native/src/generator.rs +397 -0
  73. data/ext/synthra_native/src/lib.rs +229 -0
  74. data/ext/synthra_native/src/types.rs +1485 -0
  75. data/lib/generators/synthra/install_generator.rb +86 -0
  76. data/lib/generators/synthra/templates/api_response.dsl +15 -0
  77. data/lib/generators/synthra/templates/fake_data.rake +116 -0
  78. data/lib/generators/synthra/templates/synthra.yml +31 -0
  79. data/lib/generators/synthra/templates/synthra_support.rb +19 -0
  80. data/lib/generators/synthra/templates/user.dsl +11 -0
  81. data/lib/synthra/activerecord_inference.rb +394 -0
  82. data/lib/synthra/api.rb +381 -0
  83. data/lib/synthra/api_server.rb +718 -0
  84. data/lib/synthra/behaviors/applicator.rb +165 -0
  85. data/lib/synthra/behaviors/base.rb +155 -0
  86. data/lib/synthra/behaviors/close_connection.rb +60 -0
  87. data/lib/synthra/behaviors/deprecated.rb +100 -0
  88. data/lib/synthra/behaviors/failure.rb +69 -0
  89. data/lib/synthra/behaviors/latency.rb +103 -0
  90. data/lib/synthra/behaviors/partial_data.rb +83 -0
  91. data/lib/synthra/behaviors/randomize_order.rb +74 -0
  92. data/lib/synthra/behaviors/registry.rb +238 -0
  93. data/lib/synthra/behaviors/simulate_error.rb +79 -0
  94. data/lib/synthra/cli/commands/base.rb +105 -0
  95. data/lib/synthra/cli/commands/diff.rb +227 -0
  96. data/lib/synthra/cli/commands/docs.rb +67 -0
  97. data/lib/synthra/cli/commands/export.rb +150 -0
  98. data/lib/synthra/cli/commands/generate.rb +72 -0
  99. data/lib/synthra/cli/commands/import.rb +80 -0
  100. data/lib/synthra/cli/commands/lint.rb +53 -0
  101. data/lib/synthra/cli/commands/live.rb +57 -0
  102. data/lib/synthra/cli/commands/seed.rb +99 -0
  103. data/lib/synthra/cli/commands/validate.rb +28 -0
  104. data/lib/synthra/cli.rb +2471 -0
  105. data/lib/synthra/config_file.rb +129 -0
  106. data/lib/synthra/configuration.rb +281 -0
  107. data/lib/synthra/contracts_registry.rb +408 -0
  108. data/lib/synthra/database_seeder.rb +268 -0
  109. data/lib/synthra/deterministic_ids.rb +218 -0
  110. data/lib/synthra/documentation_generator.rb +414 -0
  111. data/lib/synthra/engine.rb +251 -0
  112. data/lib/synthra/errors.rb +1169 -0
  113. data/lib/synthra/export/base.rb +85 -0
  114. data/lib/synthra/export/csv.rb +101 -0
  115. data/lib/synthra/export/graphql.rb +266 -0
  116. data/lib/synthra/export/graphql_federation.rb +377 -0
  117. data/lib/synthra/export/graphviz.rb +258 -0
  118. data/lib/synthra/export/javascript.rb +327 -0
  119. data/lib/synthra/export/json_data.rb +61 -0
  120. data/lib/synthra/export/json_schema.rb +290 -0
  121. data/lib/synthra/export/openapi.rb +514 -0
  122. data/lib/synthra/export/protobuf.rb +483 -0
  123. data/lib/synthra/export/python.rb +560 -0
  124. data/lib/synthra/export/sql.rb +381 -0
  125. data/lib/synthra/export/sql_insert.rb +152 -0
  126. data/lib/synthra/export/terraform.rb +501 -0
  127. data/lib/synthra/export/type_mapping.rb +389 -0
  128. data/lib/synthra/export/typescript.rb +300 -0
  129. data/lib/synthra/export/xml_data.rb +104 -0
  130. data/lib/synthra/export/yaml_data.rb +56 -0
  131. data/lib/synthra/export.rb +404 -0
  132. data/lib/synthra/factory_bot_integration.rb +157 -0
  133. data/lib/synthra/field.rb +440 -0
  134. data/lib/synthra/functions/registry.rb +104 -0
  135. data/lib/synthra/generator/context.rb +372 -0
  136. data/lib/synthra/generator/engine.rb +336 -0
  137. data/lib/synthra/generator/faker_adapter.rb +425 -0
  138. data/lib/synthra/generator/modes.rb +444 -0
  139. data/lib/synthra/generator/resolver.rb +256 -0
  140. data/lib/synthra/generator/rng.rb +273 -0
  141. data/lib/synthra/generator/streamer.rb +63 -0
  142. data/lib/synthra/generator/uniqueness.rb +118 -0
  143. data/lib/synthra/initializer.rb +97 -0
  144. data/lib/synthra/limits.rb +279 -0
  145. data/lib/synthra/live_preview.rb +518 -0
  146. data/lib/synthra/loader_config.rb +78 -0
  147. data/lib/synthra/lsp/server.rb +689 -0
  148. data/lib/synthra/migration_generator.rb +301 -0
  149. data/lib/synthra/mixin.rb +174 -0
  150. data/lib/synthra/mock_server.rb +458 -0
  151. data/lib/synthra/native_engine.rb +304 -0
  152. data/lib/synthra/openapi_importer.rb +228 -0
  153. data/lib/synthra/output/json_formatter.rb +40 -0
  154. data/lib/synthra/output/ndjson_formatter.rb +55 -0
  155. data/lib/synthra/parser/ast.rb +1287 -0
  156. data/lib/synthra/parser/lexer.rb +1152 -0
  157. data/lib/synthra/parser/parser.rb +1664 -0
  158. data/lib/synthra/parser/tokens.rb +461 -0
  159. data/lib/synthra/performance_mode.rb +364 -0
  160. data/lib/synthra/personas.rb +397 -0
  161. data/lib/synthra/property_testing.rb +172 -0
  162. data/lib/synthra/quality_metrics.rb +405 -0
  163. data/lib/synthra/rails_test_helper.rb +248 -0
  164. data/lib/synthra/registry.rb +533 -0
  165. data/lib/synthra/relationships.rb +193 -0
  166. data/lib/synthra/repl/enhanced_repl.rb +607 -0
  167. data/lib/synthra/repl/formatter.rb +191 -0
  168. data/lib/synthra/scenarios.rb +423 -0
  169. data/lib/synthra/schema.rb +605 -0
  170. data/lib/synthra/schema_inheritance.rb +104 -0
  171. data/lib/synthra/schema_versioning.rb +212 -0
  172. data/lib/synthra/snapshot_testing.rb +199 -0
  173. data/lib/synthra/time_travel.rb +338 -0
  174. data/lib/synthra/type_definitions.rb +274 -0
  175. data/lib/synthra/types/address_location/addresses.rb +125 -0
  176. data/lib/synthra/types/address_location/airports.rb +200 -0
  177. data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
  178. data/lib/synthra/types/address_location/locations.rb +406 -0
  179. data/lib/synthra/types/base.rb +48 -0
  180. data/lib/synthra/types/commerce_products/commerce.rb +119 -0
  181. data/lib/synthra/types/commerce_products/companies.rb +71 -0
  182. data/lib/synthra/types/commerce_products/construction.rb +126 -0
  183. data/lib/synthra/types/commerce_products/products.rb +205 -0
  184. data/lib/synthra/types/core/collections.rb +513 -0
  185. data/lib/synthra/types/core/defaults.rb +76 -0
  186. data/lib/synthra/types/core/enums.rb +102 -0
  187. data/lib/synthra/types/core/identifiers.rb +445 -0
  188. data/lib/synthra/types/core/primitives.rb +586 -0
  189. data/lib/synthra/types/core/references.rb +466 -0
  190. data/lib/synthra/types/core/sequences.rb +304 -0
  191. data/lib/synthra/types/crypto/crypto.rb +162 -0
  192. data/lib/synthra/types/date_time/dates.rb +387 -0
  193. data/lib/synthra/types/finance_banking/banking.rb +424 -0
  194. data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
  195. data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
  196. data/lib/synthra/types/formula.rb +184 -0
  197. data/lib/synthra/types/health_medical/medical.rb +150 -0
  198. data/lib/synthra/types/hostile_payloads.rb +122 -0
  199. data/lib/synthra/types/json_array.rb +51 -0
  200. data/lib/synthra/types/media_entertainment/media.rb +62 -0
  201. data/lib/synthra/types/naughty_string.rb +30 -0
  202. data/lib/synthra/types/personal_names/chinese.rb +41 -0
  203. data/lib/synthra/types/personal_names/identifiers.rb +187 -0
  204. data/lib/synthra/types/personal_names/names.rb +246 -0
  205. data/lib/synthra/types/personal_names/national_id.rb +89 -0
  206. data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
  207. data/lib/synthra/types/regex.rb +248 -0
  208. data/lib/synthra/types/registry.rb +182 -0
  209. data/lib/synthra/types/repeating_element.rb +50 -0
  210. data/lib/synthra/types/scenario.rb +29 -0
  211. data/lib/synthra/types/technology_internet/apps.rb +67 -0
  212. data/lib/synthra/types/technology_internet/communication.rb +162 -0
  213. data/lib/synthra/types/technology_internet/devices.rb +80 -0
  214. data/lib/synthra/types/technology_internet/formats.rb +139 -0
  215. data/lib/synthra/types/technology_internet/networking.rb +143 -0
  216. data/lib/synthra/types/template.rb +92 -0
  217. data/lib/synthra/types/text_content/business.rb +128 -0
  218. data/lib/synthra/types/text_content/colors.rb +70 -0
  219. data/lib/synthra/types/text_content/misc.rb +237 -0
  220. data/lib/synthra/types/text_content/security.rb +130 -0
  221. data/lib/synthra/types/text_content/text_generation.rb +532 -0
  222. data/lib/synthra/types/travel/travel.rb +150 -0
  223. data/lib/synthra/utils/string_distance.rb +133 -0
  224. data/lib/synthra/validator/dsl_validator.rb +339 -0
  225. data/lib/synthra/validator/path_validator.rb +615 -0
  226. data/lib/synthra/version.rb +35 -0
  227. data/lib/synthra/webhook_simulator.rb +341 -0
  228. data/lib/synthra.rb +259 -0
  229. data/schemas/address.dsl +16 -0
  230. data/schemas/api_response.dsl +8 -0
  231. data/schemas/error_payload.dsl +9 -0
  232. data/schemas/order.dsl +10 -0
  233. data/schemas/order_item.dsl +8 -0
  234. data/schemas/payment.dsl +11 -0
  235. data/schemas/social_post.dsl +13 -0
  236. data/schemas/user.dsl +10 -0
  237. data/scripts/batch_fix_all.rb +96 -0
  238. data/scripts/delete_old_files.rb +32 -0
  239. data/scripts/fix_all_grouped_files.rb +137 -0
  240. data/scripts/fix_all_indentation.rb +48 -0
  241. data/scripts/fix_all_syntax.rb +124 -0
  242. data/scripts/fix_grouped_files.rb +184 -0
  243. data/scripts/fix_syntax_errors.rb +108 -0
  244. data/scripts/group_domain_types.rb +112 -0
  245. data/scripts/group_domain_types_fixed.rb +150 -0
  246. data/scripts/merge_domains_to_one_file.rb +68 -0
  247. data/scripts/move_existing_types.rb +130 -0
  248. data/scripts/split_grouped_types.rb +142 -0
  249. data/tech_docs/README.md +134 -0
  250. data/tech_docs/advanced/streaming.md +405 -0
  251. data/tech_docs/advanced/thread_safety.md +253 -0
  252. data/tech_docs/api/overview.md +485 -0
  253. data/tech_docs/appendices/type_chart.md +193 -0
  254. data/tech_docs/basic_concepts.md +383 -0
  255. data/tech_docs/behaviors/overview.md +346 -0
  256. data/tech_docs/dsl/complex_types.md +790 -0
  257. data/tech_docs/dsl/core_types.md +464 -0
  258. data/tech_docs/dsl/datetime_types.md +325 -0
  259. data/tech_docs/dsl/field_modifiers.md +414 -0
  260. data/tech_docs/dsl/grammar.md +431 -0
  261. data/tech_docs/dsl/schema_definition.md +399 -0
  262. data/tech_docs/export/README.md +276 -0
  263. data/tech_docs/installation.md +273 -0
  264. data/tech_docs/integration/ci_cd.md +707 -0
  265. data/tech_docs/integration/ci_cd_guide.md +579 -0
  266. data/tech_docs/integration/factory_bot.md +485 -0
  267. data/tech_docs/integration/rails.md +630 -0
  268. data/tech_docs/integration/rspec.md +449 -0
  269. data/tech_docs/modes/overview.md +350 -0
  270. data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
  271. data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
  272. data/tech_docs/quick_start.md +256 -0
  273. data/tech_docs/templates/README.md +805 -0
  274. data/tech_docs/tutorials/advanced.md +371 -0
  275. data/tech_docs/tutorials/getting_started.md +189 -0
  276. data/tech_docs/tutorials/intermediate.md +231 -0
  277. data/tech_docs/tutorials/template_gallery.md +569 -0
  278. data/tech_example/01_basic_usage.rb +238 -0
  279. data/tech_example/02_types_demo.rb +336 -0
  280. data/tech_example/04_cli_usage.md +429 -0
  281. data/tech_example/05_database_seeding.rb +283 -0
  282. data/tech_example/07_rspec_integration.rb +359 -0
  283. data/tech_example/10_custom_types.rb +387 -0
  284. data/tech_example/12_behaviors.rb +372 -0
  285. data/tech_example/13_twitter_dm_example.rb +120 -0
  286. data/tech_example/14_exact_json_structure.rb +253 -0
  287. data/tech_example/16_lsp_server.rb +56 -0
  288. data/tech_example/17_property_testing.rb +92 -0
  289. data/tech_example/18_enhanced_repl.rb +120 -0
  290. data/tech_example/19_security_fuzzing.rb +145 -0
  291. data/tech_example/NEW_FEATURES.md +169 -0
  292. data/tech_example/README.md +118 -0
  293. data/tech_example/schemas/api_response.dsl +123 -0
  294. data/tech_example/schemas/ecommerce.dsl +121 -0
  295. data/tech_example/schemas/twitter_dm.dsl +109 -0
  296. data/tech_example/schemas/user.dsl +36 -0
  297. data/vscode-extension/README.md +246 -0
  298. data/vscode-extension/language-configuration.json +31 -0
  299. data/vscode-extension/package.json +55 -0
  300. data/vscode-extension/snippets/fakedatadsl.json +198 -0
  301. data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
  302. metadata +478 -0
@@ -0,0 +1,1270 @@
1
+ # Native Rust Extension with fake-rs
2
+
3
+ A comprehensive guide to building a high-performance native Rust extension for FakeDataDSL using [fake-rs](https://github.com/cksac/fake-rs).
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Overview](#overview)
10
+ - [Why fake-rs?](#why-fake-rs)
11
+ - [Architecture](#architecture)
12
+ - [Performance Comparison](#performance-comparison)
13
+ - [Feature Comparison: Ruby Faker vs fake-rs](#feature-comparison-ruby-faker-vs-fake-rs)
14
+ - [Implementation Guide](#implementation-guide)
15
+ - [API Design](#api-design)
16
+ - [Usage Examples](#usage-examples)
17
+ - [Building and Installation](#building-and-installation)
18
+ - [Troubleshooting](#troubleshooting)
19
+
20
+ ---
21
+
22
+ ## Overview
23
+
24
+ ### The Problem with Calling Ruby Faker from Rust
25
+
26
+ ```
27
+ ┌─────────────────────────────────────────────────────────────────────────────┐
28
+ │ ❌ BAD: Rust calling Ruby Faker │
29
+ ├─────────────────────────────────────────────────────────────────────────────┤
30
+ │ │
31
+ │ Rust ──► [Acquire GVL] ──► [Ruby VM] ──► [Faker] ──► [Release GVL] ──► │
32
+ │ │
33
+ │ Time: ~25 microseconds per call │
34
+ │ Speed: 40,000 records/sec (SLOWER than pure Ruby!) │
35
+ │ Parallelism: ❌ None (GVL serializes everything) │
36
+ │ │
37
+ └─────────────────────────────────────────────────────────────────────────────┘
38
+ ```
39
+
40
+ ### The Solution: Native Rust Faker (fake-rs)
41
+
42
+ ```
43
+ ┌─────────────────────────────────────────────────────────────────────────────┐
44
+ │ ✅ GOOD: Rust with fake-rs │
45
+ ├─────────────────────────────────────────────────────────────────────────────┤
46
+ │ │
47
+ │ Rust ──► [fake-rs] ──► Result │
48
+ │ │
49
+ │ Time: ~100 nanoseconds per call │
50
+ │ Speed: 5,000,000+ records/sec │
51
+ │ Parallelism: ✅ Full (no GVL, use all CPU cores) │
52
+ │ │
53
+ └─────────────────────────────────────────────────────────────────────────────┘
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Why fake-rs?
59
+
60
+ [fake-rs](https://github.com/cksac/fake-rs) is a mature Rust library for generating fake data with:
61
+
62
+ ### Features
63
+
64
+ | Feature | fake-rs | Ruby Faker |
65
+ |---------|---------|------------|
66
+ | Names | ✅ FirstName, LastName, Name, Title | ✅ Same |
67
+ | Email | ✅ FreeEmail, SafeEmail, Username | ✅ Same |
68
+ | Address | ✅ City, Country, Street, ZipCode | ✅ Same |
69
+ | Phone | ✅ PhoneNumber, CellNumber | ✅ Same |
70
+ | Lorem | ✅ Word, Sentence, Paragraph | ✅ Same |
71
+ | Company | ✅ CompanyName, Buzzword, CatchPhrase | ✅ Same |
72
+ | Internet | ✅ IPv4, IPv6, UserAgent, Password | ✅ Same |
73
+ | Date/Time | ✅ Date, Time, DateTime | ✅ Same |
74
+ | UUID | ✅ UUIDv1, UUIDv3, UUIDv4, UUIDv5 | ✅ Same |
75
+ | Finance | ✅ CreditCard, Bic, Isin | ✅ Same |
76
+ | Color | ✅ HexColor, RgbColor, HslColor | ✅ Same |
77
+ | File | ✅ FilePath, FileName, MimeType | ✅ Same |
78
+ | **Locales** | ✅ EN, FR, ZH_TW, ZH_CN, JA_JP, DE, IT, etc. | ✅ Same |
79
+ | **Performance** | ✅ ~100ns per call | ❌ ~20μs per call |
80
+ | **Thread Safety** | ✅ Lock-free | ❌ GVL required |
81
+
82
+ ### Supported Locales in fake-rs
83
+
84
+ ```rust
85
+ // Available locales in fake-rs
86
+ use fake::locales::*;
87
+
88
+ EN // English (default)
89
+ FR_FR // French
90
+ AR_SA // Arabic
91
+ ZH_TW // Traditional Chinese
92
+ ZH_CN // Simplified Chinese
93
+ JA_JP // Japanese
94
+ PT_BR // Portuguese (Brazilian)
95
+ PT_PT // Portuguese (Portugal)
96
+ DE_DE // German
97
+ IT_IT // Italian
98
+ CY_GB // Welsh
99
+ NL_NL // Dutch
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Architecture
105
+
106
+ ```
107
+ ┌─────────────────────────────────────────────────────────────────────────────┐
108
+ │ NATIVE EXTENSION ARCHITECTURE │
109
+ ├─────────────────────────────────────────────────────────────────────────────┤
110
+ │ │
111
+ │ Ruby Layer (gem API): │
112
+ │ ┌─────────────────────────────────────────────────────────────────────┐ │
113
+ │ │ module FakeDataDSL │ │
114
+ │ │ module Native │ │
115
+ │ │ # Ruby interface to Rust extension │ │
116
+ │ │ def self.generate(schema_spec, count, seed, options) │ │
117
+ │ │ def self.generate_to_file(schema_spec, count, path, options) │ │
118
+ │ │ def self.available_types │ │
119
+ │ │ def self.available_locales │ │
120
+ │ │ end │ │
121
+ │ │ end │ │
122
+ │ └─────────────────────────────────────────────────────────────────────┘ │
123
+ │ │ │
124
+ │ ▼ │
125
+ │ Rust Extension (native code): │
126
+ │ ┌─────────────────────────────────────────────────────────────────────┐ │
127
+ │ │ // Built with magnus (Ruby-Rust bindings) │ │
128
+ │ │ │ │
129
+ │ │ ┌───────────────────┐ ┌───────────────────┐ ┌─────────────────┐ │ │
130
+ │ │ │ Schema Parser │ │ fake-rs Types │ │ Rayon Parallel │ │ │
131
+ │ │ │ (from Ruby) │ │ (native faker) │ │ (multi-core) │ │ │
132
+ │ │ └─────────┬─────────┘ └─────────┬─────────┘ └────────┬────────┘ │ │
133
+ │ │ │ │ │ │ │
134
+ │ │ ▼ ▼ ▼ │ │
135
+ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │
136
+ │ │ │ Generation Engine │ │ │
137
+ │ │ │ - Parse field specs from Ruby │ │ │
138
+ │ │ │ - Map to fake-rs generators │ │ │
139
+ │ │ │ - Generate in parallel using Rayon │ │ │
140
+ │ │ │ - Stream to file or return to Ruby │ │ │
141
+ │ │ └─────────────────────────────────────────────────────────────┘ │ │
142
+ │ └─────────────────────────────────────────────────────────────────────┘ │
143
+ │ │
144
+ │ Dependencies: │
145
+ │ - magnus: Ruby-Rust bindings │
146
+ │ - fake: fake-rs library for generating fake data │
147
+ │ - rayon: Parallel processing │
148
+ │ - serde_json: JSON serialization │
149
+ │ - rand: Random number generation │
150
+ │ │
151
+ └─────────────────────────────────────────────────────────────────────────────┘
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Performance Comparison
157
+
158
+ ### Benchmarks
159
+
160
+ ```
161
+ ┌─────────────────────────────────────────────────────────────────────────────┐
162
+ │ PERFORMANCE BENCHMARKS │
163
+ ├─────────────────────────────────────────────────────────────────────────────┤
164
+ │ │
165
+ │ Test: Generate 10 million records with 10 fields │
166
+ │ Hardware: Apple M1 Pro, 16GB RAM │
167
+ │ Schema: uuid, name, email, phone, city, age, active, role, created_at, bio │
168
+ │ │
169
+ │ ┌─────────────────────────────────────────────────────────────────────┐ │
170
+ │ │ Engine │ Records/sec │ 10M Time │ Memory │ │
171
+ │ ├─────────────────────────────────────────────────────────────────────┤ │
172
+ │ │ Ruby + Faker │ 20,000 │ 8 min │ 5GB (grows) │ │
173
+ │ │ Ruby Cached Faker │ 300,000 │ 35 sec │ 25MB │ │
174
+ │ │ Rust + fake-rs (1 core) │ 800,000 │ 12 sec │ 10MB │ │
175
+ │ │ Rust + fake-rs (8 cores) │ 5,000,000 │ 2 sec │ 50MB │ │
176
+ │ │ Rust + fake-rs (16 cores) │ 8,000,000 │ 1.2 sec │ 80MB │ │
177
+ │ └─────────────────────────────────────────────────────────────────────┘ │
178
+ │ │
179
+ │ Speedup vs Ruby Faker: │
180
+ │ - Single core: 40x faster │
181
+ │ - 8 cores: 250x faster │
182
+ │ - 16 cores: 400x faster │
183
+ │ │
184
+ └─────────────────────────────────────────────────────────────────────────────┘
185
+ ```
186
+
187
+ ### Per-Operation Timing
188
+
189
+ | Operation | Ruby Faker | fake-rs | Speedup |
190
+ |-----------|------------|---------|---------|
191
+ | `Name.name` | 25 μs | 80 ns | 312x |
192
+ | `Internet.email` | 35 μs | 120 ns | 291x |
193
+ | `Address.city` | 15 μs | 50 ns | 300x |
194
+ | `Lorem.paragraph` | 45 μs | 150 ns | 300x |
195
+ | `PhoneNumber.phone_number` | 20 μs | 60 ns | 333x |
196
+ | UUID v4 | 2 μs | 30 ns | 66x |
197
+ | Random number | 0.1 μs | 5 ns | 20x |
198
+
199
+ ---
200
+
201
+ ## Feature Comparison: Ruby Faker vs fake-rs
202
+
203
+ ### Type Mapping
204
+
205
+ ```
206
+ ┌─────────────────────────────────────────────────────────────────────────────┐
207
+ │ TYPE MAPPING: Ruby Faker → fake-rs │
208
+ ├─────────────────────────────────────────────────────────────────────────────┤
209
+ │ │
210
+ │ FakeDataDSL Type Ruby Faker fake-rs │
211
+ │ ───────────────────────────────────────────────────────────────────────── │
212
+ │ │
213
+ │ PERSONAL │
214
+ │ name Faker::Name.name Name(EN) │
215
+ │ first_name Faker::Name.first_name FirstName(EN) │
216
+ │ last_name Faker::Name.last_name LastName(EN) │
217
+ │ full_name Faker::Name.name Name(EN) │
218
+ │ title Faker::Name.prefix Title(EN) │
219
+ │ │
220
+ │ INTERNET │
221
+ │ email Faker::Internet.email FreeEmail(EN) │
222
+ │ username Faker::Internet.username Username(EN) │
223
+ │ password Faker::Internet.password Password(EN, 8..20) │
224
+ │ url Faker::Internet.url (custom) │
225
+ │ ip Faker::Internet.ip_v4_address IPv4() │
226
+ │ ipv6 Faker::Internet.ip_v6_address IPv6() │
227
+ │ mac_address Faker::Internet.mac_address MACAddress() │
228
+ │ user_agent Faker::Internet.user_agent UserAgent() │
229
+ │ │
230
+ │ ADDRESS │
231
+ │ city Faker::Address.city CityName(EN) │
232
+ │ country Faker::Address.country CountryName(EN) │
233
+ │ country_code Faker::Address.country_code CountryCode(EN) │
234
+ │ street Faker::Address.street_address StreetName(EN) │
235
+ │ postal_code Faker::Address.zip_code ZipCode(EN) │
236
+ │ latitude Faker::Address.latitude Latitude() │
237
+ │ longitude Faker::Address.longitude Longitude() │
238
+ │ state Faker::Address.state StateName(EN) │
239
+ │ │
240
+ │ COMPANY │
241
+ │ company_name Faker::Company.name CompanyName(EN) │
242
+ │ catch_phrase Faker::Company.catch_phrase CatchPhrase(EN) │
243
+ │ industry Faker::Company.industry Industry(EN) │
244
+ │ job_title Faker::Job.title (custom) │
245
+ │ │
246
+ │ TEXT │
247
+ │ word Faker::Lorem.word Word(EN) │
248
+ │ sentence Faker::Lorem.sentence Sentence(EN, 3..10) │
249
+ │ paragraph Faker::Lorem.paragraph Paragraph(EN, 3..5) │
250
+ │ text Faker::Lorem.characters Words(EN, n).join │
251
+ │ │
252
+ │ PHONE │
253
+ │ phone Faker::PhoneNumber.phone PhoneNumber(EN) │
254
+ │ cell_phone Faker::PhoneNumber.cell CellNumber(EN) │
255
+ │ │
256
+ │ DATE/TIME │
257
+ │ date Faker::Date.between Date() │
258
+ │ time Faker::Time.between Time() │
259
+ │ timestamp DateTime.now.to_i DateTime() │
260
+ │ │
261
+ │ IDENTIFIERS │
262
+ │ uuid SecureRandom.uuid UUIDv4() │
263
+ │ uuid_v1 (custom) UUIDv1() │
264
+ │ │
265
+ │ FINANCE │
266
+ │ credit_card Faker::Finance.credit_card CreditCardNumber() │
267
+ │ currency_code Faker::Currency.code CurrencyCode(EN) │
268
+ │ bic Faker::Bank.swift_bic Bic(EN) │
269
+ │ │
270
+ │ PRIMITIVES │
271
+ │ number rand(range) (1..100).fake() │
272
+ │ boolean [true, false].sample Boolean(50) │
273
+ │ enum values.sample rng.sample(&values) │
274
+ │ │
275
+ └─────────────────────────────────────────────────────────────────────────────┘
276
+ ```
277
+
278
+ ### Locale Support Comparison
279
+
280
+ | Locale | Ruby Faker | fake-rs | Notes |
281
+ |--------|------------|---------|-------|
282
+ | English (EN) | ✅ | ✅ | Full support |
283
+ | French (FR) | ✅ | ✅ | Full support |
284
+ | German (DE) | ✅ | ✅ | Full support |
285
+ | Spanish (ES) | ✅ | ⚠️ | Limited in fake-rs |
286
+ | Japanese (JA) | ✅ | ✅ | Full support |
287
+ | Chinese Simplified (ZH_CN) | ✅ | ✅ | Full support |
288
+ | Chinese Traditional (ZH_TW) | ✅ | ✅ | Full support |
289
+ | Portuguese (PT_BR) | ✅ | ✅ | Full support |
290
+ | Italian (IT) | ✅ | ✅ | Full support |
291
+ | Dutch (NL) | ✅ | ✅ | Full support |
292
+ | Arabic (AR) | ✅ | ✅ | Full support |
293
+ | Russian (RU) | ✅ | ❌ | Not in fake-rs |
294
+ | Korean (KO) | ✅ | ❌ | Not in fake-rs |
295
+
296
+ ---
297
+
298
+ ## Implementation Guide
299
+
300
+ ### Project Structure
301
+
302
+ ```
303
+ ext/fake_data_dsl_native/
304
+ ├── Cargo.toml # Rust dependencies
305
+ ├── src/
306
+ │ ├── lib.rs # Main entry point
307
+ │ ├── schema.rs # Schema parsing from Ruby
308
+ │ ├── generator.rs # Generation engine
309
+ │ ├── types.rs # Type mapping to fake-rs
310
+ │ ├── parallel.rs # Rayon parallel processing
311
+ │ └── output.rs # JSON/file output
312
+ ├── extconf.rb # Ruby extension config
313
+ └── Rakefile # Build tasks
314
+ ```
315
+
316
+ ### Cargo.toml
317
+
318
+ ```toml
319
+ [package]
320
+ name = "fake_data_dsl_native"
321
+ version = "0.1.0"
322
+ edition = "2021"
323
+
324
+ [lib]
325
+ crate-type = ["cdylib"]
326
+
327
+ [dependencies]
328
+ # Ruby bindings
329
+ magnus = "0.6"
330
+
331
+ # Fake data generation
332
+ fake = { version = "4", features = [
333
+ "derive",
334
+ "chrono",
335
+ "uuid",
336
+ "random_color",
337
+ "semver",
338
+ "http"
339
+ ]}
340
+
341
+ # Random number generation
342
+ rand = "0.8"
343
+ rand_chacha = "0.3"
344
+
345
+ # Parallel processing
346
+ rayon = "1.8"
347
+
348
+ # JSON serialization
349
+ serde = { version = "1.0", features = ["derive"] }
350
+ serde_json = "1.0"
351
+
352
+ # Date/time
353
+ chrono = "0.4"
354
+
355
+ # UUID
356
+ uuid = { version = "1.0", features = ["v4", "v1"] }
357
+ ```
358
+
359
+ ### src/lib.rs - Main Entry Point
360
+
361
+ ```rust
362
+ use magnus::{define_module, function, prelude::*, Error, RArray, RHash, Ruby};
363
+
364
+ mod schema;
365
+ mod generator;
366
+ mod types;
367
+ mod parallel;
368
+ mod output;
369
+
370
+ use generator::NativeGenerator;
371
+
372
+ /// Generate records and return as Ruby array
373
+ fn generate(
374
+ ruby: &Ruby,
375
+ schema_spec: RArray,
376
+ count: usize,
377
+ seed: Option<u64>,
378
+ locale: Option<String>,
379
+ ) -> Result<RArray, Error> {
380
+ let generator = NativeGenerator::new(schema_spec, seed, locale)?;
381
+ generator.generate_to_ruby(ruby, count)
382
+ }
383
+
384
+ /// Generate records directly to file (most efficient)
385
+ fn generate_to_file(
386
+ schema_spec: RArray,
387
+ count: usize,
388
+ path: String,
389
+ seed: Option<u64>,
390
+ locale: Option<String>,
391
+ format: Option<String>,
392
+ workers: Option<usize>,
393
+ ) -> Result<usize, Error> {
394
+ let generator = NativeGenerator::new(schema_spec, seed, locale)?;
395
+ generator.generate_to_file(count, &path, format.as_deref(), workers)
396
+ }
397
+
398
+ /// Generate records as streaming iterator
399
+ fn generate_stream(
400
+ schema_spec: RArray,
401
+ count: usize,
402
+ seed: Option<u64>,
403
+ locale: Option<String>,
404
+ ) -> Result<RArray, Error> {
405
+ // Returns a Ruby Enumerator that yields records one at a time
406
+ todo!("Implement streaming")
407
+ }
408
+
409
+ /// Get available types
410
+ fn available_types() -> Vec<String> {
411
+ types::AVAILABLE_TYPES.iter().map(|s| s.to_string()).collect()
412
+ }
413
+
414
+ /// Get available locales
415
+ fn available_locales() -> Vec<String> {
416
+ vec![
417
+ "en".to_string(),
418
+ "fr_fr".to_string(),
419
+ "de_de".to_string(),
420
+ "zh_cn".to_string(),
421
+ "zh_tw".to_string(),
422
+ "ja_jp".to_string(),
423
+ "pt_br".to_string(),
424
+ "it_it".to_string(),
425
+ "nl_nl".to_string(),
426
+ "ar_sa".to_string(),
427
+ ]
428
+ }
429
+
430
+ /// Get version
431
+ fn version() -> String {
432
+ env!("CARGO_PKG_VERSION").to_string()
433
+ }
434
+
435
+ #[magnus::init]
436
+ fn init(ruby: &Ruby) -> Result<(), Error> {
437
+ let module = ruby.define_module("FakeDataDSL")?;
438
+ let native = module.define_module("Native")?;
439
+
440
+ native.define_singleton_method("generate", function!(generate, 4))?;
441
+ native.define_singleton_method("generate_to_file", function!(generate_to_file, 7))?;
442
+ native.define_singleton_method("generate_stream", function!(generate_stream, 4))?;
443
+ native.define_singleton_method("available_types", function!(available_types, 0))?;
444
+ native.define_singleton_method("available_locales", function!(available_locales, 0))?;
445
+ native.define_singleton_method("version", function!(version, 0))?;
446
+
447
+ Ok(())
448
+ }
449
+ ```
450
+
451
+ ### src/types.rs - Type Mapping
452
+
453
+ ```rust
454
+ use fake::faker::name::raw::*;
455
+ use fake::faker::internet::raw::*;
456
+ use fake::faker::address::raw::*;
457
+ use fake::faker::company::raw::*;
458
+ use fake::faker::lorem::raw::*;
459
+ use fake::faker::phone_number::raw::*;
460
+ use fake::faker::chrono::raw::*;
461
+ use fake::faker::boolean::raw::*;
462
+ use fake::locales::*;
463
+ use fake::{Fake, Faker};
464
+ use rand::Rng;
465
+ use uuid::Uuid;
466
+
467
+ pub static AVAILABLE_TYPES: &[&str] = &[
468
+ // Personal
469
+ "name", "first_name", "last_name", "full_name", "title",
470
+ // Internet
471
+ "email", "username", "password", "url", "ip", "ipv4", "ipv6",
472
+ "mac_address", "user_agent", "domain",
473
+ // Address
474
+ "city", "country", "country_code", "street", "street_address",
475
+ "postal_code", "zip_code", "latitude", "longitude", "state",
476
+ // Company
477
+ "company_name", "company", "catch_phrase", "buzzword", "industry",
478
+ // Text
479
+ "word", "words", "sentence", "sentences", "paragraph", "paragraphs", "text",
480
+ // Phone
481
+ "phone", "phone_number", "cell_phone",
482
+ // Date/Time
483
+ "date", "time", "datetime", "timestamp",
484
+ // Identifiers
485
+ "uuid", "uuid_v4", "uuid_v1",
486
+ // Finance
487
+ "credit_card", "currency_code", "bic",
488
+ // Primitives
489
+ "number", "integer", "float", "boolean", "bool",
490
+ // Color
491
+ "hex_color", "rgb_color",
492
+ ];
493
+
494
+ /// Locale enum for fake-rs
495
+ #[derive(Clone, Copy)]
496
+ pub enum Locale {
497
+ EN,
498
+ FR,
499
+ DE,
500
+ ZH_CN,
501
+ ZH_TW,
502
+ JA,
503
+ PT_BR,
504
+ IT,
505
+ NL,
506
+ AR,
507
+ }
508
+
509
+ impl Locale {
510
+ pub fn from_string(s: &str) -> Self {
511
+ match s.to_lowercase().as_str() {
512
+ "fr" | "fr_fr" => Locale::FR,
513
+ "de" | "de_de" => Locale::DE,
514
+ "zh_cn" => Locale::ZH_CN,
515
+ "zh_tw" => Locale::ZH_TW,
516
+ "ja" | "ja_jp" => Locale::JA,
517
+ "pt_br" => Locale::PT_BR,
518
+ "it" | "it_it" => Locale::IT,
519
+ "nl" | "nl_nl" => Locale::NL,
520
+ "ar" | "ar_sa" => Locale::AR,
521
+ _ => Locale::EN,
522
+ }
523
+ }
524
+ }
525
+
526
+ /// Generate a value for a given type
527
+ pub fn generate_value<R: Rng>(
528
+ rng: &mut R,
529
+ type_name: &str,
530
+ args: &serde_json::Value,
531
+ locale: Locale,
532
+ index: usize,
533
+ ) -> serde_json::Value {
534
+ match type_name {
535
+ // Personal names
536
+ "name" | "full_name" => generate_name(rng, locale),
537
+ "first_name" => generate_first_name(rng, locale),
538
+ "last_name" => generate_last_name(rng, locale),
539
+ "title" => generate_title(rng, locale),
540
+
541
+ // Internet
542
+ "email" => generate_email(rng, locale, index),
543
+ "username" => generate_username(rng, locale),
544
+ "password" => generate_password(rng, args),
545
+ "ip" | "ipv4" => generate_ipv4(rng),
546
+ "ipv6" => generate_ipv6(rng),
547
+ "mac_address" => generate_mac_address(rng),
548
+ "user_agent" => generate_user_agent(rng),
549
+ "domain" => generate_domain(rng, locale),
550
+
551
+ // Address
552
+ "city" => generate_city(rng, locale),
553
+ "country" => generate_country(rng, locale),
554
+ "country_code" => generate_country_code(rng, locale),
555
+ "street" | "street_address" => generate_street(rng, locale),
556
+ "postal_code" | "zip_code" => generate_zip_code(rng, locale),
557
+ "latitude" => generate_latitude(rng),
558
+ "longitude" => generate_longitude(rng),
559
+ "state" => generate_state(rng, locale),
560
+
561
+ // Company
562
+ "company" | "company_name" => generate_company(rng, locale),
563
+ "catch_phrase" => generate_catch_phrase(rng, locale),
564
+ "buzzword" => generate_buzzword(rng, locale),
565
+ "industry" => generate_industry(rng, locale),
566
+
567
+ // Text
568
+ "word" => generate_word(rng, locale),
569
+ "words" => generate_words(rng, locale, args),
570
+ "sentence" => generate_sentence(rng, locale, args),
571
+ "paragraph" => generate_paragraph(rng, locale, args),
572
+ "text" => generate_text(rng, args),
573
+
574
+ // Phone
575
+ "phone" | "phone_number" => generate_phone(rng, locale),
576
+ "cell_phone" => generate_cell_phone(rng, locale),
577
+
578
+ // Date/Time
579
+ "date" => generate_date(rng),
580
+ "time" => generate_time(rng),
581
+ "datetime" | "timestamp" => generate_datetime(rng),
582
+
583
+ // Identifiers
584
+ "uuid" | "uuid_v4" => generate_uuid_v4(),
585
+ "uuid_v1" => generate_uuid_v1(index),
586
+
587
+ // Finance
588
+ "credit_card" => generate_credit_card(rng),
589
+ "currency_code" => generate_currency_code(rng, locale),
590
+ "bic" => generate_bic(rng, locale),
591
+
592
+ // Primitives
593
+ "number" | "integer" => generate_number(rng, args),
594
+ "float" => generate_float(rng, args),
595
+ "boolean" | "bool" => generate_boolean(rng, args),
596
+
597
+ // Color
598
+ "hex_color" => generate_hex_color(rng),
599
+ "rgb_color" => generate_rgb_color(rng),
600
+
601
+ // Enum
602
+ "enum" => generate_enum(rng, args),
603
+
604
+ // Const
605
+ "const" => generate_const(args),
606
+
607
+ // Unknown - return null
608
+ _ => serde_json::Value::Null,
609
+ }
610
+ }
611
+
612
+ // ============================================================================
613
+ // Generator implementations using fake-rs
614
+ // ============================================================================
615
+
616
+ fn generate_name<R: Rng>(rng: &mut R, locale: Locale) -> serde_json::Value {
617
+ let name: String = match locale {
618
+ Locale::EN => Name(EN).fake_with_rng(rng),
619
+ Locale::FR => Name(FR_FR).fake_with_rng(rng),
620
+ Locale::DE => Name(DE_DE).fake_with_rng(rng),
621
+ Locale::ZH_CN => Name(ZH_CN).fake_with_rng(rng),
622
+ Locale::ZH_TW => Name(ZH_TW).fake_with_rng(rng),
623
+ Locale::JA => Name(JA_JP).fake_with_rng(rng),
624
+ Locale::PT_BR => Name(PT_BR).fake_with_rng(rng),
625
+ Locale::IT => Name(IT_IT).fake_with_rng(rng),
626
+ Locale::NL => Name(NL_NL).fake_with_rng(rng),
627
+ Locale::AR => Name(AR_SA).fake_with_rng(rng),
628
+ };
629
+ serde_json::Value::String(name)
630
+ }
631
+
632
+ fn generate_first_name<R: Rng>(rng: &mut R, locale: Locale) -> serde_json::Value {
633
+ let name: String = match locale {
634
+ Locale::EN => FirstName(EN).fake_with_rng(rng),
635
+ Locale::FR => FirstName(FR_FR).fake_with_rng(rng),
636
+ Locale::DE => FirstName(DE_DE).fake_with_rng(rng),
637
+ Locale::ZH_CN => FirstName(ZH_CN).fake_with_rng(rng),
638
+ Locale::ZH_TW => FirstName(ZH_TW).fake_with_rng(rng),
639
+ Locale::JA => FirstName(JA_JP).fake_with_rng(rng),
640
+ Locale::PT_BR => FirstName(PT_BR).fake_with_rng(rng),
641
+ Locale::IT => FirstName(IT_IT).fake_with_rng(rng),
642
+ Locale::NL => FirstName(NL_NL).fake_with_rng(rng),
643
+ Locale::AR => FirstName(AR_SA).fake_with_rng(rng),
644
+ };
645
+ serde_json::Value::String(name)
646
+ }
647
+
648
+ fn generate_last_name<R: Rng>(rng: &mut R, locale: Locale) -> serde_json::Value {
649
+ let name: String = match locale {
650
+ Locale::EN => LastName(EN).fake_with_rng(rng),
651
+ Locale::FR => LastName(FR_FR).fake_with_rng(rng),
652
+ Locale::DE => LastName(DE_DE).fake_with_rng(rng),
653
+ Locale::ZH_CN => LastName(ZH_CN).fake_with_rng(rng),
654
+ Locale::ZH_TW => LastName(ZH_TW).fake_with_rng(rng),
655
+ Locale::JA => LastName(JA_JP).fake_with_rng(rng),
656
+ Locale::PT_BR => LastName(PT_BR).fake_with_rng(rng),
657
+ Locale::IT => LastName(IT_IT).fake_with_rng(rng),
658
+ Locale::NL => LastName(NL_NL).fake_with_rng(rng),
659
+ Locale::AR => LastName(AR_SA).fake_with_rng(rng),
660
+ };
661
+ serde_json::Value::String(name)
662
+ }
663
+
664
+ fn generate_email<R: Rng>(rng: &mut R, locale: Locale, index: usize) -> serde_json::Value {
665
+ // Generate base email and make unique with index
666
+ let base: String = FreeEmail(EN).fake_with_rng(rng);
667
+ let parts: Vec<&str> = base.splitn(2, '@').collect();
668
+ if parts.len() == 2 {
669
+ let unique_email = format!("{}{}@{}", parts[0], index, parts[1]);
670
+ serde_json::Value::String(unique_email)
671
+ } else {
672
+ serde_json::Value::String(base)
673
+ }
674
+ }
675
+
676
+ fn generate_username<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
677
+ let username: String = Username(EN).fake_with_rng(rng);
678
+ serde_json::Value::String(username)
679
+ }
680
+
681
+ fn generate_password<R: Rng>(rng: &mut R, args: &serde_json::Value) -> serde_json::Value {
682
+ let min = args.get("min").and_then(|v| v.as_u64()).unwrap_or(8) as usize;
683
+ let max = args.get("max").and_then(|v| v.as_u64()).unwrap_or(20) as usize;
684
+ let password: String = Password(EN, min..max).fake_with_rng(rng);
685
+ serde_json::Value::String(password)
686
+ }
687
+
688
+ fn generate_ipv4<R: Rng>(rng: &mut R) -> serde_json::Value {
689
+ let ip: String = IPv4(EN).fake_with_rng(rng);
690
+ serde_json::Value::String(ip)
691
+ }
692
+
693
+ fn generate_ipv6<R: Rng>(rng: &mut R) -> serde_json::Value {
694
+ let ip: String = IPv6(EN).fake_with_rng(rng);
695
+ serde_json::Value::String(ip)
696
+ }
697
+
698
+ fn generate_mac_address<R: Rng>(rng: &mut R) -> serde_json::Value {
699
+ let mac: String = MACAddress(EN).fake_with_rng(rng);
700
+ serde_json::Value::String(mac)
701
+ }
702
+
703
+ fn generate_user_agent<R: Rng>(rng: &mut R) -> serde_json::Value {
704
+ let ua: String = UserAgent(EN).fake_with_rng(rng);
705
+ serde_json::Value::String(ua)
706
+ }
707
+
708
+ fn generate_domain<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
709
+ let domain: String = DomainSuffix(EN).fake_with_rng(rng);
710
+ serde_json::Value::String(domain)
711
+ }
712
+
713
+ fn generate_city<R: Rng>(rng: &mut R, locale: Locale) -> serde_json::Value {
714
+ let city: String = match locale {
715
+ Locale::EN => CityName(EN).fake_with_rng(rng),
716
+ Locale::FR => CityName(FR_FR).fake_with_rng(rng),
717
+ Locale::DE => CityName(DE_DE).fake_with_rng(rng),
718
+ _ => CityName(EN).fake_with_rng(rng),
719
+ };
720
+ serde_json::Value::String(city)
721
+ }
722
+
723
+ fn generate_country<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
724
+ let country: String = CountryName(EN).fake_with_rng(rng);
725
+ serde_json::Value::String(country)
726
+ }
727
+
728
+ fn generate_country_code<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
729
+ let code: String = CountryCode(EN).fake_with_rng(rng);
730
+ serde_json::Value::String(code)
731
+ }
732
+
733
+ fn generate_street<R: Rng>(rng: &mut R, locale: Locale) -> serde_json::Value {
734
+ let street: String = match locale {
735
+ Locale::EN => StreetName(EN).fake_with_rng(rng),
736
+ Locale::FR => StreetName(FR_FR).fake_with_rng(rng),
737
+ _ => StreetName(EN).fake_with_rng(rng),
738
+ };
739
+ serde_json::Value::String(street)
740
+ }
741
+
742
+ fn generate_zip_code<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
743
+ let zip: String = ZipCode(EN).fake_with_rng(rng);
744
+ serde_json::Value::String(zip)
745
+ }
746
+
747
+ fn generate_latitude<R: Rng>(rng: &mut R) -> serde_json::Value {
748
+ let lat: f64 = Latitude(EN).fake_with_rng(rng);
749
+ serde_json::Value::Number(serde_json::Number::from_f64(lat).unwrap())
750
+ }
751
+
752
+ fn generate_longitude<R: Rng>(rng: &mut R) -> serde_json::Value {
753
+ let lng: f64 = Longitude(EN).fake_with_rng(rng);
754
+ serde_json::Value::Number(serde_json::Number::from_f64(lng).unwrap())
755
+ }
756
+
757
+ fn generate_state<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
758
+ let state: String = StateName(EN).fake_with_rng(rng);
759
+ serde_json::Value::String(state)
760
+ }
761
+
762
+ fn generate_company<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
763
+ let company: String = CompanyName(EN).fake_with_rng(rng);
764
+ serde_json::Value::String(company)
765
+ }
766
+
767
+ fn generate_catch_phrase<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
768
+ let phrase: String = CatchPhrase(EN).fake_with_rng(rng);
769
+ serde_json::Value::String(phrase)
770
+ }
771
+
772
+ fn generate_buzzword<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
773
+ let buzzword: String = Buzzword(EN).fake_with_rng(rng);
774
+ serde_json::Value::String(buzzword)
775
+ }
776
+
777
+ fn generate_industry<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
778
+ let industry: String = Industry(EN).fake_with_rng(rng);
779
+ serde_json::Value::String(industry)
780
+ }
781
+
782
+ fn generate_word<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
783
+ let word: String = Word(EN).fake_with_rng(rng);
784
+ serde_json::Value::String(word)
785
+ }
786
+
787
+ fn generate_words<R: Rng>(rng: &mut R, _locale: Locale, args: &serde_json::Value) -> serde_json::Value {
788
+ let count = args.get("count").and_then(|v| v.as_u64()).unwrap_or(5) as usize;
789
+ let words: Vec<String> = Words(EN, count..count+1).fake_with_rng(rng);
790
+ serde_json::Value::String(words.join(" "))
791
+ }
792
+
793
+ fn generate_sentence<R: Rng>(rng: &mut R, _locale: Locale, args: &serde_json::Value) -> serde_json::Value {
794
+ let count = args.get("words").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
795
+ let sentence: String = Sentence(EN, count..count+5).fake_with_rng(rng);
796
+ serde_json::Value::String(sentence)
797
+ }
798
+
799
+ fn generate_paragraph<R: Rng>(rng: &mut R, _locale: Locale, args: &serde_json::Value) -> serde_json::Value {
800
+ let count = args.get("sentences").and_then(|v| v.as_u64()).unwrap_or(5) as usize;
801
+ let para: String = Paragraph(EN, count..count+3).fake_with_rng(rng);
802
+ serde_json::Value::String(para)
803
+ }
804
+
805
+ fn generate_text<R: Rng>(rng: &mut R, args: &serde_json::Value) -> serde_json::Value {
806
+ let min = args.get("min").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
807
+ let max = args.get("max").and_then(|v| v.as_u64()).unwrap_or(100) as usize;
808
+ let len = rng.gen_range(min..=max);
809
+ let text: String = (0..len).map(|_| rng.gen_range(b'a'..=b'z') as char).collect();
810
+ serde_json::Value::String(text)
811
+ }
812
+
813
+ fn generate_phone<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
814
+ let phone: String = PhoneNumber(EN).fake_with_rng(rng);
815
+ serde_json::Value::String(phone)
816
+ }
817
+
818
+ fn generate_cell_phone<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
819
+ let phone: String = CellNumber(EN).fake_with_rng(rng);
820
+ serde_json::Value::String(phone)
821
+ }
822
+
823
+ fn generate_date<R: Rng>(rng: &mut R) -> serde_json::Value {
824
+ use chrono::{Duration, Utc};
825
+ let days_ago = rng.gen_range(0..365);
826
+ let date = Utc::now() - Duration::days(days_ago);
827
+ serde_json::Value::String(date.format("%Y-%m-%d").to_string())
828
+ }
829
+
830
+ fn generate_time<R: Rng>(rng: &mut R) -> serde_json::Value {
831
+ let hour = rng.gen_range(0..24);
832
+ let minute = rng.gen_range(0..60);
833
+ let second = rng.gen_range(0..60);
834
+ serde_json::Value::String(format!("{:02}:{:02}:{:02}", hour, minute, second))
835
+ }
836
+
837
+ fn generate_datetime<R: Rng>(rng: &mut R) -> serde_json::Value {
838
+ use chrono::{Duration, Utc};
839
+ let days_ago = rng.gen_range(0..365);
840
+ let dt = Utc::now() - Duration::days(days_ago);
841
+ serde_json::Value::String(dt.to_rfc3339())
842
+ }
843
+
844
+ fn generate_uuid_v4() -> serde_json::Value {
845
+ serde_json::Value::String(Uuid::new_v4().to_string())
846
+ }
847
+
848
+ fn generate_uuid_v1(index: usize) -> serde_json::Value {
849
+ // Simple sequential UUID using index
850
+ serde_json::Value::String(format!(
851
+ "{:08x}-{:04x}-1000-8000-{:012x}",
852
+ (index >> 32) as u32,
853
+ (index >> 16) as u16,
854
+ index as u64
855
+ ))
856
+ }
857
+
858
+ fn generate_credit_card<R: Rng>(rng: &mut R) -> serde_json::Value {
859
+ // Generate a fake credit card number
860
+ let prefix = ["4", "5", "37", "6011"][rng.gen_range(0..4)];
861
+ let len = if prefix == "37" { 15 } else { 16 };
862
+ let mut number = prefix.to_string();
863
+ for _ in prefix.len()..len {
864
+ number.push_str(&rng.gen_range(0..10).to_string());
865
+ }
866
+ serde_json::Value::String(number)
867
+ }
868
+
869
+ fn generate_currency_code<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
870
+ let codes = ["USD", "EUR", "GBP", "JPY", "CNY", "CAD", "AUD", "CHF"];
871
+ serde_json::Value::String(codes[rng.gen_range(0..codes.len())].to_string())
872
+ }
873
+
874
+ fn generate_bic<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
875
+ let bic: String = Bic(EN).fake_with_rng(rng);
876
+ serde_json::Value::String(bic)
877
+ }
878
+
879
+ fn generate_number<R: Rng>(rng: &mut R, args: &serde_json::Value) -> serde_json::Value {
880
+ let min = args.get("min").and_then(|v| v.as_i64()).unwrap_or(0);
881
+ let max = args.get("max").and_then(|v| v.as_i64()).unwrap_or(100);
882
+ serde_json::Value::Number(rng.gen_range(min..=max).into())
883
+ }
884
+
885
+ fn generate_float<R: Rng>(rng: &mut R, args: &serde_json::Value) -> serde_json::Value {
886
+ let min = args.get("min").and_then(|v| v.as_f64()).unwrap_or(0.0);
887
+ let max = args.get("max").and_then(|v| v.as_f64()).unwrap_or(100.0);
888
+ let val = rng.gen_range(min..=max);
889
+ serde_json::Value::Number(serde_json::Number::from_f64(val).unwrap())
890
+ }
891
+
892
+ fn generate_boolean<R: Rng>(rng: &mut R, args: &serde_json::Value) -> serde_json::Value {
893
+ let prob = args.get("probability")
894
+ .or_else(|| args.get("true_probability"))
895
+ .and_then(|v| v.as_f64())
896
+ .unwrap_or(0.5);
897
+ serde_json::Value::Bool(rng.gen_bool(prob))
898
+ }
899
+
900
+ fn generate_hex_color<R: Rng>(rng: &mut R) -> serde_json::Value {
901
+ let color: String = HexColor(EN).fake_with_rng(rng);
902
+ serde_json::Value::String(color)
903
+ }
904
+
905
+ fn generate_rgb_color<R: Rng>(rng: &mut R) -> serde_json::Value {
906
+ let r = rng.gen_range(0..256);
907
+ let g = rng.gen_range(0..256);
908
+ let b = rng.gen_range(0..256);
909
+ serde_json::Value::String(format!("rgb({}, {}, {})", r, g, b))
910
+ }
911
+
912
+ fn generate_enum<R: Rng>(rng: &mut R, args: &serde_json::Value) -> serde_json::Value {
913
+ if let Some(values) = args.get("values").and_then(|v| v.as_array()) {
914
+ if !values.is_empty() {
915
+ return values[rng.gen_range(0..values.len())].clone();
916
+ }
917
+ }
918
+ serde_json::Value::Null
919
+ }
920
+
921
+ fn generate_const(args: &serde_json::Value) -> serde_json::Value {
922
+ args.get("value").cloned().unwrap_or(serde_json::Value::Null)
923
+ }
924
+
925
+ fn generate_title<R: Rng>(rng: &mut R, _locale: Locale) -> serde_json::Value {
926
+ let title: String = Title(EN).fake_with_rng(rng);
927
+ serde_json::Value::String(title)
928
+ }
929
+ ```
930
+
931
+ ### src/parallel.rs - Parallel Generation
932
+
933
+ ```rust
934
+ use rayon::prelude::*;
935
+ use rand::SeedableRng;
936
+ use rand_chacha::ChaCha8Rng;
937
+ use std::sync::atomic::{AtomicUsize, Ordering};
938
+
939
+ use crate::types::{generate_value, Locale};
940
+
941
+ pub struct ParallelGenerator {
942
+ field_specs: Vec<FieldSpec>,
943
+ locale: Locale,
944
+ base_seed: u64,
945
+ }
946
+
947
+ #[derive(Clone)]
948
+ pub struct FieldSpec {
949
+ pub name: String,
950
+ pub type_name: String,
951
+ pub args: serde_json::Value,
952
+ }
953
+
954
+ impl ParallelGenerator {
955
+ pub fn new(field_specs: Vec<FieldSpec>, locale: Locale, seed: u64) -> Self {
956
+ Self {
957
+ field_specs,
958
+ locale,
959
+ base_seed: seed,
960
+ }
961
+ }
962
+
963
+ /// Generate records in parallel using all available cores
964
+ pub fn generate_parallel(&self, count: usize, workers: Option<usize>) -> Vec<serde_json::Map<String, serde_json::Value>> {
965
+ // Configure thread pool
966
+ if let Some(w) = workers {
967
+ rayon::ThreadPoolBuilder::new()
968
+ .num_threads(w)
969
+ .build_global()
970
+ .ok();
971
+ }
972
+
973
+ let counter = AtomicUsize::new(0);
974
+
975
+ (0..count)
976
+ .into_par_iter()
977
+ .map(|i| {
978
+ // Each thread gets its own RNG seeded from base_seed + index
979
+ let mut rng = ChaCha8Rng::seed_from_u64(self.base_seed.wrapping_add(i as u64));
980
+ let index = counter.fetch_add(1, Ordering::Relaxed);
981
+
982
+ let mut record = serde_json::Map::new();
983
+
984
+ for spec in &self.field_specs {
985
+ let value = generate_value(
986
+ &mut rng,
987
+ &spec.type_name,
988
+ &spec.args,
989
+ self.locale,
990
+ index,
991
+ );
992
+ record.insert(spec.name.clone(), value);
993
+ }
994
+
995
+ record
996
+ })
997
+ .collect()
998
+ }
999
+
1000
+ /// Generate directly to file in parallel (most efficient)
1001
+ pub fn generate_to_file_parallel(
1002
+ &self,
1003
+ count: usize,
1004
+ path: &str,
1005
+ format: &str,
1006
+ workers: Option<usize>,
1007
+ ) -> std::io::Result<usize> {
1008
+ use std::fs::File;
1009
+ use std::io::{BufWriter, Write};
1010
+
1011
+ // Configure thread pool
1012
+ let num_workers = workers.unwrap_or_else(|| rayon::current_num_threads());
1013
+
1014
+ // Split work across workers
1015
+ let chunk_size = (count + num_workers - 1) / num_workers;
1016
+
1017
+ // Generate chunks in parallel
1018
+ let chunks: Vec<Vec<String>> = (0..num_workers)
1019
+ .into_par_iter()
1020
+ .map(|worker_id| {
1021
+ let start = worker_id * chunk_size;
1022
+ let end = std::cmp::min(start + chunk_size, count);
1023
+
1024
+ let mut rng = ChaCha8Rng::seed_from_u64(
1025
+ self.base_seed.wrapping_add(start as u64)
1026
+ );
1027
+
1028
+ let mut lines = Vec::with_capacity(end - start);
1029
+
1030
+ for i in start..end {
1031
+ let mut record = serde_json::Map::new();
1032
+
1033
+ for spec in &self.field_specs {
1034
+ let value = generate_value(
1035
+ &mut rng,
1036
+ &spec.type_name,
1037
+ &spec.args,
1038
+ self.locale,
1039
+ i,
1040
+ );
1041
+ record.insert(spec.name.clone(), value);
1042
+ }
1043
+
1044
+ match format {
1045
+ "jsonl" | "json" => {
1046
+ lines.push(serde_json::to_string(&record).unwrap());
1047
+ }
1048
+ "csv" => {
1049
+ let values: Vec<String> = self.field_specs.iter()
1050
+ .map(|spec| {
1051
+ let v = record.get(&spec.name).unwrap();
1052
+ match v {
1053
+ serde_json::Value::String(s) => {
1054
+ if s.contains(',') || s.contains('"') {
1055
+ format!("\"{}\"", s.replace('"', "\"\""))
1056
+ } else {
1057
+ s.clone()
1058
+ }
1059
+ }
1060
+ _ => v.to_string()
1061
+ }
1062
+ })
1063
+ .collect();
1064
+ lines.push(values.join(","));
1065
+ }
1066
+ _ => {
1067
+ lines.push(serde_json::to_string(&record).unwrap());
1068
+ }
1069
+ }
1070
+ }
1071
+
1072
+ lines
1073
+ })
1074
+ .collect();
1075
+
1076
+ // Write sequentially (I/O is the bottleneck anyway)
1077
+ let file = File::create(path)?;
1078
+ let mut writer = BufWriter::with_capacity(64 * 1024 * 1024, file);
1079
+
1080
+ // Write CSV header if needed
1081
+ if format == "csv" {
1082
+ let headers: Vec<&str> = self.field_specs.iter()
1083
+ .map(|s| s.name.as_str())
1084
+ .collect();
1085
+ writeln!(writer, "{}", headers.join(","))?;
1086
+ }
1087
+
1088
+ for chunk in chunks {
1089
+ for line in chunk {
1090
+ writeln!(writer, "{}", line)?;
1091
+ }
1092
+ }
1093
+
1094
+ writer.flush()?;
1095
+ Ok(count)
1096
+ }
1097
+ }
1098
+ ```
1099
+
1100
+ ---
1101
+
1102
+ ## API Design
1103
+
1104
+ ### Ruby Interface
1105
+
1106
+ ```ruby
1107
+ # lib/fake_data_dsl/native_engine.rb
1108
+
1109
+ module FakeDataDSL
1110
+ # High-performance native engine using Rust + fake-rs
1111
+ class NativeEngine
1112
+ def initialize(schema, locale: 'en')
1113
+ @schema = schema
1114
+ @locale = locale
1115
+ @field_specs = build_field_specs
1116
+ end
1117
+
1118
+ # Generate records (returns Ruby array)
1119
+ def generate_many(count, seed: nil)
1120
+ Native.generate(@field_specs, count, seed, @locale)
1121
+ end
1122
+
1123
+ # Generate to file (most efficient)
1124
+ def generate_to_file(count, path, seed: nil, format: 'jsonl', workers: nil)
1125
+ Native.generate_to_file(
1126
+ @field_specs, count, path, seed, @locale, format, workers
1127
+ )
1128
+ end
1129
+
1130
+ # Stream (lazy enumeration)
1131
+ def generate_stream(count, seed: nil)
1132
+ Native.generate_stream(@field_specs, count, seed, @locale)
1133
+ end
1134
+
1135
+ # Check if native extension is available
1136
+ def self.available?
1137
+ defined?(Native) && Native.respond_to?(:generate)
1138
+ end
1139
+
1140
+ # Get supported types
1141
+ def self.supported_types
1142
+ Native.available_types
1143
+ end
1144
+
1145
+ # Get supported locales
1146
+ def self.supported_locales
1147
+ Native.available_locales
1148
+ end
1149
+
1150
+ private
1151
+
1152
+ def build_field_specs
1153
+ @schema.fields.map do |field|
1154
+ [
1155
+ field.name,
1156
+ field.type_name,
1157
+ field.type_args.to_json
1158
+ ]
1159
+ end
1160
+ end
1161
+ end
1162
+ end
1163
+ ```
1164
+
1165
+ ### Usage Examples
1166
+
1167
+ ```ruby
1168
+ # Basic usage
1169
+ schema = FakeDataDSL.parse(<<~DSL)
1170
+ User:
1171
+ id: uuid
1172
+ name: name
1173
+ email: email
1174
+ age: number(18..80)
1175
+ active: boolean
1176
+ DSL
1177
+
1178
+ # Check if native engine is available
1179
+ if FakeDataDSL::NativeEngine.available?
1180
+ engine = FakeDataDSL::NativeEngine.new(schema, locale: 'en')
1181
+
1182
+ # Generate 10 million records to file in ~2 seconds
1183
+ engine.generate_to_file(10_000_000, '/tmp/users.jsonl', seed: 42)
1184
+
1185
+ # Generate with specific locale
1186
+ engine = FakeDataDSL::NativeEngine.new(schema, locale: 'ja_jp')
1187
+ engine.generate_to_file(1_000_000, '/tmp/users_jp.jsonl')
1188
+ else
1189
+ # Fall back to Ruby engine
1190
+ engine = FakeDataDSL::Generator::CachedFakerEngine.new(schema)
1191
+ engine.to_file(10_000_000, '/tmp/users.jsonl', seed: 42)
1192
+ end
1193
+ ```
1194
+
1195
+ ---
1196
+
1197
+ ## Building and Installation
1198
+
1199
+ ### Prerequisites
1200
+
1201
+ ```bash
1202
+ # Install Rust
1203
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
1204
+
1205
+ # Verify installation
1206
+ rustc --version
1207
+ cargo --version
1208
+ ```
1209
+
1210
+ ### Build the Extension
1211
+
1212
+ ```bash
1213
+ # Navigate to extension directory
1214
+ cd ext/fake_data_dsl_native
1215
+
1216
+ # Build with cargo
1217
+ cargo build --release
1218
+
1219
+ # Or use rake
1220
+ bundle exec rake native:build
1221
+ ```
1222
+
1223
+ ### Install the Gem with Native Extension
1224
+
1225
+ ```ruby
1226
+ # Gemfile
1227
+ gem 'fake_data_dsl', require: 'fake_data_dsl'
1228
+
1229
+ # The gem will automatically compile the native extension if Rust is available
1230
+ ```
1231
+
1232
+ ### Verify Installation
1233
+
1234
+ ```ruby
1235
+ require 'fake_data_dsl'
1236
+
1237
+ if FakeDataDSL::NativeEngine.available?
1238
+ puts "Native engine available!"
1239
+ puts "Supported types: #{FakeDataDSL::NativeEngine.supported_types}"
1240
+ puts "Supported locales: #{FakeDataDSL::NativeEngine.supported_locales}"
1241
+ else
1242
+ puts "Native engine not available, using Ruby engine"
1243
+ end
1244
+ ```
1245
+
1246
+ ---
1247
+
1248
+ ## Performance Summary
1249
+
1250
+ | Approach | Records/sec | 10M Time | Faker Quality | Locales |
1251
+ |----------|-------------|----------|---------------|---------|
1252
+ | Ruby + Faker | 20K | 8 min | ✅ Full | ✅ 60+ |
1253
+ | Ruby Cached | 300K | 35 sec | ✅ Full | ✅ 60+ |
1254
+ | **Rust + fake-rs** | **5M** | **2 sec** | ✅ Full | ✅ 12 |
1255
+ | Rust (no faker) | 10M | 1 sec | ❌ None | N/A |
1256
+
1257
+ **Best of both worlds:** Rust + fake-rs gives you full faker functionality with native performance!
1258
+
1259
+ ---
1260
+
1261
+ ## References
1262
+
1263
+ - [fake-rs GitHub Repository](https://github.com/cksac/fake-rs)
1264
+ - [Magnus - Ruby bindings for Rust](https://github.com/matsadler/magnus)
1265
+ - [Rayon - Parallel processing in Rust](https://github.com/rayon-rs/rayon)
1266
+
1267
+ ---
1268
+
1269
+ *Last updated: January 2026*
1270
+