From a82af5d6f6d70047e15527921200c3c3bfef590f Mon Sep 17 00:00:00 2001 From: SimonFJ20 Date: Wed, 5 Mar 2025 13:28:01 +0100 Subject: [PATCH] add json --- backend/Makefile | 10 +- backend/prepare.sql | 16 +++- backend/src/json.c | 16 +++- backend/src/json.h | 3 +- backend/src/main.c | 35 ++++++- backend/src/models.c | 183 ++++++++++++++++++++++++++++++++++++ backend/src/models.h | 48 ++++++++++ backend/src/models_json.h | 13 +++ backend/src/models_sqlite.h | 16 ++++ backend/src/str_util.c | 20 ++++ backend/src/str_util.h | 5 + 11 files changed, 354 insertions(+), 11 deletions(-) create mode 100644 backend/src/models.c create mode 100644 backend/src/models.h create mode 100644 backend/src/models_json.h create mode 100644 backend/src/models_sqlite.h diff --git a/backend/Makefile b/backend/Makefile index 6b1a68b..bebaebb 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -38,16 +38,18 @@ O_FILES = $(patsubst src/%.c,build/%.o,$(C_FILES)) CC = gcc -all: build_dir server +all: server server: $(O_FILES) $(CC) -o build/$@ $^ $(F_FLAGS) $(OPTIMIZATION) $(L_FLAGS) build/%.o: src/%.c $(HEADERS) + mkdir -p $(@D) $(CC) $< -c -o $@ $(C_FLAGS) $(OPTIMIZATION) $(F_FLAGS) -build_dir: - mkdir -p build/ - clean: rm -rf build/ + +drop_database: + rm -rf database.db && sqlite3 database.db < prepare.sql + diff --git a/backend/prepare.sql b/backend/prepare.sql index 0422ab9..b911ea3 100644 --- a/backend/prepare.sql +++ b/backend/prepare.sql @@ -6,22 +6,35 @@ CREATE TABLE IF NOT EXISTS users ( balance_dkk_cent INTEGER NOT NULL ); +CREATE TABLE IF NOT EXISTS coords ( + id INTEGER PRIMARY KEY, + x INTEGER NOT NULL, + y INTEGER NOT NULL +); + CREATE TABLE IF NOT EXISTS products ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, - price_dkk_cent INTEGER NOT NULL + price_dkk_cent INTEGER NOT NULL, + description TEXT NOT NULL, + coord INTEGER, + barcode TEXT, + + FOREIGN KEY(coord) REFERENCES coords(id) ); CREATE TABLE IF NOT EXISTS product_prices ( id INTEGER PRIMARY KEY, product INTEGER NOT NULL, price_dkk_cent INTEGER NOT NULL, + FOREIGN KEY(product) REFERENCES products(id) ); CREATE TABLE IF NOT EXISTS carts ( id INTEGER PRIMARY KEY, user INTEGER NOT NULL, + FOREIGN KEY(user) REFERENCES users(id) ); @@ -29,6 +42,7 @@ CREATE TABLE IF NOT EXISTS cart_items ( id INTEGER PRIMARY KEY, cart INTEGER NOT NULL, amount INTEGER NOT NULL, + FOREIGN KEY(cart) REFERENCES carts(id) ); diff --git a/backend/src/json.c b/backend/src/json.c index 08f1ec7..9b382d2 100644 --- a/backend/src/json.c +++ b/backend/src/json.c @@ -81,7 +81,7 @@ const JsonValue* json_object_get(const JsonValue* value, const char* key) return NULL; } -void json_value_free(JsonValue* value) +void json_free(JsonValue* value) { switch (value->type) { case JsonType_Error: @@ -94,18 +94,28 @@ void json_value_free(JsonValue* value) break; case JsonType_Array: for (size_t i = 0; i < value->arr_val.size; ++i) { - json_value_free(value->arr_val.data[i]); + json_free(value->arr_val.data[i]); } arr_destroy(&value->arr_val); break; case JsonType_Object: for (size_t i = 0; i < value->obj_val.size; ++i) { free(value->obj_val.data[i].key); - json_value_free(value->obj_val.data[i].val); + json_free(value->obj_val.data[i].val); } obj_destroy(&value->obj_val); break; } + free(value); +} + +JsonValue* json_parse(const char* text, size_t text_len) +{ + JsonParser p; + json_parser_construct(&p, text, text_len); + JsonValue* json = json_parser_parse(&p); + json_parser_destroy(&p); + return json; } #define TOK_EOF '\0' diff --git a/backend/src/json.h b/backend/src/json.h index e6589dc..25f839e 100644 --- a/backend/src/json.h +++ b/backend/src/json.h @@ -26,7 +26,8 @@ const JsonValue* json_array_get(const JsonValue* value, size_t idx); bool json_object_has(const JsonValue* value, const char* key); const JsonValue* json_object_get(const JsonValue* value, const char* key); -void json_value_free(JsonValue* value); +void json_free(JsonValue* value); +JsonValue* json_parse(const char* text, size_t text_len); typedef struct { const char* text; diff --git a/backend/src/main.c b/backend/src/main.c index bf8c7f3..132b191 100644 --- a/backend/src/main.c +++ b/backend/src/main.c @@ -1,5 +1,7 @@ #include "http_server.h" #include "json.h" +#include "models.h" +#include "models_json.h" #include "str_util.h" #include #include @@ -61,7 +63,7 @@ void route_post_set_number(HttpCtx* ctx) RESPOND_JSON(ctx, 200, "{\"ok\": true}\r\n"); l0_return: - json_value_free(body); + json_free(body); } static inline void insert_test_user(sqlite3* db) @@ -79,7 +81,7 @@ static inline void insert_test_user(sqlite3* db) sqlite3_bind_text(stmt, 1, email, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, password_hash_str, -1, SQLITE_STATIC); - sqlite3_bind_int(stmt, 3, 123); + sqlite3_bind_int64(stmt, 3, 123); int res = sqlite3_step(stmt); if (res != SQLITE_DONE) { @@ -94,6 +96,35 @@ HttpServer* server; int main(void) { + + User user = { + .id = 12, + .email = str_dup("test@mail.dk"), + .password_hash = str_dup("hawd"), + .balance_dkk_cent = 321, + }; + + char* str = user_to_json_string(&user); + printf("user = '%s'\n", str); + + User user2; + + JsonValue* json = json_parse(str, strlen(str)); + + user_from_json(&user2, json); + + char* str2 = user_to_json_string(&user2); + printf("user2 = '%s'\n", str2); + + user_free(&user); + user_free(&user2); + + json_free(json); + free(str); + free(str2); + + return 0; + sqlite3* db; int res = sqlite3_open("database.db", &db); if (res != SQLITE_OK) { diff --git a/backend/src/models.c b/backend/src/models.c new file mode 100644 index 0000000..7563281 --- /dev/null +++ b/backend/src/models.c @@ -0,0 +1,183 @@ +#include "models.h" +#include "json.h" +#include "str_util.h" +#include +#include + +void user_free(User* m) +{ + free(m->email); + free(m->password_hash); +} + +void coord_free(Coord* m) +{ + (void)m; +} + +void product_free(Product* m) +{ + free(m->name); + free(m->barcode); +} + +void product_price_free(ProductPrice* m) +{ + (void)m; +} + +void cart_free(Cart* m) +{ + (void)m; +} + +void cart_item_free(CartItem* m) +{ + (void)m; +} + +char* user_to_json_string(const User* m) +{ + String string; + string_construct(&string); + string_pushf(&string, + "{" + "\"id\":%ld," + "\"email\":\"%s\"," + "\"password_hash\":\"%s\"," + "\"balance_dkk_cent\":%ld" + "}", + m->id, m->email, m->password_hash, m->balance_dkk_cent); + + char* result = string_copy(&string); + string_destroy(&string); + return result; +} + +char* coord_to_json_string(const Coord* m) +{ + String string; + string_construct(&string); + string_pushf(&string, + "{" + "\"id\":%ld," + "\"x\":%ld," + "\"y\":%ld" + "}", + m->id, m->x, m->y); + + char* result = string_copy(&string); + string_destroy(&string); + return result; +} + +char* product_to_json_string(const Product* m) +{ + String string; + string_construct(&string); + string_pushf(&string, + "{" + "\"id\":%ld," + "\"name\":\"%s\"," + "\"price_dkk_cent\":%ld," + "\"coord_id\":%ld," + "\"barcode\":\"%s\"" + "}", + m->id, m->name, m->price_dkk_cent, m->coord_id, m->barcode); + + char* result = string_copy(&string); + string_destroy(&string); + return result; +} + +char* product_price_to_json_string(const ProductPrice* m) +{ + String string; + string_construct(&string); + string_pushf(&string, + "{" + "\"id\":%ld," + "\"product_id\":%ld," + "\"price_dkk_cent\":%ld" + "}", + m->id, m->product_id, m->price_dkk_cent); + + char* result = string_copy(&string); + string_destroy(&string); + return result; +} + +char* cart_to_json_string(const Cart* m) +{ + String string; + string_construct(&string); + string_pushf(&string, + "{" + "\"id\":%ld," + "\"user_id\":%ld" + "}", + m->id, m->user_id); + + char* result = string_copy(&string); + string_destroy(&string); + return result; +} + +char* cart_item_to_json_string(const CartItem* m) +{ + String string; + string_construct(&string); + string_pushf(&string, + "{" + "\"id\":%ld," + "\"cart_id\":%ld," + "\"amount\":%ld" + "}", + m->id, m->cart_id, m->amount); + + char* result = string_copy(&string); + string_destroy(&string); + return result; +} + +typedef struct { + const char* key; + JsonType type; +} ObjField; + +static inline bool obj_conforms( + const JsonValue* val, const ObjField* fields, size_t fields_size) +{ + if (!json_is(val, JsonType_Object)) { + return false; + } + for (size_t i = 0; i < fields_size; ++i) { + if (!json_object_has(val, fields[i].key)) { + return false; + } + if (!json_is(json_object_get(val, fields[i].key), fields[i].type)) { + return false; + } + } + return true; +} + +int user_from_json(User* m, const JsonValue* json) +{ + ObjField fields[] = { + { "id", JsonType_Number }, + { "email", JsonType_String }, + { "password_hash", JsonType_String }, + { "balance_dkk_cent", JsonType_Number }, + }; + if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0]))) + return -1; + *m = (User) { + .id = json_int(json_object_get(json, "id")), + .email = str_dup(json_string(json_object_get(json, "email"))), + .password_hash + = str_dup(json_string(json_object_get(json, "password_hash"))), + .balance_dkk_cent = json_int(json_object_get(json, "balance_dkk_cent")), + }; + return 0; +} diff --git a/backend/src/models.h b/backend/src/models.h new file mode 100644 index 0000000..5e2b33f --- /dev/null +++ b/backend/src/models.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +typedef struct { + int64_t id; + char* email; + char* password_hash; + int64_t balance_dkk_cent; +} User; + +typedef struct { + int64_t id; + int64_t x; + int64_t y; +} Coord; + +typedef struct { + int64_t id; + char* name; + int64_t price_dkk_cent; + int64_t coord_id; + char* barcode; +} Product; + +typedef struct { + int64_t id; + int64_t product_id; + int64_t price_dkk_cent; +} ProductPrice; + +typedef struct { + int64_t id; + int64_t user_id; +} Cart; + +typedef struct { + int64_t id; + int64_t cart_id; + int64_t amount; +} CartItem; + +void user_free(User* model); +void coord_free(Coord* model); +void product_free(Product* model); +void product_price_free(ProductPrice* model); +void cart_free(Cart* model); +void cart_item_free(CartItem* model); diff --git a/backend/src/models_json.h b/backend/src/models_json.h new file mode 100644 index 0000000..e804b62 --- /dev/null +++ b/backend/src/models_json.h @@ -0,0 +1,13 @@ +#include "json.h" +#include "models.h" + +#define DEFINE_MODEL_JSON(TYPE, PREFIX) \ + char* PREFIX##_to_json_string(const TYPE* model); \ + int PREFIX##_from_json(TYPE* model, const JsonValue* json); + +DEFINE_MODEL_JSON(User, user) +DEFINE_MODEL_JSON(Coord, coord) +DEFINE_MODEL_JSON(Product, product) +DEFINE_MODEL_JSON(ProductPrice, product_price) +DEFINE_MODEL_JSON(Cart, cart) +DEFINE_MODEL_JSON(CartItem, cart_item) diff --git a/backend/src/models_sqlite.h b/backend/src/models_sqlite.h new file mode 100644 index 0000000..85489a1 --- /dev/null +++ b/backend/src/models_sqlite.h @@ -0,0 +1,16 @@ +#pragma once + +#include "models.h" +#include + +int user_sqlite_from_id(User* model, sqlite3* db, int64_t id); + +int coord_sqlite_from(Coord* model, sqlite3* db); + +int product_sqlite_from(Product* model, sqlite3_stmt* stmt); + +int product_price_sqlite_from(ProductPrice* model, sqlite3_stmt* stmt); + +int cart_sqlite_from(Cart* model, sqlite3_stmt* stmt); + +int cart_item_sqlite_from(CartItem* model, sqlite3_stmt* stmt); diff --git a/backend/src/str_util.c b/backend/src/str_util.c index 8986876..b4c8965 100644 --- a/backend/src/str_util.c +++ b/backend/src/str_util.c @@ -6,6 +6,13 @@ #include #include +char* str_dup(const char* str) +{ + char* clone = calloc(strlen(str) + 1, sizeof(char)); + strcpy(clone, str); + return clone; +} + StrSplitter str_split(const char* text, size_t text_len, const char* split) { return (StrSplitter) { @@ -41,6 +48,19 @@ void string_push_str(String* string, const char* str) } } +void string_push_fmt_va(String* string, const char* fmt, ...) +{ + va_list args1; + va_start(args1, fmt); + va_list args2; + va_copy(args2, args1); + char buf[1 + vsnprintf(NULL, 0, fmt, args1)]; + va_end(args1); + vsnprintf(buf, sizeof buf, fmt, args2); + va_end(args2); + string_push_str(string, buf); +} + char* string_copy(const String* string) { char* copy = malloc(string->size + 1); diff --git a/backend/src/str_util.h b/backend/src/str_util.h index 60aa9b2..44aaacc 100644 --- a/backend/src/str_util.h +++ b/backend/src/str_util.h @@ -5,6 +5,8 @@ #include #include +char* str_dup(const char* str); + typedef struct { const char* ptr; size_t len; @@ -24,8 +26,11 @@ StrSlice strsplit_next(StrSplitter* splitter); DEFINE_VEC(char, String, string, 8) void string_push_str(String* string, const char* str); +void string_push_fmt_va(String* string, const char* fmt, ...); char* string_copy(const String* string); +#define string_pushf(STRING, ...) string_push_fmt_va(STRING, __VA_ARGS__) + DEFINE_VEC(char*, RawStrVec, rawstr_vec, 8) #define MAX_HASH_INPUT_LEN 256