Mercurial
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 } |