This commit is contained in:
SimonFJ20 2025-03-05 13:28:01 +01:00
parent 86100a103a
commit a82af5d6f6
11 changed files with 354 additions and 11 deletions

View File

@ -38,16 +38,18 @@ O_FILES = $(patsubst src/%.c,build/%.o,$(C_FILES))
CC = gcc CC = gcc
all: build_dir server all: server
server: $(O_FILES) server: $(O_FILES)
$(CC) -o build/$@ $^ $(F_FLAGS) $(OPTIMIZATION) $(L_FLAGS) $(CC) -o build/$@ $^ $(F_FLAGS) $(OPTIMIZATION) $(L_FLAGS)
build/%.o: src/%.c $(HEADERS) build/%.o: src/%.c $(HEADERS)
mkdir -p $(@D)
$(CC) $< -c -o $@ $(C_FLAGS) $(OPTIMIZATION) $(F_FLAGS) $(CC) $< -c -o $@ $(C_FLAGS) $(OPTIMIZATION) $(F_FLAGS)
build_dir:
mkdir -p build/
clean: clean:
rm -rf build/ rm -rf build/
drop_database:
rm -rf database.db && sqlite3 database.db < prepare.sql

View File

@ -6,22 +6,35 @@ CREATE TABLE IF NOT EXISTS users (
balance_dkk_cent INTEGER NOT NULL 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 ( CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
name TEXT NOT NULL, 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 ( CREATE TABLE IF NOT EXISTS product_prices (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
product INTEGER NOT NULL, product INTEGER NOT NULL,
price_dkk_cent INTEGER NOT NULL, price_dkk_cent INTEGER NOT NULL,
FOREIGN KEY(product) REFERENCES products(id) FOREIGN KEY(product) REFERENCES products(id)
); );
CREATE TABLE IF NOT EXISTS carts ( CREATE TABLE IF NOT EXISTS carts (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
user INTEGER NOT NULL, user INTEGER NOT NULL,
FOREIGN KEY(user) REFERENCES users(id) FOREIGN KEY(user) REFERENCES users(id)
); );
@ -29,6 +42,7 @@ CREATE TABLE IF NOT EXISTS cart_items (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
cart INTEGER NOT NULL, cart INTEGER NOT NULL,
amount INTEGER NOT NULL, amount INTEGER NOT NULL,
FOREIGN KEY(cart) REFERENCES carts(id) FOREIGN KEY(cart) REFERENCES carts(id)
); );

View File

@ -81,7 +81,7 @@ const JsonValue* json_object_get(const JsonValue* value, const char* key)
return NULL; return NULL;
} }
void json_value_free(JsonValue* value) void json_free(JsonValue* value)
{ {
switch (value->type) { switch (value->type) {
case JsonType_Error: case JsonType_Error:
@ -94,18 +94,28 @@ void json_value_free(JsonValue* value)
break; break;
case JsonType_Array: case JsonType_Array:
for (size_t i = 0; i < value->arr_val.size; ++i) { 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); arr_destroy(&value->arr_val);
break; break;
case JsonType_Object: case JsonType_Object:
for (size_t i = 0; i < value->obj_val.size; ++i) { for (size_t i = 0; i < value->obj_val.size; ++i) {
free(value->obj_val.data[i].key); 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); obj_destroy(&value->obj_val);
break; 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' #define TOK_EOF '\0'

View File

@ -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); bool json_object_has(const JsonValue* value, const char* key);
const JsonValue* json_object_get(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 { typedef struct {
const char* text; const char* text;

View File

@ -1,5 +1,7 @@
#include "http_server.h" #include "http_server.h"
#include "json.h" #include "json.h"
#include "models.h"
#include "models_json.h"
#include "str_util.h" #include "str_util.h"
#include <sqlite3.h> #include <sqlite3.h>
#include <stdio.h> #include <stdio.h>
@ -61,7 +63,7 @@ void route_post_set_number(HttpCtx* ctx)
RESPOND_JSON(ctx, 200, "{\"ok\": true}\r\n"); RESPOND_JSON(ctx, 200, "{\"ok\": true}\r\n");
l0_return: l0_return:
json_value_free(body); json_free(body);
} }
static inline void insert_test_user(sqlite3* db) 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, 1, email, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, password_hash_str, -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); int res = sqlite3_step(stmt);
if (res != SQLITE_DONE) { if (res != SQLITE_DONE) {
@ -94,6 +96,35 @@ HttpServer* server;
int main(void) 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; sqlite3* db;
int res = sqlite3_open("database.db", &db); int res = sqlite3_open("database.db", &db);
if (res != SQLITE_OK) { if (res != SQLITE_OK) {

183
backend/src/models.c Normal file
View File

@ -0,0 +1,183 @@
#include "models.h"
#include "json.h"
#include "str_util.h"
#include <stdlib.h>
#include <string.h>
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;
}

48
backend/src/models.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include <stdint.h>
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);

13
backend/src/models_json.h Normal file
View File

@ -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)

View File

@ -0,0 +1,16 @@
#pragma once
#include "models.h"
#include <sqlite3.h>
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);

View File

@ -6,6 +6,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
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) StrSplitter str_split(const char* text, size_t text_len, const char* split)
{ {
return (StrSplitter) { 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* string_copy(const String* string)
{ {
char* copy = malloc(string->size + 1); char* copy = malloc(string->size + 1);

View File

@ -5,6 +5,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
char* str_dup(const char* str);
typedef struct { typedef struct {
const char* ptr; const char* ptr;
size_t len; size_t len;
@ -24,8 +26,11 @@ StrSlice strsplit_next(StrSplitter* splitter);
DEFINE_VEC(char, String, string, 8) DEFINE_VEC(char, String, string, 8)
void string_push_str(String* string, const char* str); 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); 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_VEC(char*, RawStrVec, rawstr_vec, 8)
#define MAX_HASH_INPUT_LEN 256 #define MAX_HASH_INPUT_LEN 256