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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +75 -0
- data/CONTRIBUTING.md +123 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/Rakefile +11 -0
- data/bin/konpeito +6 -0
- data/konpeito.gemspec +43 -0
- data/lib/konpeito/ast/typed_ast.rb +620 -0
- data/lib/konpeito/ast/visitor.rb +78 -0
- data/lib/konpeito/cache/cache_manager.rb +230 -0
- data/lib/konpeito/cache/dependency_graph.rb +192 -0
- data/lib/konpeito/cache.rb +8 -0
- data/lib/konpeito/cli/base_command.rb +187 -0
- data/lib/konpeito/cli/build_command.rb +220 -0
- data/lib/konpeito/cli/check_command.rb +104 -0
- data/lib/konpeito/cli/config.rb +231 -0
- data/lib/konpeito/cli/deps_command.rb +128 -0
- data/lib/konpeito/cli/doctor_command.rb +340 -0
- data/lib/konpeito/cli/fmt_command.rb +199 -0
- data/lib/konpeito/cli/init_command.rb +312 -0
- data/lib/konpeito/cli/lsp_command.rb +40 -0
- data/lib/konpeito/cli/run_command.rb +150 -0
- data/lib/konpeito/cli/test_command.rb +248 -0
- data/lib/konpeito/cli/watch_command.rb +212 -0
- data/lib/konpeito/cli.rb +301 -0
- data/lib/konpeito/codegen/builtin_methods.rb +229 -0
- data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
- data/lib/konpeito/codegen/debug_info.rb +352 -0
- data/lib/konpeito/codegen/inliner.rb +486 -0
- data/lib/konpeito/codegen/jvm_backend.rb +197 -0
- data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
- data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
- data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
- data/lib/konpeito/codegen/monomorphizer.rb +359 -0
- data/lib/konpeito/codegen/profile_runtime.c +341 -0
- data/lib/konpeito/codegen/profiler.rb +99 -0
- data/lib/konpeito/compiler.rb +592 -0
- data/lib/konpeito/dependency_resolver.rb +296 -0
- data/lib/konpeito/diagnostics/collector.rb +127 -0
- data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
- data/lib/konpeito/diagnostics/renderer.rb +144 -0
- data/lib/konpeito/formatter/formatter.rb +1214 -0
- data/lib/konpeito/hir/builder.rb +7167 -0
- data/lib/konpeito/hir/nodes.rb +2465 -0
- data/lib/konpeito/lsp/document_manager.rb +820 -0
- data/lib/konpeito/lsp/server.rb +183 -0
- data/lib/konpeito/lsp/transport.rb +38 -0
- data/lib/konpeito/parser/prism_adapter.rb +65 -0
- data/lib/konpeito/platform.rb +103 -0
- data/lib/konpeito/profile/report.rb +136 -0
- data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
- data/lib/konpeito/stdlib/compression/compression.rb +72 -0
- data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
- data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
- data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
- data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
- data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
- data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
- data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
- data/lib/konpeito/stdlib/http/extconf.rb +19 -0
- data/lib/konpeito/stdlib/http/http.rb +125 -0
- data/lib/konpeito/stdlib/http/http.rbs +57 -0
- data/lib/konpeito/stdlib/http/http_native.c +440 -0
- data/lib/konpeito/stdlib/json/extconf.rb +17 -0
- data/lib/konpeito/stdlib/json/json.rb +44 -0
- data/lib/konpeito/stdlib/json/json.rbs +33 -0
- data/lib/konpeito/stdlib/json/json_native.c +286 -0
- data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
- data/lib/konpeito/stdlib/ui/ui.rb +318 -0
- data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
- data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
- data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
- data/lib/konpeito/type_checker/inferrer.rb +565 -0
- data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
- data/lib/konpeito/type_checker/type_resolver.rb +276 -0
- data/lib/konpeito/type_checker/types.rb +1434 -0
- data/lib/konpeito/type_checker/unification.rb +323 -0
- data/lib/konpeito/ui/animation/animated_state.rb +80 -0
- data/lib/konpeito/ui/animation/easing.rb +59 -0
- data/lib/konpeito/ui/animation/value_tween.rb +66 -0
- data/lib/konpeito/ui/app.rb +379 -0
- data/lib/konpeito/ui/box.rb +38 -0
- data/lib/konpeito/ui/castella.rb +70 -0
- data/lib/konpeito/ui/castella_native.rb +76 -0
- data/lib/konpeito/ui/chart/area_chart.rb +305 -0
- data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
- data/lib/konpeito/ui/chart/base_chart.rb +210 -0
- data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
- data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
- data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
- data/lib/konpeito/ui/chart/line_chart.rb +289 -0
- data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
- data/lib/konpeito/ui/chart/scales.rb +77 -0
- data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
- data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
- data/lib/konpeito/ui/column.rb +271 -0
- data/lib/konpeito/ui/core.rb +2199 -0
- data/lib/konpeito/ui/dsl.rb +443 -0
- data/lib/konpeito/ui/frame.rb +171 -0
- data/lib/konpeito/ui/frame_native.rb +494 -0
- data/lib/konpeito/ui/markdown/ast.rb +124 -0
- data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
- data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
- data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
- data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
- data/lib/konpeito/ui/markdown/parser.rb +805 -0
- data/lib/konpeito/ui/markdown/renderer.rb +639 -0
- data/lib/konpeito/ui/markdown/theme.rb +165 -0
- data/lib/konpeito/ui/render_node.rb +260 -0
- data/lib/konpeito/ui/row.rb +207 -0
- data/lib/konpeito/ui/spacer.rb +18 -0
- data/lib/konpeito/ui/style.rb +799 -0
- data/lib/konpeito/ui/theme.rb +563 -0
- data/lib/konpeito/ui/themes/material.rb +35 -0
- data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
- data/lib/konpeito/ui/widgets/button.rb +103 -0
- data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
- data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
- data/lib/konpeito/ui/widgets/container.rb +91 -0
- data/lib/konpeito/ui/widgets/data_table.rb +667 -0
- data/lib/konpeito/ui/widgets/divider.rb +29 -0
- data/lib/konpeito/ui/widgets/image.rb +105 -0
- data/lib/konpeito/ui/widgets/input.rb +485 -0
- data/lib/konpeito/ui/widgets/markdown.rb +57 -0
- data/lib/konpeito/ui/widgets/modal.rb +163 -0
- data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
- data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
- data/lib/konpeito/ui/widgets/net_image.rb +100 -0
- data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
- data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
- data/lib/konpeito/ui/widgets/slider.rb +133 -0
- data/lib/konpeito/ui/widgets/switch.rb +84 -0
- data/lib/konpeito/ui/widgets/tabs.rb +157 -0
- data/lib/konpeito/ui/widgets/text.rb +110 -0
- data/lib/konpeito/ui/widgets/tree.rb +426 -0
- data/lib/konpeito/version.rb +5 -0
- data/lib/konpeito.rb +109 -0
- data/test_native_array.rb +172 -0
- data/test_native_array_class.rb +197 -0
- data/test_native_class.rb +151 -0
- data/tools/konpeito-asm/build.sh +65 -0
- data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
- data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
- data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
- 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
|