comparison postdog/main.c @ 112:d6d578b49a19

[PostDog] Got CRUD working.
author June Park <parkjune1995@gmail.com>
date Sun, 04 Jan 2026 11:38:44 -0800
parents 48f260576059
children e2a73e64e8e6
comparison
equal deleted inserted replaced
111:48f260576059 112:d6d578b49a19
12 #include "third_party/raylib/include/raygui.h" 12 #include "third_party/raylib/include/raygui.h"
13 13
14 #define SCREEN_WIDTH 1280 14 #define SCREEN_WIDTH 1280
15 #define SCREEN_HEIGHT 780 15 #define SCREEN_HEIGHT 780
16 #define TEXT_SIZE 10 16 #define TEXT_SIZE 10
17 #define LINE_HEIGHT 15
18 #define GENERIC_PADDING 15
19
20 #define SIDEBAR_WIDTH 200
21 #define SIDEBAR_PADDING_GENERAL 5
22 #define SIDEBAR_AREA_PADDING_X 10
23 #define SIDEBAR_AREA_PADDING_Y 10
24 #define SIDEBAR_REFERSH_BUTTON_WIDTH 90
25 #define SIDEBAR_REFERSH_BUTTON_HEIGHT 20
26 #define SIDEBAR_HISTORY_ITEM_HEIGHT 45
27
28 #define URL_INPUT_HEIGHT 40
29
30 #define TAB_LEN 3
31 #define METHOD_BUTTON_WIDTH 100
32 #define METHOD_BUTTON_HEIGHT 40
33 #define SEND_BUTTON_WIDTH 100
34 #define SEND_BUTTON_HEIGHT 40
35 17
36 #define JSON_INPUT_BUFFER_LEN 8192 18 #define JSON_INPUT_BUFFER_LEN 8192
37 #define HEADER_INPUT_BUFFER_LEN 4096 19 #define HEADER_INPUT_BUFFER_LEN 4096
38 #define PARAM_INPUT_BUFFER_LEN 4096 20 #define PARAM_INPUT_BUFFER_LEN 4096
39 #define MAX_HISTORY_ITEMS 100 21 #define MAX_HISTORY_ITEMS 100
40 22
41 // Structure to hold response data 23 #define HEADER_BUFFER_LENGTH 1024 * 4
24 #define DEFAULT_TEXT_BUFFER_LENGTH 1024 * 4
25 #define URL_TEXT_BUFFER 1024 * 10
26 #define BODY_BUFFER_LENGTH 1024 * 1025 * 5
27 #define RESULT_BUFFER_LENGTH 1024 * 1025 * 5
28 #define AREANA_BUFFER_LENGTH 1024 * 1025 * 15
29
30 typedef Dowa_KV(char*, char*) INPUT_HASHMAP;
31
42 typedef struct { 32 typedef struct {
43 char *data; 33 char *data;
44 size_t size; 34 size_t size;
45 } ResponseBuffer; 35 } ResponseBuffer;
46 36
47 // Structure to hold history item
48 typedef struct { 37 typedef struct {
49 char filename[256]; 38 char filename[256];
50 char displayName[128]; 39 char displayName[128];
51 char method[16]; 40 char method[16];
52 time_t timestamp; 41 time_t timestamp;
53 } HistoryItem; 42 } HistoryItem;
54 43
55 typedef enum { 44 char *PostDog_Enum_To_String(int active_enum)
56 ActiveTab_JSON = 0, 45 {
57 ActiveTab_Headers, 46 switch(active_enum)
58 ActiveTab_Params, 47 {
59 } ActiveTab; 48 case 0: return "GET";
49 case 1: return "POST";
50 case 2: return "PUT";
51 case 3: return "DELETE";
52 }
53 }
60 54
61 static size_t Postdog_Curl_Callback(void *contents, size_t size, size_t nmemb, void *userp) 55 static size_t Postdog_Curl_Callback(void *contents, size_t size, size_t nmemb, void *userp)
62 { 56 {
63 size_t real_size = size * nmemb; 57 size_t real_size = size * nmemb;
64 ResponseBuffer *buf = (ResponseBuffer *)userp; 58 ResponseBuffer *buf = (ResponseBuffer *)userp;
76 buf->data[buf->size] = 0; 70 buf->data[buf->size] = 0;
77 71
78 return real_size; 72 return real_size;
79 } 73 }
80 74
81 int PostDog_Make_HttpRequest(const char *url, const char *method, const char *headers, 75 int PostDog_Make_HttpRequest(
82 const char *body, char *response, size_t responseSize) 76 const char *url,
77 const char *method,
78 const char *headers,
79 const char *body,
80 char *response, size_t responseSize)
83 { 81 {
84 CURL *curl; 82 CURL *curl;
85 CURLcode res; 83 CURLcode res;
86 ResponseBuffer buffer = { .data = malloc(1), .size = 0 }; 84 ResponseBuffer buffer = { .data = malloc(1), .size = 0 };
85
86 response[0] = '\n';
87 87
88 if (buffer.data == NULL) 88 if (buffer.data == NULL)
89 { 89 {
90 snprintf(response, responseSize, "Error: Failed to allocate memory"); 90 snprintf(response, responseSize, "Error: Failed to allocate memory");
91 return -1; 91 return -1;
178 } 178 }
179 179
180 typedef struct { 180 typedef struct {
181 Rectangle rectangle; 181 Rectangle rectangle;
182 char *label; 182 char *label;
183 bool active;
183 } TabItem; 184 } TabItem;
185
186 typedef enum {
187 TAB_HEADER = 0,
188 TAB_BODY,
189 TAB_GET_PARAMS,
190 TAB_BAR,
191 } PostDog_Tab_Enum;
192
193 void PostDog_Update_URL(char **p_url_input_text, char* get_params)
194 {
195 char *url_input_text = *p_url_input_text;
196
197 // Reset
198 char *question_mark = strchr(url_input_text, '?');
199 if (question_mark)
200 *question_mark = '\0';
201
202 int get_params_length = (int)strlen(get_params) ;
203 if (get_params_length == 0)
204 return;
205
206 char *separator = "?";
207
208 Dowa_Arena *arena = Dowa_Arena_Create(1024*1024);
209 char **lines = Dowa_String_Split(get_params, "\n", get_params_length, 1, arena);
210
211 size_t url_len = strlen(url_input_text);
212 size_t url_capacity = URL_TEXT_BUFFER;
213
214 for (int i = 0; i < Dowa_Array_Length(lines); i++)
215 {
216 char *line = lines[i];
217 char **key_value = Dowa_String_Split(line, " ", (int)strlen(line), 1, arena);
218
219 if (Dowa_Array_Length(key_value) < 2)
220 continue; // Skip this line, not break entire loop
221
222 // Check buffer capacity before each append
223 size_t needed = url_len + strlen(separator) + strlen(key_value[0]) + 1 + 10; // +10 for "=" and safety
224 if (needed >= url_capacity) break;
225
226 strcat(url_input_text, separator);
227 strcat(url_input_text, key_value[0]);
228 strcat(url_input_text, "=");
229 url_len = strlen(url_input_text);
230
231 for (int i = 1; i < Dowa_Array_Length(key_value); i++)
232 {
233 if (!key_value[i] || key_value[i][0] == '\0')
234 break;
235
236 size_t value_len = strlen(key_value[i]);
237 needed = url_len + value_len + 4; // +4 for "%20" if needed
238 if (needed >= url_capacity) break;
239
240 printf("\n\n------\n\n");
241 printf("key_value[%i]: %s, p = %p\n", i, key_value[i], key_value[i]);
242 printf("\n\n------\n\n");
243
244 if (i > 1) strcat(url_input_text, "%20");
245 strcat(url_input_text, key_value[i]);
246 url_len = strlen(url_input_text);
247 }
248 separator = "&";
249 }
250
251 Dowa_Arena_Free(arena);
252 }
184 253
185 int main() 254 int main()
186 { 255 {
187 // -- initizlied --// 256 // -- initizlied --//
188 InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "PostDog"); 257 InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "PostDog");
189 SetWindowState(FLAG_WINDOW_RESIZABLE); 258 SetWindowState(FLAG_WINDOW_RESIZABLE);
190 SetTargetFPS(60); 259 SetTargetFPS(60);
191 260
192 Dowa_Arena *arena = Dowa_Arena_Create(1024 * 1025 * 10); 261 Dowa_Arena *arena = Dowa_Arena_Create(AREANA_BUFFER_LENGTH);
193 262
194 // -- Starting pos ---// 263 // -- Starting pos ---//
195 float side_bar_x = 10; 264 float side_bar_x = 10;
196 float side_bar_y = 10; 265 float side_bar_y = 10;
197 266
198 Rectangle history_sidebar = { .x = side_bar_x, .y = side_bar_y, .width = 0, .height = 0 }; 267 Rectangle history_sidebar = { .x = side_bar_x, .y = side_bar_y, .width = 0, .height = 0 };
199 268
200 Rectangle url_area = { 0 }; 269 Rectangle url_area = { 0 };
201 Rectangle textBounds = { 0 }; 270 Rectangle textBounds = { 0 };
202 Rectangle url_input_bounds = { 0 }; 271 Rectangle url_input_bounds = { 0 };
272 bool url_input_bool = false;
203 Rectangle url_text_bounds = { 0 }; 273 Rectangle url_text_bounds = { 0 };
204 Rectangle url_enter_button = { 0 }; 274 Rectangle url_enter_button = { 0 };
205 275 Rectangle method_dropdown = { 0 };
206 char *input_text = (char *)Dowa_Arena_Allocate(arena, 1024 * 4); 276
277 char *url_input_text = (char *)Dowa_Arena_Allocate(arena, URL_TEXT_BUFFER);
278 snprintf(url_input_text, URL_TEXT_BUFFER, "https://httpbin.org/get");
279
280 INPUT_HASHMAP *url_body_map = NULL;
281 Dowa_HashMap_Push_Arena(url_body_map, "Header", (char *)Dowa_Arena_Allocate(arena, HEADER_BUFFER_LENGTH), arena);
282 Dowa_HashMap_Push_Arena(url_body_map, "Body", (char *)Dowa_Arena_Allocate(arena, BODY_BUFFER_LENGTH), arena);
283 Dowa_HashMap_Push_Arena(url_body_map, "Get Param", (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena);
284 Dowa_HashMap_Push_Arena(url_body_map, "Bar", (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena);
285
286 snprintf(url_body_map[0].value, HEADER_BUFFER_LENGTH, "Content-Type: application/json");
287 snprintf(url_body_map[1].value, HEADER_BUFFER_LENGTH, "");
288
289 char *url_result_text = (char *)Dowa_Arena_Allocate(arena, RESULT_BUFFER_LENGTH);
290
291 int active_method_dropdown = 0;
292 bool method_edit = false;
293
207 int sendRequest; 294 int sendRequest;
208 295
296 // -- input --//
209 Rectangle input_area = { 0 }; 297 Rectangle input_area = { 0 };
210 Rectangle input_tab = { 0 }; 298 Rectangle input_tab = { 0 };
211 TabItem input_tab_items[4] = { 299 Rectangle input_tab_item = { 0 };
212 {
213 .rectangle={0}, .label="body"
214 },
215 {
216 .rectangle={0}, .label="header"
217 },
218 {
219 .rectangle={0}, .label="foo"
220 },
221 {
222 .rectangle={0}, .label="bar"
223 },
224 };
225 Rectangle input_body = { 0 }; 300 Rectangle input_body = { 0 };
226 301 bool input_body_bool = false;
302
303 // -- result --//
227 Rectangle result_area = { 0 }; 304 Rectangle result_area = { 0 };
228 Rectangle result_body = { 0 }; 305 Rectangle result_body = { 0 };
229 306
230 // General styling. 307 // General styling.
231 int padding = 10; // TODO make it % based? 308 int padding = 10; // TODO make it % based?
232 309 int active_input_tab = 0;
233 310
234 while (!WindowShouldClose()) 311 while (!WindowShouldClose())
235 { 312 {
236 int screen_width = GetScreenWidth(); 313 int screen_width = GetScreenWidth();
237 int screen_height = GetScreenHeight(); 314 int screen_height = GetScreenHeight();
258 url_enter_button.x = url_input_bounds.x + url_input_bounds.width + padding; 335 url_enter_button.x = url_input_bounds.x + url_input_bounds.width + padding;
259 url_enter_button.y = (url_area.height) / 2; 336 url_enter_button.y = (url_area.height) / 2;
260 url_enter_button.width = (url_area.width - padding) * 0.1; 337 url_enter_button.width = (url_area.width - padding) * 0.1;
261 url_enter_button.height = TEXT_SIZE * 2; 338 url_enter_button.height = TEXT_SIZE * 2;
262 339
340 method_dropdown.x = url_enter_button.x + url_enter_button.width + padding;
341 method_dropdown.y = (url_area.height) / 2;
342 method_dropdown.width = (url_area.width - padding) * 0.1;
343 method_dropdown.height = TEXT_SIZE * 2;
344
263 // -- Input Area --// 345 // -- Input Area --//
264 input_area.x = (side_bar_x + history_sidebar.width); 346 input_area.x = (side_bar_x + history_sidebar.width);
265 input_area.y = (side_bar_y + url_area.height); 347 input_area.y = (side_bar_y + url_area.height);
266 input_area.width = url_area.width * 0.5; 348 input_area.width = url_area.width * 0.5;
267 input_area.height = screen_height - url_area.height; 349 input_area.height = screen_height - url_area.height;
268 350
269 input_tab.x = (side_bar_x + history_sidebar.width) + padding; 351 input_tab.x = (side_bar_x + history_sidebar.width) + padding;
270 input_tab.y = (side_bar_y + url_area.height) + padding; 352 input_tab.y = (side_bar_y + url_area.height) + padding;
271 input_tab.width = url_area.width * 0.49 - padding; 353 input_tab.width = url_area.width * 0.49 - padding;
272 input_tab.height = input_area.height * 0.1; 354 input_tab.height = input_area.height * 0.1;
273 for (int pos = 0; pos < 4; pos++) 355 input_tab_item = input_tab;
274 { 356 input_tab_item.width = input_tab.width / 4;
275 input_tab_items[pos].rectangle = input_tab;
276 input_tab_items[pos].rectangle.x = input_tab.x + (pos * input_tab.width / 4);
277 input_tab_items[pos].rectangle.width = input_tab.width / 4;
278 }
279 357
280 input_body.x = input_tab.x; 358 input_body.x = input_tab.x;
281 input_body.y = input_tab.y + input_tab.height; 359 input_body.y = input_tab.y + input_tab.height;
282 input_body.width = url_area.width * 0.49 - padding; 360 input_body.width = url_area.width * 0.49 - padding;
283 input_body.height = input_area.height - input_tab.height - padding; 361 input_body.height = input_area.height - input_tab.height - padding;
300 DrawRectangleRec(history_sidebar, Fade(GRAY, 0.1f)); 378 DrawRectangleRec(history_sidebar, Fade(GRAY, 0.1f));
301 379
302 // URL area Rect 380 // URL area Rect
303 GuiDrawText("URL: ", url_text_bounds, TEXT_ALIGN_CENTER, RED); 381 GuiDrawText("URL: ", url_text_bounds, TEXT_ALIGN_CENTER, RED);
304 DrawRectangleRec(url_area, Fade(RED, 0.1f)); 382 DrawRectangleRec(url_area, Fade(RED, 0.1f));
305 GuiTextBox(url_input_bounds, input_text, 1024 * 4, true); 383 if (GuiTextBox(url_input_bounds, url_input_text, DEFAULT_TEXT_BUFFER_LENGTH, url_input_bool))
384 url_input_bool = !url_input_bool;
306 sendRequest = GuiButton(url_enter_button, "ENTER"); 385 sendRequest = GuiButton(url_enter_button, "ENTER");
386 if (sendRequest)
387 PostDog_Make_HttpRequest(
388 url_input_text,
389 PostDog_Enum_To_String(active_method_dropdown),
390 url_body_map[0].value,
391 url_body_map[1].value,
392 url_result_text,
393 RESULT_BUFFER_LENGTH
394 );
395 if (GuiDropdownBox(method_dropdown, "GET;POST;PUT;DELETE", &active_method_dropdown, method_edit))
396 method_edit = !method_edit;
307 397
308 // Input Tabs Rect 398 // Input Tabs Rect
309 DrawRectangleRec(input_area, Fade(BLUE, 0.1f)); 399 DrawRectangleRec(input_area, Fade(BLUE, 0.1f));
310 DrawRectangleRec(input_tab, Fade(DARKBLUE, 0.1f)); 400 DrawRectangleRec(input_tab, Fade(DARKBLUE, 0.1f));
311 for (int pos = 0; pos < 4; pos++) 401 GuiSetStyle(TOGGLE, GROUP_PADDING, 0);
312 { 402 if (JUNE_GuiTextBox(input_body, url_body_map[active_input_tab].value, DEFAULT_TEXT_BUFFER_LENGTH, input_body_bool))
313 JUNE_GuiButton(input_tab_items[pos].rectangle, input_tab_items[pos].label, 0, Fade(DARKBLUE, 0.1f)); 403 input_body_bool = !input_body_bool;
314 } 404 GuiToggleGroup(input_tab_item, "Header;Body;Get Param;Bar", &active_input_tab);
315 JUNE_GuiTextBox(input_body, input_text, 1024 * 4, true); 405 PostDog_Update_URL(&url_input_text, url_body_map[TAB_GET_PARAMS].value);
316 406
317 // Result Rect 407 // Result Rect
318 DrawRectangleRec(result_area, Fade(GREEN, 0.1f)); 408 DrawRectangleRec(result_area, Fade(GREEN, 0.1f));
319 DrawRectangleRec(result_body, Fade(DARKGREEN, 0.1f)); 409 DrawRectangleRec(result_body, Fade(DARKGREEN, 0.1f));
410 GuiTextBoxMulti(result_body, url_result_text, RESULT_BUFFER_LENGTH, false);
320 EndDrawing(); 411 EndDrawing();
321 } 412 }
322 CloseWindow(); 413 CloseWindow();
323 return 0; 414 return 0;
324 } 415 }