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

View File

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

View File

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

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

View File

@ -1,5 +1,7 @@
#include "http_server.h"
#include "json.h"
#include "models.h"
#include "models_json.h"
#include "str_util.h"
#include <sqlite3.h>
#include <stdio.h>
@ -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) {

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 <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)
{
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);

View File

@ -5,6 +5,8 @@
#include <stddef.h>
#include <stdint.h>
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