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,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nocov:
4
+ module Synthra
5
+ module Export
6
+ # Exports generated data as XML
7
+ #
8
+ # @example Export schema data as XML
9
+ # exporter = XmlData.new(schema, registry: registry, count: 10)
10
+ # xml = exporter.export
11
+ #
12
+ class XmlData < Base
13
+ def export
14
+ count = options[:count] || 10
15
+ seed = options[:seed]
16
+ mode = options[:mode] || :random
17
+ indent = options[:indent] || 2
18
+
19
+ records = schema.generate_many(count, seed: seed, mode: mode, registry: registry)
20
+
21
+ lines = []
22
+ lines << '<?xml version="1.0" encoding="UTF-8"?>'
23
+ lines << "<!-- Generated from Synthra schema: #{schema.name} -->"
24
+ lines << "<!-- Records: #{count}, Generated at: #{Time.now.iso8601} -->"
25
+
26
+ root_name = options[:root] || "#{to_snake_case(schema.name)}s"
27
+ item_name = options[:item] || to_snake_case(schema.name)
28
+
29
+ lines << "<#{root_name}>"
30
+
31
+ records.each do |record|
32
+ lines << build_xml_element(item_name, record, indent)
33
+ end
34
+
35
+ lines << "</#{root_name}>"
36
+ lines.join("\n")
37
+ end
38
+
39
+ private
40
+
41
+ def build_xml_element(name, value, indent, depth = 1)
42
+ prefix = " " * (indent * depth)
43
+
44
+ case value
45
+ when Hash
46
+ lines = ["#{prefix}<#{name}>"]
47
+ value.each do |k, v|
48
+ lines << build_xml_element(to_snake_case(k.to_s), v, indent, depth + 1)
49
+ end
50
+ lines << "#{prefix}</#{name}>"
51
+ lines.join("\n")
52
+ when Array
53
+ if value.empty?
54
+ "#{prefix}<#{name}/>"
55
+ else
56
+ lines = ["#{prefix}<#{name}>"]
57
+ item_name = singularize(name)
58
+ value.each do |item|
59
+ lines << build_xml_element(item_name, item, indent, depth + 1)
60
+ end
61
+ lines << "#{prefix}</#{name}>"
62
+ lines.join("\n")
63
+ end
64
+ when nil
65
+ "#{prefix}<#{name} nil=\"true\"/>"
66
+ when true, false
67
+ "#{prefix}<#{name}>#{value}</#{name}>"
68
+ else
69
+ escaped = escape_xml(value.to_s)
70
+ "#{prefix}<#{name}>#{escaped}</#{name}>"
71
+ end
72
+ end
73
+
74
+ def escape_xml(str)
75
+ str.gsub("&", "&amp;")
76
+ .gsub("<", "&lt;")
77
+ .gsub(">", "&gt;")
78
+ .gsub("\"", "&quot;")
79
+ .gsub("'", "&apos;")
80
+ end
81
+
82
+ def to_snake_case(str)
83
+ str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
84
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
85
+ .downcase
86
+ end
87
+
88
+ def singularize(str)
89
+ # Simple singularization
90
+ case str
91
+ when /ies$/
92
+ str.sub(/ies$/, "y")
93
+ when /es$/
94
+ str.sub(/es$/, "")
95
+ when /s$/
96
+ str.sub(/s$/, "")
97
+ else
98
+ str
99
+ end
100
+ end
101
+ end
102
+ # :nocov:
103
+ end
104
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ # :nocov:
6
+ module Synthra
7
+ module Export
8
+ # Exports generated data as YAML
9
+ #
10
+ # @example Export schema data as YAML
11
+ # exporter = YamlData.new(schema, registry: registry, count: 10)
12
+ # yaml = exporter.export
13
+ #
14
+ class YamlData < Base
15
+ def export
16
+ count = options[:count] || 10
17
+ seed = options[:seed]
18
+ mode = options[:mode] || :random
19
+
20
+ records = if count == 1
21
+ schema.generate(seed: seed, mode: mode, registry: registry) # single record
22
+ else
23
+ schema.generate_many(count, seed: seed, mode: mode, registry: registry)
24
+ end
25
+
26
+ output = wrap_output(records)
27
+
28
+ # Add header comment
29
+ lines = []
30
+ lines << "# Generated from Synthra schema: #{schema.name}"
31
+ lines << "# Version: #{schema.version}" if schema.version
32
+ lines << "# Records: #{count}"
33
+ lines << "# Generated at: #{Time.now.iso8601}"
34
+ lines << ""
35
+ lines << YAML.dump(output)
36
+
37
+ lines.join("\n")
38
+ end
39
+
40
+ private
41
+
42
+ def wrap_output(records)
43
+ # :nocov:
44
+ if options[:wrap] == false
45
+ records
46
+ # :nocov:
47
+ elsif options[:root_key]
48
+ { options[:root_key] => records }
49
+ else
50
+ records
51
+ end
52
+ end
53
+ end
54
+ # :nocov:
55
+ end
56
+ end
@@ -0,0 +1,404 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "export/base"
4
+ require_relative "export/json_schema"
5
+ require_relative "export/typescript"
6
+ require_relative "export/javascript"
7
+ require_relative "export/python"
8
+ require_relative "export/sql"
9
+ require_relative "export/csv"
10
+ require_relative "export/graphviz"
11
+ require_relative "export/graphql"
12
+ require_relative "export/json_data"
13
+ require_relative "export/sql_insert"
14
+ require_relative "export/yaml_data"
15
+ require_relative "export/xml_data"
16
+
17
+ module Synthra
18
+ # Export module for converting schemas to various formats
19
+ #
20
+ # Supports two categories of exports:
21
+ #
22
+ # ## Schema Exports (structure/types)
23
+ # - JSON Schema (draft 2020-12)
24
+ # - TypeScript interfaces
25
+ # - JavaScript with JSDoc types
26
+ # - SQL CREATE TABLE statements
27
+ # - GraphViz DOT diagrams
28
+ #
29
+ # ## Data Exports (generated records)
30
+ # - JSON data
31
+ # - CSV data
32
+ # - SQL INSERT statements
33
+ # - YAML data
34
+ # - XML data
35
+ #
36
+ # @example Export schema as TypeScript
37
+ # Synthra::Export.typescript(schema, registry: registry)
38
+ #
39
+ # @example Export generated data as JSON
40
+ # Synthra::Export.json_data(schema, registry: registry, count: 100)
41
+ #
42
+ # @example Export generated data as SQL INSERTs
43
+ # Synthra::Export.sql_insert(schema, count: 50, dialect: :postgresql)
44
+ #
45
+ module Export
46
+ # =========================================================================
47
+ # Schema Export Formats (types/structure)
48
+ # =========================================================================
49
+ SCHEMA_FORMATS = {
50
+ "json-schema" => JsonSchema,
51
+ "typescript" => Typescript,
52
+ "ts" => Typescript,
53
+ "javascript" => Javascript,
54
+ "js" => Javascript,
55
+ "python" => Python,
56
+ "py" => Python,
57
+ "pydantic" => Python,
58
+ "dataclass" => Python,
59
+ "sql" => Sql,
60
+ "sql-ddl" => Sql,
61
+ "postgresql" => Sql,
62
+ "mysql" => Sql,
63
+ "sqlite" => Sql,
64
+ "graphviz" => Graphviz,
65
+ "dot" => Graphviz,
66
+ "graphql" => Graphql,
67
+ "gql" => Graphql
68
+ }.freeze
69
+
70
+ # File extensions for auto-generated filenames
71
+ FILE_EXTENSIONS = {
72
+ "json-schema" => ".schema.json",
73
+ "typescript" => ".ts",
74
+ "ts" => ".ts",
75
+ "javascript" => ".js",
76
+ "js" => ".js",
77
+ "python" => ".py",
78
+ "py" => ".py",
79
+ "pydantic" => ".py",
80
+ "dataclass" => ".py",
81
+ "sql" => ".sql",
82
+ "sql-ddl" => ".sql",
83
+ "postgresql" => ".sql",
84
+ "mysql" => ".sql",
85
+ "sqlite" => ".sql",
86
+ "graphviz" => ".dot",
87
+ "dot" => ".dot",
88
+ "graphql" => ".graphql",
89
+ "gql" => ".graphql",
90
+ "json" => ".json",
91
+ "json-data" => ".json",
92
+ "csv" => ".csv",
93
+ "sql-insert" => ".sql",
94
+ "insert" => ".sql",
95
+ "yaml" => ".yaml",
96
+ "yml" => ".yaml",
97
+ "xml" => ".xml"
98
+ }.freeze
99
+
100
+ # =========================================================================
101
+ # Data Export Formats (generated records)
102
+ # =========================================================================
103
+ DATA_FORMATS = {
104
+ "json" => JsonData,
105
+ "json-data" => JsonData,
106
+ "csv" => Csv,
107
+ "sql-insert" => SqlInsert,
108
+ "insert" => SqlInsert,
109
+ "yaml" => YamlData,
110
+ "yml" => YamlData,
111
+ "xml" => XmlData
112
+ }.freeze
113
+
114
+ # All available formats
115
+ FORMATS = SCHEMA_FORMATS.merge(DATA_FORMATS).freeze
116
+
117
+ class << self
118
+ # Export schema to specified format
119
+ #
120
+ # @param format [String, Symbol] the export format
121
+ # @param schema [Schema] the schema to export
122
+ # @param registry [Registry] the registry for resolving references
123
+ # @param options [Hash] format-specific options
124
+ # @return [String] the exported content
125
+ #
126
+ def export(format, schema, registry: nil, **options)
127
+ format_key = format.to_s.downcase
128
+ exporter_class = FORMATS[format_key]
129
+
130
+ unless exporter_class
131
+ available = available_formats.join(", ")
132
+ raise ArgumentError, "Unknown format: #{format}. Available: #{available}"
133
+ end
134
+
135
+ # Set dialect for SQL formats
136
+ if %w[postgresql mysql sqlite].include?(format_key)
137
+ options[:dialect] = format_key.to_sym
138
+ end
139
+
140
+ exporter = exporter_class.new(schema, registry: registry, **options)
141
+ exporter.export
142
+ end
143
+
144
+ # =====================================================================
145
+ # Schema Export Methods
146
+ # =====================================================================
147
+
148
+ # Export schema as JSON Schema
149
+ #
150
+ # @param schema [Schema] the schema to export
151
+ # @param options [Hash] export options
152
+ # @return [String] JSON Schema string
153
+ #
154
+ def json_schema(schema, **options)
155
+ JsonSchema.new(schema, **options).export
156
+ end
157
+
158
+ # Export schema as TypeScript interface
159
+ #
160
+ # @param schema [Schema] the schema to export
161
+ # @param options [Hash] export options
162
+ # @return [String] TypeScript interface string
163
+ #
164
+ def typescript(schema, **options)
165
+ Typescript.new(schema, **options).export
166
+ end
167
+
168
+ # Export schema as JavaScript with JSDoc
169
+ #
170
+ # @param schema [Schema] the schema to export
171
+ # @param options [Hash] export options
172
+ # @return [String] JavaScript with JSDoc types
173
+ #
174
+ def javascript(schema, **options)
175
+ Javascript.new(schema, **options).export
176
+ end
177
+
178
+ # Export schema as Python (Pydantic/dataclass/TypedDict)
179
+ #
180
+ # @param schema [Schema] the schema to export
181
+ # @param options [Hash] export options (style: :pydantic/:dataclass/:typed_dict)
182
+ # @return [String] Python code
183
+ #
184
+ def python(schema, **options)
185
+ Python.new(schema, **options).export
186
+ end
187
+
188
+ # Export schema as SQL CREATE TABLE
189
+ #
190
+ # @param schema [Schema] the schema to export
191
+ # @param options [Hash] export options (dialect: :postgresql/:mysql/:sqlite)
192
+ # @return [String] SQL statement string
193
+ #
194
+ def sql(schema, **options)
195
+ Sql.new(schema, **options).export
196
+ end
197
+
198
+ # Export schemas as GraphViz DOT
199
+ #
200
+ # @param schema [Schema, nil] single schema or nil for registry
201
+ # @param options [Hash] export options (registry, direction)
202
+ # @return [String] DOT string
203
+ #
204
+ def graphviz(schema = nil, **options)
205
+ Graphviz.new(schema, **options).export
206
+ end
207
+
208
+ # Export schema as GraphQL SDL
209
+ #
210
+ # @param schema [Schema] the schema to export
211
+ # @param options [Hash] export options (registry)
212
+ # @return [String] GraphQL SDL string
213
+ #
214
+ def graphql(schema, **options)
215
+ Graphql.new(schema, **options).export
216
+ end
217
+
218
+ # =====================================================================
219
+ # Data Export Methods
220
+ # =====================================================================
221
+
222
+ # Export generated data as JSON
223
+ #
224
+ # @param schema [Schema] the schema to generate from
225
+ # @param options [Hash] export options (count, seed, mode, pretty, envelope)
226
+ # @return [String] JSON string
227
+ #
228
+ def json_data(schema, **options)
229
+ JsonData.new(schema, **options).export
230
+ end
231
+
232
+ # Export generated data as CSV
233
+ #
234
+ # @param schema [Schema] the schema to generate from
235
+ # @param options [Hash] export options (count, seed, mode)
236
+ # @return [String] CSV string
237
+ #
238
+ def csv(schema, **options)
239
+ Csv.new(schema, **options).export
240
+ end
241
+
242
+ # Export generated data as SQL INSERT statements
243
+ #
244
+ # @param schema [Schema] the schema to generate from
245
+ # @param options [Hash] export options (count, seed, mode, dialect, batch_insert)
246
+ # @return [String] SQL INSERT statements
247
+ #
248
+ def sql_insert(schema, **options)
249
+ SqlInsert.new(schema, **options).export
250
+ end
251
+
252
+ # Export generated data as YAML
253
+ #
254
+ # @param schema [Schema] the schema to generate from
255
+ # @param options [Hash] export options (count, seed, mode, root_key)
256
+ # @return [String] YAML string
257
+ #
258
+ def yaml_data(schema, **options)
259
+ YamlData.new(schema, **options).export
260
+ end
261
+
262
+ # Export generated data as XML
263
+ #
264
+ # @param schema [Schema] the schema to generate from
265
+ # @param options [Hash] export options (count, seed, mode, root, item, indent)
266
+ # @return [String] XML string
267
+ #
268
+ def xml_data(schema, **options)
269
+ XmlData.new(schema, **options).export
270
+ end
271
+
272
+ # =====================================================================
273
+ # Bulk Export Methods
274
+ # =====================================================================
275
+
276
+ # Export all schemas from a registry to a format
277
+ #
278
+ # @param registry [Registry] the registry
279
+ # @param format [String, Symbol] the export format
280
+ # @param options [Hash] format-specific options
281
+ # @return [String] the exported content
282
+ #
283
+ def export_all(registry, format, **options)
284
+ format_key = format.to_s.downcase
285
+
286
+ case format_key
287
+ when "typescript", "ts"
288
+ Typescript.export_all(registry, **options)
289
+ when "javascript", "js"
290
+ Javascript.export_all(registry, **options)
291
+ when "python", "py", "pydantic", "dataclass"
292
+ options[:style] = :dataclass if format_key == "dataclass"
293
+ Python.export_all(registry, **options)
294
+ when "sql", "postgresql", "mysql", "sqlite", "sql-ddl"
295
+ options[:dialect] = format_key.to_sym if %w[postgresql mysql sqlite].include?(format_key)
296
+ Sql.export_all(registry, **options)
297
+ when "graphviz", "dot"
298
+ Graphviz.new(nil, registry: registry, **options).export
299
+ when "graphql", "gql"
300
+ Graphql.export_all(registry)
301
+ else
302
+ raise ArgumentError, "Bulk export not supported for format: #{format}"
303
+ end
304
+ end
305
+
306
+ # =====================================================================
307
+ # Utility Methods
308
+ # =====================================================================
309
+
310
+ # List available formats
311
+ #
312
+ # @return [Array<String>] available format names
313
+ #
314
+ def available_formats
315
+ FORMATS.keys.uniq.sort
316
+ end
317
+
318
+ # List schema export formats
319
+ #
320
+ # @return [Array<String>] schema format names
321
+ #
322
+ def schema_formats
323
+ SCHEMA_FORMATS.keys.uniq.sort
324
+ end
325
+
326
+ # List data export formats
327
+ #
328
+ # @return [Array<String>] data format names
329
+ #
330
+ def data_formats
331
+ DATA_FORMATS.keys.uniq.sort
332
+ end
333
+
334
+ # Check if format is for schema export
335
+ #
336
+ # @param format [String] format name
337
+ # @return [Boolean]
338
+ #
339
+ def schema_format?(format)
340
+ SCHEMA_FORMATS.key?(format.to_s.downcase)
341
+ end
342
+
343
+ # Check if format is for data export
344
+ #
345
+ # @param format [String] format name
346
+ # @return [Boolean]
347
+ #
348
+ def data_format?(format)
349
+ DATA_FORMATS.key?(format.to_s.downcase)
350
+ end
351
+
352
+ # Get file extension for a format
353
+ #
354
+ # @param format [String] format name
355
+ # @return [String] file extension with dot
356
+ #
357
+ def file_extension(format)
358
+ FILE_EXTENSIONS[format.to_s.downcase] || ".txt"
359
+ end
360
+
361
+ # Generate automatic filename
362
+ #
363
+ # @param schema_name [String] schema name
364
+ # @param format [String] format name
365
+ # @param options [Hash] additional options (prefix, suffix)
366
+ # @return [String] generated filename
367
+ #
368
+ def auto_filename(schema_name, format, **options)
369
+ prefix = options[:prefix] || ""
370
+ suffix = options[:suffix] || ""
371
+ ext = file_extension(format)
372
+
373
+ base = to_snake_case(schema_name)
374
+ "#{prefix}#{base}#{suffix}#{ext}"
375
+ end
376
+
377
+ # Export and save to auto-generated file
378
+ #
379
+ # @param format [String] format name
380
+ # @param schema [Schema] schema to export
381
+ # @param output_dir [String] output directory
382
+ # @param options [Hash] export options
383
+ # @return [String] path to generated file
384
+ #
385
+ def export_to_file(format, schema, output_dir: ".", **options)
386
+ filename = auto_filename(schema.name, format, **options)
387
+ path = File.join(output_dir, filename)
388
+
389
+ content = export(format, schema, **options)
390
+ File.write(path, content)
391
+
392
+ path
393
+ end
394
+
395
+ private
396
+
397
+ def to_snake_case(str)
398
+ str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
399
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
400
+ .downcase
401
+ end
402
+ end
403
+ end
404
+ end