konpeito 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 (180) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +75 -0
  4. data/CONTRIBUTING.md +123 -0
  5. data/LICENSE +21 -0
  6. data/README.md +257 -0
  7. data/Rakefile +11 -0
  8. data/bin/konpeito +6 -0
  9. data/konpeito.gemspec +43 -0
  10. data/lib/konpeito/ast/typed_ast.rb +620 -0
  11. data/lib/konpeito/ast/visitor.rb +78 -0
  12. data/lib/konpeito/cache/cache_manager.rb +230 -0
  13. data/lib/konpeito/cache/dependency_graph.rb +192 -0
  14. data/lib/konpeito/cache.rb +8 -0
  15. data/lib/konpeito/cli/base_command.rb +187 -0
  16. data/lib/konpeito/cli/build_command.rb +220 -0
  17. data/lib/konpeito/cli/check_command.rb +104 -0
  18. data/lib/konpeito/cli/config.rb +231 -0
  19. data/lib/konpeito/cli/deps_command.rb +128 -0
  20. data/lib/konpeito/cli/doctor_command.rb +340 -0
  21. data/lib/konpeito/cli/fmt_command.rb +199 -0
  22. data/lib/konpeito/cli/init_command.rb +312 -0
  23. data/lib/konpeito/cli/lsp_command.rb +40 -0
  24. data/lib/konpeito/cli/run_command.rb +150 -0
  25. data/lib/konpeito/cli/test_command.rb +248 -0
  26. data/lib/konpeito/cli/watch_command.rb +212 -0
  27. data/lib/konpeito/cli.rb +301 -0
  28. data/lib/konpeito/codegen/builtin_methods.rb +229 -0
  29. data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
  30. data/lib/konpeito/codegen/debug_info.rb +352 -0
  31. data/lib/konpeito/codegen/inliner.rb +486 -0
  32. data/lib/konpeito/codegen/jvm_backend.rb +197 -0
  33. data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
  34. data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
  35. data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
  36. data/lib/konpeito/codegen/monomorphizer.rb +359 -0
  37. data/lib/konpeito/codegen/profile_runtime.c +341 -0
  38. data/lib/konpeito/codegen/profiler.rb +99 -0
  39. data/lib/konpeito/compiler.rb +592 -0
  40. data/lib/konpeito/dependency_resolver.rb +296 -0
  41. data/lib/konpeito/diagnostics/collector.rb +127 -0
  42. data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
  43. data/lib/konpeito/diagnostics/renderer.rb +144 -0
  44. data/lib/konpeito/formatter/formatter.rb +1214 -0
  45. data/lib/konpeito/hir/builder.rb +7167 -0
  46. data/lib/konpeito/hir/nodes.rb +2465 -0
  47. data/lib/konpeito/lsp/document_manager.rb +820 -0
  48. data/lib/konpeito/lsp/server.rb +183 -0
  49. data/lib/konpeito/lsp/transport.rb +38 -0
  50. data/lib/konpeito/parser/prism_adapter.rb +65 -0
  51. data/lib/konpeito/platform.rb +103 -0
  52. data/lib/konpeito/profile/report.rb +136 -0
  53. data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
  54. data/lib/konpeito/stdlib/compression/compression.rb +72 -0
  55. data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
  56. data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
  57. data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
  58. data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
  59. data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
  60. data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
  61. data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
  62. data/lib/konpeito/stdlib/http/extconf.rb +19 -0
  63. data/lib/konpeito/stdlib/http/http.rb +125 -0
  64. data/lib/konpeito/stdlib/http/http.rbs +57 -0
  65. data/lib/konpeito/stdlib/http/http_native.c +440 -0
  66. data/lib/konpeito/stdlib/json/extconf.rb +17 -0
  67. data/lib/konpeito/stdlib/json/json.rb +44 -0
  68. data/lib/konpeito/stdlib/json/json.rbs +33 -0
  69. data/lib/konpeito/stdlib/json/json_native.c +286 -0
  70. data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
  71. data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
  72. data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
  73. data/lib/konpeito/stdlib/ui/ui.rb +318 -0
  74. data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
  75. data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
  76. data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
  77. data/lib/konpeito/type_checker/inferrer.rb +565 -0
  78. data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
  79. data/lib/konpeito/type_checker/type_resolver.rb +276 -0
  80. data/lib/konpeito/type_checker/types.rb +1434 -0
  81. data/lib/konpeito/type_checker/unification.rb +323 -0
  82. data/lib/konpeito/ui/animation/animated_state.rb +80 -0
  83. data/lib/konpeito/ui/animation/easing.rb +59 -0
  84. data/lib/konpeito/ui/animation/value_tween.rb +66 -0
  85. data/lib/konpeito/ui/app.rb +379 -0
  86. data/lib/konpeito/ui/box.rb +38 -0
  87. data/lib/konpeito/ui/castella.rb +70 -0
  88. data/lib/konpeito/ui/castella_native.rb +76 -0
  89. data/lib/konpeito/ui/chart/area_chart.rb +305 -0
  90. data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
  91. data/lib/konpeito/ui/chart/base_chart.rb +210 -0
  92. data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
  93. data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
  94. data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
  95. data/lib/konpeito/ui/chart/line_chart.rb +289 -0
  96. data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
  97. data/lib/konpeito/ui/chart/scales.rb +77 -0
  98. data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
  99. data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
  100. data/lib/konpeito/ui/column.rb +271 -0
  101. data/lib/konpeito/ui/core.rb +2199 -0
  102. data/lib/konpeito/ui/dsl.rb +443 -0
  103. data/lib/konpeito/ui/frame.rb +171 -0
  104. data/lib/konpeito/ui/frame_native.rb +494 -0
  105. data/lib/konpeito/ui/markdown/ast.rb +124 -0
  106. data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
  107. data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
  108. data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
  109. data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
  110. data/lib/konpeito/ui/markdown/parser.rb +805 -0
  111. data/lib/konpeito/ui/markdown/renderer.rb +639 -0
  112. data/lib/konpeito/ui/markdown/theme.rb +165 -0
  113. data/lib/konpeito/ui/render_node.rb +260 -0
  114. data/lib/konpeito/ui/row.rb +207 -0
  115. data/lib/konpeito/ui/spacer.rb +18 -0
  116. data/lib/konpeito/ui/style.rb +799 -0
  117. data/lib/konpeito/ui/theme.rb +563 -0
  118. data/lib/konpeito/ui/themes/material.rb +35 -0
  119. data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
  120. data/lib/konpeito/ui/widgets/button.rb +103 -0
  121. data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
  122. data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
  123. data/lib/konpeito/ui/widgets/container.rb +91 -0
  124. data/lib/konpeito/ui/widgets/data_table.rb +667 -0
  125. data/lib/konpeito/ui/widgets/divider.rb +29 -0
  126. data/lib/konpeito/ui/widgets/image.rb +105 -0
  127. data/lib/konpeito/ui/widgets/input.rb +485 -0
  128. data/lib/konpeito/ui/widgets/markdown.rb +57 -0
  129. data/lib/konpeito/ui/widgets/modal.rb +163 -0
  130. data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
  131. data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
  132. data/lib/konpeito/ui/widgets/net_image.rb +100 -0
  133. data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
  134. data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
  135. data/lib/konpeito/ui/widgets/slider.rb +133 -0
  136. data/lib/konpeito/ui/widgets/switch.rb +84 -0
  137. data/lib/konpeito/ui/widgets/tabs.rb +157 -0
  138. data/lib/konpeito/ui/widgets/text.rb +110 -0
  139. data/lib/konpeito/ui/widgets/tree.rb +426 -0
  140. data/lib/konpeito/version.rb +5 -0
  141. data/lib/konpeito.rb +109 -0
  142. data/test_native_array.rb +172 -0
  143. data/test_native_array_class.rb +197 -0
  144. data/test_native_class.rb +151 -0
  145. data/tools/konpeito-asm/build.sh +65 -0
  146. data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
  147. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
  148. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
  149. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
  150. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
  151. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
  152. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
  153. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
  154. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
  155. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
  156. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
  157. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
  158. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
  159. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
  160. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
  161. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
  162. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
  163. data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
  164. data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
  165. data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
  166. data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
  167. data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
  168. data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
  169. data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
  170. data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
  171. data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
  172. data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
  173. data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
  174. data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
  175. data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
  176. data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
  177. data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
  178. data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
  179. data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
  180. metadata +267 -0
@@ -0,0 +1,440 @@
1
+ /*
2
+ * Konpeito HTTP stdlib - libcurl wrapper
3
+ *
4
+ * Provides HTTP client functionality using libcurl.
5
+ * Supports GET, POST, and custom headers.
6
+ */
7
+
8
+ #include <ruby.h>
9
+ #include <curl/curl.h>
10
+ #include <stdlib.h>
11
+ #include <string.h>
12
+
13
+ /* Response buffer structure */
14
+ typedef struct {
15
+ char *data;
16
+ size_t size;
17
+ } response_buffer_t;
18
+
19
+ /* Write callback for curl */
20
+ static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
21
+ size_t realsize = size * nmemb;
22
+ response_buffer_t *buf = (response_buffer_t *)userp;
23
+
24
+ char *ptr = realloc(buf->data, buf->size + realsize + 1);
25
+ if (!ptr) {
26
+ return 0; /* out of memory */
27
+ }
28
+
29
+ buf->data = ptr;
30
+ memcpy(&(buf->data[buf->size]), contents, realsize);
31
+ buf->size += realsize;
32
+ buf->data[buf->size] = '\0';
33
+
34
+ return realsize;
35
+ }
36
+
37
+ /* Header write callback for curl */
38
+ static size_t header_callback(char *buffer, size_t size, size_t nitems, void *userp) {
39
+ size_t realsize = size * nitems;
40
+ VALUE headers_hash = (VALUE)userp;
41
+
42
+ /* Parse header line: "Key: Value\r\n" */
43
+ char *colon = memchr(buffer, ':', realsize);
44
+ if (colon && colon > buffer) {
45
+ size_t key_len = colon - buffer;
46
+ char *value_start = colon + 1;
47
+
48
+ /* Skip leading whitespace in value */
49
+ while (*value_start == ' ' && value_start < buffer + realsize) {
50
+ value_start++;
51
+ }
52
+
53
+ /* Calculate value length, removing trailing \r\n */
54
+ size_t value_len = realsize - (value_start - buffer);
55
+ while (value_len > 0 && (value_start[value_len - 1] == '\r' || value_start[value_len - 1] == '\n')) {
56
+ value_len--;
57
+ }
58
+
59
+ if (value_len > 0) {
60
+ VALUE key = rb_utf8_str_new(buffer, key_len);
61
+ VALUE value = rb_utf8_str_new(value_start, value_len);
62
+ rb_hash_aset(headers_hash, key, value);
63
+ }
64
+ }
65
+
66
+ return realsize;
67
+ }
68
+
69
+ /*
70
+ * Perform HTTP GET request
71
+ *
72
+ * @param url [String] URL to fetch
73
+ * @return [String] Response body
74
+ * @raise [RuntimeError] if request fails
75
+ */
76
+ VALUE konpeito_http_get(VALUE self, VALUE url) {
77
+ Check_Type(url, T_STRING);
78
+
79
+ const char *url_str = RSTRING_PTR(url);
80
+
81
+ CURL *curl = curl_easy_init();
82
+ if (!curl) {
83
+ rb_raise(rb_eRuntimeError, "Failed to initialize curl");
84
+ return Qnil;
85
+ }
86
+
87
+ response_buffer_t buf = {0};
88
+ buf.data = malloc(1);
89
+ buf.data[0] = '\0';
90
+ buf.size = 0;
91
+
92
+ curl_easy_setopt(curl, CURLOPT_URL, url_str);
93
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
94
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
95
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
96
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
97
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
98
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); /* Required for multi-threaded use */
99
+ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
100
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "Konpeito-HTTP/1.0");
101
+
102
+ CURLcode res = curl_easy_perform(curl);
103
+
104
+ if (res != CURLE_OK) {
105
+ free(buf.data);
106
+ curl_easy_cleanup(curl);
107
+ rb_raise(rb_eRuntimeError, "HTTP request failed: %s", curl_easy_strerror(res));
108
+ return Qnil;
109
+ }
110
+
111
+ VALUE result = rb_utf8_str_new(buf.data, buf.size);
112
+
113
+ free(buf.data);
114
+ curl_easy_cleanup(curl);
115
+
116
+ return result;
117
+ }
118
+
119
+ /*
120
+ * Perform HTTP POST request
121
+ *
122
+ * @param url [String] URL to post to
123
+ * @param body [String] Request body
124
+ * @return [String] Response body
125
+ * @raise [RuntimeError] if request fails
126
+ */
127
+ VALUE konpeito_http_post(VALUE self, VALUE url, VALUE body) {
128
+ Check_Type(url, T_STRING);
129
+ Check_Type(body, T_STRING);
130
+
131
+ const char *url_str = RSTRING_PTR(url);
132
+ const char *body_str = RSTRING_PTR(body);
133
+ size_t body_len = RSTRING_LEN(body);
134
+
135
+ CURL *curl = curl_easy_init();
136
+ if (!curl) {
137
+ rb_raise(rb_eRuntimeError, "Failed to initialize curl");
138
+ return Qnil;
139
+ }
140
+
141
+ response_buffer_t buf = {0};
142
+ buf.data = malloc(1);
143
+ buf.data[0] = '\0';
144
+ buf.size = 0;
145
+
146
+ curl_easy_setopt(curl, CURLOPT_URL, url_str);
147
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
148
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body_str);
149
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)body_len);
150
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
151
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
152
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
153
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
154
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "Konpeito-HTTP/1.0");
155
+
156
+ CURLcode res = curl_easy_perform(curl);
157
+
158
+ if (res != CURLE_OK) {
159
+ free(buf.data);
160
+ curl_easy_cleanup(curl);
161
+ rb_raise(rb_eRuntimeError, "HTTP request failed: %s", curl_easy_strerror(res));
162
+ return Qnil;
163
+ }
164
+
165
+ VALUE result = rb_utf8_str_new(buf.data, buf.size);
166
+
167
+ free(buf.data);
168
+ curl_easy_cleanup(curl);
169
+
170
+ return result;
171
+ }
172
+
173
+ /*
174
+ * Perform HTTP GET request with full response details
175
+ *
176
+ * @param url [String] URL to fetch
177
+ * @return [Hash] Response with :status, :body, :headers
178
+ * @raise [RuntimeError] if request fails
179
+ */
180
+ VALUE konpeito_http_get_response(VALUE self, VALUE url) {
181
+ Check_Type(url, T_STRING);
182
+
183
+ const char *url_str = RSTRING_PTR(url);
184
+
185
+ CURL *curl = curl_easy_init();
186
+ if (!curl) {
187
+ rb_raise(rb_eRuntimeError, "Failed to initialize curl");
188
+ return Qnil;
189
+ }
190
+
191
+ response_buffer_t buf = {0};
192
+ buf.data = malloc(1);
193
+ buf.data[0] = '\0';
194
+ buf.size = 0;
195
+
196
+ VALUE headers_hash = rb_hash_new();
197
+
198
+ curl_easy_setopt(curl, CURLOPT_URL, url_str);
199
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
200
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
201
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
202
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)headers_hash);
203
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
204
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
205
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "Konpeito-HTTP/1.0");
206
+
207
+ CURLcode res = curl_easy_perform(curl);
208
+
209
+ if (res != CURLE_OK) {
210
+ free(buf.data);
211
+ curl_easy_cleanup(curl);
212
+ rb_raise(rb_eRuntimeError, "HTTP request failed: %s", curl_easy_strerror(res));
213
+ return Qnil;
214
+ }
215
+
216
+ long status_code;
217
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code);
218
+
219
+ VALUE result = rb_hash_new();
220
+ rb_hash_aset(result, ID2SYM(rb_intern("status")), LONG2NUM(status_code));
221
+ rb_hash_aset(result, ID2SYM(rb_intern("body")), rb_utf8_str_new(buf.data, buf.size));
222
+ rb_hash_aset(result, ID2SYM(rb_intern("headers")), headers_hash);
223
+
224
+ free(buf.data);
225
+ curl_easy_cleanup(curl);
226
+
227
+ return result;
228
+ }
229
+
230
+ /*
231
+ * Perform HTTP POST request with full response details
232
+ *
233
+ * @param url [String] URL to post to
234
+ * @param body [String] Request body
235
+ * @param content_type [String] Content-Type header (optional, default: application/x-www-form-urlencoded)
236
+ * @return [Hash] Response with :status, :body, :headers
237
+ * @raise [RuntimeError] if request fails
238
+ */
239
+ VALUE konpeito_http_post_response(VALUE self, VALUE url, VALUE body, VALUE content_type) {
240
+ Check_Type(url, T_STRING);
241
+ Check_Type(body, T_STRING);
242
+
243
+ const char *url_str = RSTRING_PTR(url);
244
+ const char *body_str = RSTRING_PTR(body);
245
+ size_t body_len = RSTRING_LEN(body);
246
+
247
+ CURL *curl = curl_easy_init();
248
+ if (!curl) {
249
+ rb_raise(rb_eRuntimeError, "Failed to initialize curl");
250
+ return Qnil;
251
+ }
252
+
253
+ struct curl_slist *headers_list = NULL;
254
+ if (!NIL_P(content_type)) {
255
+ Check_Type(content_type, T_STRING);
256
+ char header_buf[256];
257
+ snprintf(header_buf, sizeof(header_buf), "Content-Type: %s", RSTRING_PTR(content_type));
258
+ headers_list = curl_slist_append(headers_list, header_buf);
259
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_list);
260
+ }
261
+
262
+ response_buffer_t buf = {0};
263
+ buf.data = malloc(1);
264
+ buf.data[0] = '\0';
265
+ buf.size = 0;
266
+
267
+ VALUE headers_hash = rb_hash_new();
268
+
269
+ curl_easy_setopt(curl, CURLOPT_URL, url_str);
270
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
271
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body_str);
272
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)body_len);
273
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
274
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
275
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
276
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)headers_hash);
277
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
278
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
279
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "Konpeito-HTTP/1.0");
280
+
281
+ CURLcode res = curl_easy_perform(curl);
282
+
283
+ if (headers_list) {
284
+ curl_slist_free_all(headers_list);
285
+ }
286
+
287
+ if (res != CURLE_OK) {
288
+ free(buf.data);
289
+ curl_easy_cleanup(curl);
290
+ rb_raise(rb_eRuntimeError, "HTTP request failed: %s", curl_easy_strerror(res));
291
+ return Qnil;
292
+ }
293
+
294
+ long status_code;
295
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code);
296
+
297
+ VALUE result = rb_hash_new();
298
+ rb_hash_aset(result, ID2SYM(rb_intern("status")), LONG2NUM(status_code));
299
+ rb_hash_aset(result, ID2SYM(rb_intern("body")), rb_utf8_str_new(buf.data, buf.size));
300
+ rb_hash_aset(result, ID2SYM(rb_intern("headers")), headers_hash);
301
+
302
+ free(buf.data);
303
+ curl_easy_cleanup(curl);
304
+
305
+ return result;
306
+ }
307
+
308
+ /*
309
+ * Perform HTTP request with custom method and headers
310
+ *
311
+ * @param method [String] HTTP method (GET, POST, PUT, DELETE, PATCH)
312
+ * @param url [String] URL
313
+ * @param body [String, nil] Request body (optional)
314
+ * @param headers [Hash, nil] Custom headers (optional)
315
+ * @return [Hash] Response with :status, :body, :headers
316
+ * @raise [RuntimeError] if request fails
317
+ */
318
+ VALUE konpeito_http_request(VALUE self, VALUE method, VALUE url, VALUE body, VALUE headers) {
319
+ Check_Type(method, T_STRING);
320
+ Check_Type(url, T_STRING);
321
+
322
+ const char *method_str = RSTRING_PTR(method);
323
+ const char *url_str = RSTRING_PTR(url);
324
+
325
+ CURL *curl = curl_easy_init();
326
+ if (!curl) {
327
+ rb_raise(rb_eRuntimeError, "Failed to initialize curl");
328
+ return Qnil;
329
+ }
330
+
331
+ /* Set HTTP method */
332
+ if (strcmp(method_str, "GET") == 0) {
333
+ curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
334
+ } else if (strcmp(method_str, "POST") == 0) {
335
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
336
+ } else if (strcmp(method_str, "PUT") == 0) {
337
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
338
+ } else if (strcmp(method_str, "DELETE") == 0) {
339
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
340
+ } else if (strcmp(method_str, "PATCH") == 0) {
341
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
342
+ } else if (strcmp(method_str, "HEAD") == 0) {
343
+ curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
344
+ } else {
345
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method_str);
346
+ }
347
+
348
+ /* Set request body */
349
+ if (!NIL_P(body)) {
350
+ Check_Type(body, T_STRING);
351
+ const char *body_str = RSTRING_PTR(body);
352
+ size_t body_len = RSTRING_LEN(body);
353
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body_str);
354
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)body_len);
355
+ }
356
+
357
+ /* Set custom headers */
358
+ struct curl_slist *headers_list = NULL;
359
+ if (!NIL_P(headers)) {
360
+ Check_Type(headers, T_HASH);
361
+ VALUE keys = rb_funcall(headers, rb_intern("keys"), 0);
362
+ long len = RARRAY_LEN(keys);
363
+ for (long i = 0; i < len; i++) {
364
+ VALUE key = rb_ary_entry(keys, i);
365
+ VALUE val = rb_hash_aref(headers, key);
366
+ VALUE key_str = RB_TYPE_P(key, T_STRING) ? key : rb_funcall(key, rb_intern("to_s"), 0);
367
+ VALUE val_str = RB_TYPE_P(val, T_STRING) ? val : rb_funcall(val, rb_intern("to_s"), 0);
368
+
369
+ char header_buf[1024];
370
+ snprintf(header_buf, sizeof(header_buf), "%s: %s",
371
+ RSTRING_PTR(key_str), RSTRING_PTR(val_str));
372
+ headers_list = curl_slist_append(headers_list, header_buf);
373
+ }
374
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_list);
375
+ }
376
+
377
+ response_buffer_t buf = {0};
378
+ buf.data = malloc(1);
379
+ buf.data[0] = '\0';
380
+ buf.size = 0;
381
+
382
+ VALUE response_headers = rb_hash_new();
383
+
384
+ curl_easy_setopt(curl, CURLOPT_URL, url_str);
385
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
386
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
387
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
388
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)response_headers);
389
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
390
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
391
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "Konpeito-HTTP/1.0");
392
+
393
+ CURLcode res = curl_easy_perform(curl);
394
+
395
+ if (headers_list) {
396
+ curl_slist_free_all(headers_list);
397
+ }
398
+
399
+ if (res != CURLE_OK) {
400
+ free(buf.data);
401
+ curl_easy_cleanup(curl);
402
+ rb_raise(rb_eRuntimeError, "HTTP request failed: %s", curl_easy_strerror(res));
403
+ return Qnil;
404
+ }
405
+
406
+ long status_code;
407
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code);
408
+
409
+ VALUE result = rb_hash_new();
410
+ rb_hash_aset(result, ID2SYM(rb_intern("status")), LONG2NUM(status_code));
411
+ rb_hash_aset(result, ID2SYM(rb_intern("body")), rb_utf8_str_new(buf.data, buf.size));
412
+ rb_hash_aset(result, ID2SYM(rb_intern("headers")), response_headers);
413
+
414
+ free(buf.data);
415
+ curl_easy_cleanup(curl);
416
+
417
+ return result;
418
+ }
419
+
420
+ /* Module initialization */
421
+ void Init_konpeito_http(void) {
422
+ /* Global curl initialization */
423
+ curl_global_init(CURL_GLOBAL_ALL);
424
+
425
+ VALUE mKonpeitoHTTP = rb_define_module("KonpeitoHTTP");
426
+
427
+ /* Simple methods (return body only) */
428
+ rb_define_module_function(mKonpeitoHTTP, "get", konpeito_http_get, 1);
429
+ rb_define_module_function(mKonpeitoHTTP, "post", konpeito_http_post, 2);
430
+
431
+ /* Full response methods (return Hash with status, body, headers) */
432
+ rb_define_module_function(mKonpeitoHTTP, "get_response", konpeito_http_get_response, 1);
433
+ rb_define_module_function(mKonpeitoHTTP, "post_response", konpeito_http_post_response, 3);
434
+
435
+ /* Generic request method */
436
+ rb_define_module_function(mKonpeitoHTTP, "request", konpeito_http_request, 4);
437
+
438
+ /* Register cleanup at exit */
439
+ rb_set_end_proc((void (*)(VALUE))curl_global_cleanup, Qnil);
440
+ }
@@ -0,0 +1,17 @@
1
+ # extconf.rb for konpeito_json
2
+ require 'mkmf'
3
+
4
+ # yyjson source location
5
+ yyjson_dir = File.expand_path('../../../../vendor/yyjson', __dir__)
6
+
7
+ # Add yyjson to include path
8
+ $INCFLAGS << " -I#{yyjson_dir}"
9
+
10
+ # Add yyjson.c to sources
11
+ $srcs = ['json_native.c', File.join(yyjson_dir, 'yyjson.c')]
12
+ $VPATH << yyjson_dir
13
+
14
+ # Optimization flags
15
+ $CFLAGS << ' -O3'
16
+
17
+ create_makefile('konpeito_json')
@@ -0,0 +1,44 @@
1
+ # KonpeitoJSON - Fast JSON parsing using yyjson
2
+ #
3
+ # This module provides fast JSON parsing and generation using the yyjson C library.
4
+ # It is implemented as a C extension for maximum performance.
5
+ #
6
+ # Usage:
7
+ # require 'konpeito/stdlib/json'
8
+ #
9
+ # # Parse JSON
10
+ # obj = KonpeitoJSON.parse('{"name": "Alice", "age": 30}')
11
+ # # => {"name" => "Alice", "age" => 30}
12
+ #
13
+ # # Generate JSON
14
+ # json = KonpeitoJSON.generate({name: "Bob", active: true})
15
+ # # => '{"name":"Bob","active":true}'
16
+ #
17
+ # # Pretty print
18
+ # json = KonpeitoJSON.generate_pretty({a: 1, b: 2}, 2)
19
+
20
+ # Try to load the native extension
21
+ begin
22
+ require_relative 'konpeito_json'
23
+ rescue LoadError
24
+ # Fallback to Ruby's JSON if native extension is not available
25
+ require 'json'
26
+
27
+ module KonpeitoJSON
28
+ ALLOW_COMMENTS = 1 << 3
29
+ ALLOW_TRAILING_COMMAS = 1 << 2
30
+ ALLOW_INF_NAN = 1 << 4
31
+
32
+ def self.parse(json_string)
33
+ JSON.parse(json_string)
34
+ end
35
+
36
+ def self.generate(obj)
37
+ JSON.generate(obj)
38
+ end
39
+
40
+ def self.generate_pretty(obj, indent = 2)
41
+ JSON.pretty_generate(obj)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ # KonpeitoJSON - Fast JSON parsing using yyjson
2
+ #
3
+ # Usage:
4
+ # obj = KonpeitoJSON.parse('{"name": "Alice", "age": 30}')
5
+ # json = KonpeitoJSON.generate(obj)
6
+
7
+ module KonpeitoJSON
8
+ # Parse JSON string to Ruby object
9
+ # @param json_string [String] JSON string to parse
10
+ # @return [untyped] Ruby object (Hash, Array, String, Integer, Float, true, false, nil)
11
+ # @raise [ArgumentError] if JSON is invalid
12
+ def self.parse: (String json_string) -> untyped
13
+
14
+ # Generate JSON string from Ruby object
15
+ # @param obj [untyped] Ruby object to convert
16
+ # @return [String] JSON string
17
+ def self.generate: (untyped obj) -> String
18
+
19
+ # Generate pretty-printed JSON string from Ruby object
20
+ # @param obj [untyped] Ruby object to convert
21
+ # @param indent [Integer] indentation spaces (note: yyjson uses fixed 4-space indent)
22
+ # @return [String] pretty-printed JSON string
23
+ def self.generate_pretty: (untyped obj, Integer indent) -> String
24
+
25
+ # Parse flag: Allow JavaScript-style comments
26
+ ALLOW_COMMENTS: Integer
27
+
28
+ # Parse flag: Allow trailing commas in arrays and objects
29
+ ALLOW_TRAILING_COMMAS: Integer
30
+
31
+ # Parse flag: Allow Infinity and NaN values
32
+ ALLOW_INF_NAN: Integer
33
+ end