Compare commits

...

11 Commits

62 changed files with 1302 additions and 182 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
.idea/
.vscode/
cmake-build-*
build/

10
App.cpp Normal file
View File

@ -0,0 +1,10 @@
//
// Created by selim on 01.10.22.
//
#include "App.h"
App &App::instance() {
static App app("pro.aliencat.autocat");
return app;
}

17
App.h Normal file
View File

@ -0,0 +1,17 @@
//
// Created by selim on 01.10.22.
//
#ifndef AUTOCAT_GNOME_APP_H
#define AUTOCAT_GNOME_APP_H
#include "gtkpp/Application.h"
class App: public gtkpp::Application {
public:
using gtkpp::Application::Application;
static App& instance();
};
#endif //AUTOCAT_GNOME_APP_H

View File

@ -1,17 +1,28 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.16)
project(autocat_gnome)
include(ExternalProject)
set(CMAKE_CXX_STANDARD 20)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#if(APPLE)
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-coroutines-ts")
#endif()
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -g -Og")
if(APPLE)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/opt/libsoup@2/lib/pkgconfig:/usr/local/opt/icu4c/lib/pkgconfig")
set(OPENSSL_ROOT_DIR /usr/local/opt/openssl)
set(OPENSSL_LIBRARIES /usr/local/opt/openssl/lib)
endif()
find_package(PkgConfig REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(folly REQUIRED)
find_package(concurrencpp REQUIRED)
find_package(Threads REQUIRED)
find_package(SQLite3 REQUIRED)
find_package(fmt REQUIRED)
pkg_check_modules(GTK REQUIRED gtk4)
pkg_check_modules(GLIB REQUIRED glib-2.0)
@ -64,7 +75,17 @@ add_executable(autocat_gnome main.cpp
gtkpp/MessageDialog.cpp
gtkpp/MessageDialog.h
gtkpp/Leaflet.cpp
gtkpp/Leaflet.h)
gtkpp/Leaflet.h
gtkpp/Dialog.cpp
gtkpp/Dialog.h
gui/AddNumberDialog.cpp
gui/AddNumberDialog.h
gui/custom/PlateView.h
gui/custom/PlateView.c
App.cpp
App.h
models/Vehicle.cpp
models/Vehicle.h services/Storage.cpp services/Storage.h models/Engine.cpp models/Engine.h services/IDBEntity.h models/JsonOptional.h services/IDBEntity.cpp gtkpp/ScrolledWindow.cpp gtkpp/ScrolledWindow.h gtkpp/ListView.cpp gtkpp/ListView.h gtkpp/SelectionModel.cpp gtkpp/SelectionModel.h gtkpp/ListItemFactory.cpp gtkpp/ListItemFactory.h models/VehiclesListFactory.cpp models/VehiclesListFactory.h gtkpp/Separator.cpp gtkpp/Separator.h gtkpp/Label.cpp gtkpp/Label.h)
target_link_libraries(autocat_gnome ${GTK_LIBRARIES}
${GLIB_LIBRARIES}
@ -72,7 +93,10 @@ target_link_libraries(autocat_gnome ${GTK_LIBRARIES}
${LIBADWAITA_LIBRARIES}
${LIBSIGCPP_LIBRARIES}
nlohmann_json::nlohmann_json
Folly::folly)
concurrencpp::concurrencpp
Threads::Threads
SQLite::SQLite3
fmt::fmt)
set(XML gui/MainWindow.xml)

View File

@ -5,19 +5,26 @@
#ifndef AUTOCAT_GNOME_CORO_H
#define AUTOCAT_GNOME_CORO_H
#include <coroutine>
#include <exception>
#include <iostream>
#if __cpp_lib_coroutine
#include <coroutine>
namespace corons = std;
#else
#include <experimental/coroutine>
namespace corons = std::experimental;
#endif
template<typename... Args>
struct std::coroutine_traits<void, Args...> {
struct corons::coroutine_traits<void, Args...> {
struct promise_type {
void get_return_object() noexcept {}
std::suspend_never initial_suspend() const noexcept {
corons::suspend_never initial_suspend() const noexcept {
return {};
}
std::suspend_never final_suspend() const noexcept {
corons::suspend_never final_suspend() const noexcept {
return {};
}

View File

@ -5,6 +5,8 @@
#include "GLibMainContextExecutor.h"
#include <glib.h>
/*
int callback(void* data) {
auto executor = reinterpret_cast<GLibMainContextExecutor*>(data);
executor->runFront();
@ -30,3 +32,4 @@ folly::Executor::KeepAlive<GLibMainContextExecutor> GLibMainContextExecutor::ins
static GLibMainContextExecutor instance;
return folly::getKeepAliveToken(instance);
}
*/

View File

@ -5,20 +5,16 @@
#ifndef AUTOCAT_GNOME_GLIBMAINCONTEXTEXECUTOR_H
#define AUTOCAT_GNOME_GLIBMAINCONTEXTEXECUTOR_H
#include <folly/Executor.h>
#include <queue>
#include <mutex>
class GLibMainContextExecutor: public folly::Executor {
class GLibMainContextExecutor {
private:
std::queue<folly::Func> _tasks;
//std::queue<folly::Func> _tasks;
std::mutex _mutex;
public:
static folly::Executor::KeepAlive<GLibMainContextExecutor> instance();
~GLibMainContextExecutor() override = default;
void add(folly::Func func) override;
void runFront();
//~GLibMainContextExecutor() override = default;
};

View File

@ -13,7 +13,7 @@ namespace gtkpp {
}
Application::Application(const std::string &id) {
_app = adw_application_new(id.c_str(), G_APPLICATION_FLAGS_NONE);
_app = adw_application_new(id.c_str(), G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect(_app, "activate", G_CALLBACK(activateCallback), this);
}

View File

@ -4,14 +4,22 @@
#include "Box.h"
gtkpp::Box::Box(GtkOrientation orientation, int spacing) : Widget() {
_widget = gtk_box_new(orientation, spacing);
}
namespace gtkpp {
void gtkpp::Box::append(const gtkpp::Widget& widget) {
gtk_box_append(GTK_BOX(_widget), widget.gobj());
}
Box::Box(GtkWidget *widget): Widget() {
_widget = widget;
}
Box::Box(GtkOrientation orientation, int spacing) : Widget() {
_widget = gtk_box_new(orientation, spacing);
}
void Box::append(const gtkpp::Widget& widget) {
gtk_box_append(GTK_BOX(_widget), widget.gobj());
}
void Box::append(GtkWidget *widget) {
gtk_box_append(GTK_BOX(_widget), widget);
}
void gtkpp::Box::append(GtkWidget *widget) {
gtk_box_append(GTK_BOX(_widget), widget);
}

View File

@ -11,8 +11,9 @@
namespace gtkpp {
class Box: public Widget{
class Box: public Widget {
public:
Box(GtkWidget* widget);
Box(GtkOrientation orientation, int spacing);
void append(const Widget& widget);
void append(GtkWidget* widget);

View File

@ -6,22 +6,17 @@
namespace gtkpp {
void clickedCallback(GtkButton*, void* data) {
auto button = reinterpret_cast<Button*>(data);
button->_signalClicked.emit();
}
Button::Button() : Widget() {
_widget = gtk_button_new();
g_signal_connect(_widget, "clicked", G_CALLBACK(clickedCallback), this);
setupSignals();
}
void Button::setTitle(const std::string &title) {
gtk_button_set_label(GTK_BUTTON(_widget), title.c_str());
}
void Button::onClick(const std::function<void()> &callback) {
_signalClicked.connect(callback);
void Button::setIconName(const std::string &name) {
gtk_button_set_icon_name(GTK_BUTTON(_widget), name.c_str());
}
}

View File

@ -7,21 +7,15 @@
#include "Widget.h"
#include <string>
#include <sigc++/sigc++.h>
namespace gtkpp {
class Button: public Widget {
private:
sigc::signal<void()> _signalClicked;
private:
friend void clickedCallback(GtkButton*, void* data);
public:
using Widget::Widget;
Button();
void setTitle(const std::string& title);
void onClick(const std::function<void()>& callback);
void setIconName(const std::string& name);
};
}

60
gtkpp/Dialog.cpp Normal file
View File

@ -0,0 +1,60 @@
//
// Created by selim on 17.05.2022.
//
#include "Dialog.h"
namespace gtkpp {
void responseCallback(GtkDialog*, GtkResponseType responseId, void* data) {
auto dialog = reinterpret_cast<Dialog*>(data);
dialog->_response = responseId;
dialog->_signalResponse.emit(responseId);
}
Dialog::Dialog(Window* parent) {
auto flags = static_cast<GtkDialogFlags>(GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_MODAL
| GTK_DIALOG_USE_HEADER_BAR);
auto dialog = gtk_dialog_new_with_buttons("Add plate number",
parent->gobj(),
flags,
"OK", GTK_RESPONSE_OK,
"Cancel", GTK_RESPONSE_CANCEL,
nullptr);
_window = GTK_WINDOW(dialog);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
_responseHandlerId = g_signal_connect (dialog, "response", G_CALLBACK(responseCallback), this);
}
void Dialog::onResponse(const std::function<void(GtkResponseType)> &callback) {
_signalResponse.connect(callback);
}
cc::result<GtkResponseType> Dialog::responseAsync() {
auto promise = std::make_shared<cc::result_promise<GtkResponseType>>();
auto result = promise->get_result();
_signalResponse.connect([promise](GtkResponseType response){
promise->set_result(response);
});
return result;
}
Dialog::~Dialog() {
g_signal_handler_disconnect(_window, _responseHandlerId);
}
GtkResponseType Dialog::currentResponse() const {
return _response;
}
Box Dialog::contentWidget() const {
auto contentArea = gtk_dialog_get_content_area (GTK_DIALOG(_window));
return {contentArea};
}
}

37
gtkpp/Dialog.h Normal file
View File

@ -0,0 +1,37 @@
//
// Created by selim on 17.05.2022.
//
#ifndef AUTOCAT_GNOME_DIALOG_H
#define AUTOCAT_GNOME_DIALOG_H
#include "Window.h"
#include "Box.h"
#include <sigc++/sigc++.h>
#include <concurrencpp/concurrencpp.h>
namespace cc = concurrencpp;
namespace gtkpp {
class Dialog: public Window {
private:
GtkResponseType _response = GTK_RESPONSE_NONE;
gulong _responseHandlerId;
sigc::signal<void(GtkResponseType)> _signalResponse;
friend void responseCallback(GtkDialog*, GtkResponseType responseId, void* data);
public:
void onResponse(const std::function<void(GtkResponseType)>& callback);
public:
explicit Dialog(Window* parent);
~Dialog();
cc::result<GtkResponseType> responseAsync();
GtkResponseType currentResponse() const;
Box contentWidget() const;
};
}
#endif //AUTOCAT_GNOME_DIALOG_H

View File

@ -13,7 +13,11 @@ namespace gtkpp {
Entry::Entry() : Widget() {
_widget = gtk_entry_new();
g_signal_connect(_widget, "changed", G_CALLBACK(changedCallback), this);
_changedHandlerId = g_signal_connect(_widget, "changed", G_CALLBACK(changedCallback), this);
}
Entry::~Entry() {
g_signal_handler_disconnect(_widget, _changedHandlerId);
}
void Entry::setPlaceholder(const std::string &placeholder) {

View File

@ -13,6 +13,7 @@ namespace gtkpp {
class Entry: public Widget {
private:
gulong _changedHandlerId;
sigc::signal<void()> _signalChanged;
private:
@ -20,6 +21,7 @@ namespace gtkpp {
public:
Entry();
~Entry();
void setPlaceholder(const std::string& placeholder);
void setPurpose(GtkInputPurpose purpose);
void setVisibility(bool visibility);

View File

@ -18,4 +18,12 @@ namespace gtkpp {
void HeaderBar::setTitle(const std::string& title) {
adw_header_bar_set_title_widget(ADW_HEADER_BAR(_widget), gtk_label_new(title.c_str()));
}
void HeaderBar::showEndButtons(bool show) {
adw_header_bar_set_show_end_title_buttons(ADW_HEADER_BAR(_widget), show);
}
void HeaderBar::packStart(const Widget &widget) {
adw_header_bar_pack_start(ADW_HEADER_BAR(_widget), widget.gobj());
}
}

View File

@ -15,6 +15,8 @@ namespace gtkpp {
HeaderBar();
explicit HeaderBar(const std::string& title);
void setTitle(const std::string& title);
void showEndButtons(bool show);
void packStart(const Widget& widget);
};
}

17
gtkpp/Label.cpp Normal file
View File

@ -0,0 +1,17 @@
//
// Created by selim on 18.12.22.
//
#include "Label.h"
namespace gtkpp {
Label::Label(): Widget() {
_widget = gtk_label_new("");
}
void Label::setText(const std::string& text) {
gtk_label_set_text(GTK_LABEL(_widget), text.c_str());
}
}

23
gtkpp/Label.h Normal file
View File

@ -0,0 +1,23 @@
//
// Created by selim on 18.12.22.
//
#ifndef AUTOCAT_GNOME_LABEL_H
#define AUTOCAT_GNOME_LABEL_H
#include "Widget.h"
namespace gtkpp {
class Label: public Widget {
public:
Label();
using Widget::Widget;
public:
void setText(const std::string& text);
};
}
#endif //AUTOCAT_GNOME_LABEL_H

View File

@ -11,8 +11,8 @@ namespace gtkpp {
_widget = adw_leaflet_new();
}
void Leaflet::append(Widget *widget) {
adw_leaflet_append(ADW_LEAFLET(_widget), widget->gobj());
void Leaflet::append(const Widget& widget) {
adw_leaflet_append(ADW_LEAFLET(_widget), widget.gobj());
}
}

View File

@ -12,7 +12,7 @@ namespace gtkpp {
class Leaflet: public Widget {
public:
Leaflet();
void append(Widget* widget);
void append(const Widget& widget);
};
}

32
gtkpp/ListItemFactory.cpp Normal file
View File

@ -0,0 +1,32 @@
//
// Created by selim on 13.11.22.
//
#include "ListItemFactory.h"
namespace gtkpp {
void setupListItemCallback(GtkListItemFactory* factory, GtkListItem* list_item, void* data) {
auto self = reinterpret_cast<ListItemFactory*>(data);
auto widget = self->setup();
gtk_list_item_set_child(list_item, widget);
}
void bindListItemCallback(GtkListItemFactory* factory, GtkListItem* list_item, void* data) {
auto self = reinterpret_cast<ListItemFactory*>(data);
auto widget = gtk_list_item_get_child(list_item);
auto item = gtk_list_item_get_item (list_item);
self->bind(widget);
}
ListItemFactory::ListItemFactory() {
_factory = gtk_signal_list_item_factory_new();
g_signal_connect(_factory, "setup", G_CALLBACK(setupListItemCallback), this);
g_signal_connect(_factory, "bind", G_CALLBACK(bindListItemCallback), this);
}
GtkListItemFactory *ListItemFactory::gobj() const {
return _factory;
}
}

28
gtkpp/ListItemFactory.h Normal file
View File

@ -0,0 +1,28 @@
//
// Created by selim on 13.11.22.
//
#ifndef AUTOCAT_GNOME_LISTITEMFACTORY_H
#define AUTOCAT_GNOME_LISTITEMFACTORY_H
#include "Widget.h"
#include <gtk/gtk.h>
namespace gtkpp {
class ListItemFactory {
private:
GtkListItemFactory* _factory;
public:
ListItemFactory();
[[nodiscard]] GtkListItemFactory* gobj() const;
public:
virtual GtkWidget* setup() = 0;
virtual void bind(GtkWidget* widget) = 0;
};
}
#endif //AUTOCAT_GNOME_LISTITEMFACTORY_H

13
gtkpp/ListView.cpp Normal file
View File

@ -0,0 +1,13 @@
//
// Created by selim on 13.11.22.
//
#include "ListView.h"
namespace gtkpp {
ListView::ListView(SelectionModel selectionModel, ListItemFactory* factory) {
_widget = gtk_list_view_new(selectionModel.gobj(), factory->gobj());
}
}

21
gtkpp/ListView.h Normal file
View File

@ -0,0 +1,21 @@
//
// Created by selim on 13.11.22.
//
#ifndef AUTOCAT_GNOME_LISTVIEW_H
#define AUTOCAT_GNOME_LISTVIEW_H
#include "Widget.h"
#include "SelectionModel.h"
#include "ListItemFactory.h"
namespace gtkpp {
class ListView: public Widget {
public:
ListView(SelectionModel selectionModel, ListItemFactory* factory);
};
}
#endif //AUTOCAT_GNOME_LISTVIEW_H

15
gtkpp/ScrolledWindow.cpp Normal file
View File

@ -0,0 +1,15 @@
//
// Created by selim on 13.11.22.
//
#include "ScrolledWindow.h"
#include <gtk/gtkscrolledwindow.h>
namespace gtkpp {
ScrolledWindow::ScrolledWindow() {
_widget = gtk_scrolled_window_new();
}
}

19
gtkpp/ScrolledWindow.h Normal file
View File

@ -0,0 +1,19 @@
//
// Created by selim on 13.11.22.
//
#ifndef AUTOCAT_GNOME_SCROLLEDWINDOW_H
#define AUTOCAT_GNOME_SCROLLEDWINDOW_H
#include "Widget.h"
namespace gtkpp {
class ScrolledWindow: public Widget {
public:
ScrolledWindow();
};
}
#endif //AUTOCAT_GNOME_SCROLLEDWINDOW_H

26
gtkpp/SelectionModel.cpp Normal file
View File

@ -0,0 +1,26 @@
//
// Created by selim on 13.11.22.
//
#include "SelectionModel.h"
namespace gtkpp {
SelectionModel::SelectionModel(gtkpp::Selection selection) {
const char *array[] = { "one", "two", "three", "four", nullptr };
GtkStringList *sl = gtk_string_list_new ((const char * const *) array);
switch (selection) {
case None:
_model = GTK_SELECTION_MODEL(gtk_no_selection_new(G_LIST_MODEL(sl)));
case Single:
_model = GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(sl)));
}
}
GtkSelectionModel *SelectionModel::gobj() const {
return _model;
}
}

29
gtkpp/SelectionModel.h Normal file
View File

@ -0,0 +1,29 @@
//
// Created by selim on 13.11.22.
//
#ifndef AUTOCAT_GNOME_SELECTIONMODEL_H
#define AUTOCAT_GNOME_SELECTIONMODEL_H
#include <gtk/gtk.h>
#include <gio/glistmodel.h>
namespace gtkpp {
enum Selection {
None,
Single
};
class SelectionModel {
private:
GtkSelectionModel* _model;
public:
explicit SelectionModel(Selection selection);
[[nodiscard]] GtkSelectionModel* gobj() const;
};
}
#endif //AUTOCAT_GNOME_SELECTIONMODEL_H

9
gtkpp/Separator.cpp Normal file
View File

@ -0,0 +1,9 @@
//
// Created by selim on 15.11.22.
//
#include "Separator.h"
gtkpp::Separator::Separator(GtkOrientation orientation) {
_widget = gtk_separator_new(orientation);
}

19
gtkpp/Separator.h Normal file
View File

@ -0,0 +1,19 @@
//
// Created by selim on 15.11.22.
//
#ifndef AUTOCAT_GNOME_SEPARATOR_H
#define AUTOCAT_GNOME_SEPARATOR_H
#include "Widget.h"
namespace gtkpp {
class Separator: public Widget {
public:
explicit Separator(GtkOrientation orientation);
};
}
#endif //AUTOCAT_GNOME_SEPARATOR_H

View File

@ -4,35 +4,78 @@
#include "Widget.h"
GtkWidget *gtkpp::Widget::gobj() const {
return _widget;
}
namespace gtkpp {
void gtkpp::Widget::setMargins(int margin) {
gtk_widget_set_margin_top(_widget, margin);
gtk_widget_set_margin_bottom(_widget, margin);
gtk_widget_set_margin_start(_widget, margin);
gtk_widget_set_margin_end(_widget, margin);
}
void clickedCallback(GtkButton*, void* data) {
auto widget = reinterpret_cast<Widget*>(data);
widget->_signalClicked.emit();
}
void gtkpp::Widget::setVAlign(GtkAlign align) {
gtk_widget_set_valign(_widget, align);
}
void Widget::onClick(const std::function<void()> &callback) {
_signalClicked.connect(callback);
}
void gtkpp::Widget::setVExpand(bool expand) {
gtk_widget_set_vexpand(_widget, expand);
}
Widget::Widget() {
_widget = nullptr;
}
void gtkpp::Widget::setHorizontalMargins(int margin) {
gtk_widget_set_margin_start(_widget, margin);
gtk_widget_set_margin_end(_widget, margin);
}
Widget::Widget(GtkWidget *widget) {
_widget = widget;
//g_signal_connect(_widget, "clicked", G_CALLBACK(clickedCallback), this);
}
void gtkpp::Widget::setVerticalMargins(int margin) {
gtk_widget_set_margin_top(_widget, margin);
gtk_widget_set_margin_bottom(_widget, margin);
}
Widget::Widget(GtkBuilder *builder, const char *id) {
_widget = GTK_WIDGET(gtk_builder_get_object(builder, id));
g_signal_connect(_widget, "clicked", G_CALLBACK(clickedCallback), this);
}
GtkWidget *Widget::gobj() const {
return _widget;
}
void Widget::setMargins(int margin) {
gtk_widget_set_margin_top(_widget, margin);
gtk_widget_set_margin_bottom(_widget, margin);
gtk_widget_set_margin_start(_widget, margin);
gtk_widget_set_margin_end(_widget, margin);
}
void Widget::setVAlign(GtkAlign align) {
gtk_widget_set_valign(_widget, align);
}
void Widget::setVExpand(bool expand) {
gtk_widget_set_vexpand(_widget, expand);
}
void Widget::setHExpand(bool expand) {
gtk_widget_set_hexpand(_widget, expand);
}
void Widget::setHorizontalMargins(int margin) {
gtk_widget_set_margin_start(_widget, margin);
gtk_widget_set_margin_end(_widget, margin);
}
void Widget::setVerticalMargins(int margin) {
gtk_widget_set_margin_top(_widget, margin);
gtk_widget_set_margin_bottom(_widget, margin);
}
void Widget::setEnabled(bool enabled) {
gtk_widget_set_sensitive(_widget, enabled);
}
void Widget::setupSignals() {
g_signal_connect(_widget, "clicked", G_CALLBACK(clickedCallback), this);
}
void Widget::show() {
gtk_widget_show(_widget);
}
void Widget::hide() {
gtk_widget_hide(_widget);
}
void gtkpp::Widget::setEnabled(bool enabled) {
gtk_widget_set_sensitive(_widget, enabled);
}

View File

@ -6,6 +6,7 @@
#define AUTOCAT_GNOME_WIDGET_H
#include <gtk/gtk.h>
#include <sigc++/sigc++.h>
namespace gtkpp {
@ -13,14 +14,32 @@ namespace gtkpp {
protected:
GtkWidget* _widget;
private:
sigc::signal<void()> _signalClicked;
private:
friend void clickedCallback(GtkButton*, void* data);
protected:
void setupSignals();
public:
void onClick(const std::function<void()>& callback);
public:
Widget();
explicit Widget(GtkWidget* widget);
Widget(GtkBuilder* builder, const char* id);
[[nodiscard]] GtkWidget* gobj() const;
void setMargins(int margin);
void setVerticalMargins(int margin);
void setHorizontalMargins(int margin);
void setVAlign(GtkAlign align);
void setVExpand(bool expand);
void setHExpand(bool expand);
void setEnabled(bool enabled);
void show();
void hide();
};
}

View File

@ -3,6 +3,7 @@
//
#include "Window.h"
#include "../App.h"
namespace gtkpp {
@ -10,29 +11,31 @@ namespace gtkpp {
return wnd1._window == wnd2._window;
}
Window::Window(std::shared_ptr<Application> app, bool isAppWindow) {
Window::Window() {
_window = nullptr;
_builder = nullptr;
}
Window::Window(bool isAppWindow): _isAppWindow(isAppWindow) {
if(isAppWindow) {
_window = GTK_WINDOW(adw_application_window_new(GTK_APPLICATION(app->gobj())));
auto app = GTK_APPLICATION(App::instance().gobj());
_window = GTK_WINDOW(adw_application_window_new(app));
} else {
_window = GTK_WINDOW(adw_window_new());
}
_app = app;
_builder = nullptr;
}
Window::Window(std::shared_ptr<Application> app, const char* resourceName) {
_app = app;
// _builder = gtk_builder_new();
//
// GError* error = nullptr;
// gtk_builder_add_from_resource(_builder, resourceName, &error);
// if(error) {
// throw std::runtime_error(error->message);
// }
Window::Window(const char* resourceName, const char* id) {
_builder = gtk_builder_new_from_resource(resourceName);
_window = GTK_WINDOW(gtk_builder_get_object(_builder, "adw_main_window"));
_window = GTK_WINDOW(gtk_builder_get_object(_builder, id));
}
Window::~Window() {
if(_window) {
destroy();
}
}
void Window::show() {
@ -51,12 +54,32 @@ namespace gtkpp {
gtk_window_set_default_size(GTK_WINDOW(_window), width, height);
}
std::shared_ptr<Application> Window::application() const {
return _app.lock();
}
void Window::hide() {
gtk_window_close(GTK_WINDOW(_window));
}
void Window::destroy() {
gtk_window_destroy(GTK_WINDOW(_window));
}
void Window::setTransientFor(const Window* window) {
gtk_window_set_transient_for(_window, window->gobj());
}
void Window::setModal() {
gtk_window_set_modal(_window, true);
}
void Window::setContent(const Widget& widget) {
if(_isAppWindow) {
adw_application_window_set_content(ADW_APPLICATION_WINDOW(_window), widget.gobj());
} else {
adw_window_set_content(ADW_WINDOW(_window), widget.gobj());
}
}
void Window::setResizable(bool resizable) {
gtk_window_set_resizable(_window, resizable);
}
}

View File

@ -6,6 +6,8 @@
#define AUTOCAT_GNOME_WINDOW_H
#include "Application.h"
#include "Widget.h"
#include <adwaita.h>
#include <memory>
@ -15,22 +17,29 @@ namespace gtkpp {
protected:
GtkBuilder* _builder;
GtkWindow* _window;
std::weak_ptr<Application> _app;
bool _isAppWindow;
private:
friend bool operator==(const Window& wnd1, const Window& wnd2);
public:
Window(std::shared_ptr<Application> app, bool isAppWindow);
Window(std::shared_ptr<Application> app, const char* resourceName);
Window();
explicit Window(bool isAppWindow);
Window(const char* resourceName, const char* id);
virtual ~Window();
void show();
void hide();
void destroy();
void setTitle(const std::string& title);
void setDefaultSize(int width, int height);
void setTransientFor(const Window* window);
void setModal();
[[nodiscard]] GtkWindow* gobj() const;
[[nodiscard]] std::shared_ptr<Application> application() const;
void setContent(const Widget& widget);
void setResizable(bool resizable);
};
using WindowPtr = std::shared_ptr<Window>;
}
#endif //AUTOCAT_GNOME_WINDOW_H

35
gui/AddNumberDialog.cpp Normal file
View File

@ -0,0 +1,35 @@
//
// Created by selim on 17.05.2022.
//
#include "AddNumberDialog.h"
#include "../services/Api.h"
#include "../services/Storage.h"
#include <iostream>
#include <algorithm>
#include <cctype>
AddNumberDialog::AddNumberDialog(gtkpp::Window* parent): gtkpp::Dialog(parent) {
auto content = contentWidget();
_entry.setMargins(16);
_entry.setPlaceholder("Plate number");
content.append(_entry);
content.append(_spinner);
_spinner.setMargins(16);
_spinner.hide();
}
std::string AddNumberDialog::text() const {
return _entry.text();
}
cc::result<void> AddNumberDialog::checkNumber() {
_spinner.show();
_spinner.start();
std::string text = _entry.text();
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c){ return std::toupper(c); });
auto vehicle = co_await Api::check(text);
Storage::instance().insert(&vehicle);
}

29
gui/AddNumberDialog.h Normal file
View File

@ -0,0 +1,29 @@
//
// Created by selim on 17.05.2022.
//
#ifndef AUTOCAT_GNOME_ADDNUMBERDIALOG_H
#define AUTOCAT_GNOME_ADDNUMBERDIALOG_H
#include "../gtkpp/Dialog.h"
#include "../gtkpp/Entry.h"
#include "../gtkpp/Spinner.h"
#include <concurrencpp/concurrencpp.h>
#include <vector>
namespace cc = concurrencpp;
class AddNumberDialog: public gtkpp::Dialog {
private:
gtkpp::Entry _entry;
gtkpp::Spinner _spinner;
public:
explicit AddNumberDialog(gtkpp::Window* parent);
std::string text() const;
cc::result<void> checkNumber();
};
#endif //AUTOCAT_GNOME_ADDNUMBERDIALOG_H

View File

@ -5,17 +5,14 @@
#include "LoginWindow.h"
#include "MainWindow.h"
#include "../services/Api.h"
#include "../coro/GLibMainContextExecutor.h"
#include "../gtkpp/HeaderBar.h"
#include "../gtkpp/MessageDialog.h"
#include "../App.h"
#include <iostream>
#include <utility>
#include <folly/experimental/coro/Task.h>
#include <folly/executors/IOThreadPoolExecutor.h>
LoginWindow::LoginWindow(std::shared_ptr<gtkpp::Application> app): gtkpp::Window(std::move(app), false) {
LoginWindow::LoginWindow(): gtkpp::Window(false) {
setDefaultSize(640, 480);
@ -46,7 +43,7 @@ LoginWindow::LoginWindow(std::shared_ptr<gtkpp::Application> app): gtkpp::Window
rootBox.append(gtkpp::HeaderBar("Login"));
rootBox.append(contentBox);
adw_window_set_content(ADW_WINDOW(_window), rootBox.gobj());
setContent(rootBox);
}
void LoginWindow::loginClicked() {
@ -57,14 +54,12 @@ void LoginWindow::loginClicked() {
_spinner.start();
try {
User user = co_await Api::login(email, password).scheduleOn(GLibMainContextExecutor::instance());
if(auto app = this->application()) {
auto mainWindow = std::make_shared<MainWindow>(app);
mainWindow->show();
app->addWindow(mainWindow);
app->removeWindow(this);
hide();
}
User user = co_await Api::login(email, password);
auto mainWindow = std::make_shared<MainWindow>();
mainWindow->show();
App::instance().addWindow(mainWindow);
App::instance().removeWindow(this);
hide();
} catch (std::exception& ex) {
enableControls(true);
_spinner.stop();

View File

@ -11,7 +11,6 @@
#include "../gtkpp/Entry.h"
#include "../gtkpp/Button.h"
#include "../gtkpp/Spinner.h"
#include "../gtkpp/Application.h"
class LoginWindow: public gtkpp::Window {
private:
@ -21,7 +20,7 @@ private:
gtkpp::Spinner _spinner;
public:
explicit LoginWindow(std::shared_ptr<gtkpp::Application> app);
LoginWindow();
void loginClicked();
void validateFields();

View File

@ -3,15 +3,62 @@
//
#include "MainWindow.h"
#include "../gtkpp/Box.h"
#include "AddNumberDialog.h"
#include "../gtkpp/MessageDialog.h"
#include "../coro/Coro.h"
#include "../services/Storage.h"
#include "../gtkpp/HeaderBar.h"
#include "../gtkpp/Separator.h"
#include "../gtkpp/SelectionModel.h"
MainWindow::MainWindow(std::shared_ptr<gtkpp::Application> app): gtkpp::Window(std::move(app), "/gui/MainWindow.xml") {
#include <iostream>
// gtkpp::Box rootBox(GTK_ORIENTATION_VERTICAL, 0);
// rootBox.append(gtkpp::HeaderBar("Main"));
//
// _leaflet.append(&rootBox);
//
// adw_application_window_set_content(ADW_APPLICATION_WINDOW(_window), _leaflet.gobj());
MainWindow::MainWindow(): gtkpp::Window(true),
_vehiclesListView(gtkpp::SelectionModel(gtkpp::Selection::Single),
&_vehiclesListFactory) {
setDefaultSize(640, 480);
setResizable(true);
_addNumberButton.setIconName("list-add-symbolic");
_addNumberButton.onClick([this] {
showCheckDialog();
});
gtkpp::HeaderBar leftHeader("Vehicles");
leftHeader.showEndButtons(false);
leftHeader.packStart(_addNumberButton);
gtkpp::Box leftPanel(GTK_ORIENTATION_VERTICAL, 0);
leftPanel.append(leftHeader);
leftPanel.append(_vehiclesListView);
gtkpp::HeaderBar rightHeader;
gtkpp::Box rightPanel(GTK_ORIENTATION_VERTICAL, 0);
rightPanel.append(rightHeader);
rightPanel.setHExpand(true);
_leaflet.append(leftPanel);
_leaflet.append(gtkpp::Separator(GTK_ORIENTATION_VERTICAL));
_leaflet.append(rightPanel);
_leaflet.setVExpand(true);
setContent(_leaflet);
auto vehicles = Storage::instance().select<Vehicle>();
std::cout << "" << std::endl;
}
void MainWindow::showCheckDialog() {
AddNumberDialog dialog(this);
dialog.show();
GtkResponseType response = co_await dialog.responseAsync();
if(response == GTK_RESPONSE_OK) {
try {
co_await dialog.checkNumber();
} catch(std::exception& ex) {
gtkpp::MessageDialog::showError(this, ex.what());
}
}
}

View File

@ -7,13 +7,21 @@
#include "../gtkpp/Window.h"
#include "../gtkpp/Leaflet.h"
#include "../gtkpp/Button.h"
#include "../gtkpp/ListView.h"
#include "../models/VehiclesListFactory.h"
class MainWindow: public gtkpp::Window {
private:
gtkpp::Leaflet _leaflet;
gtkpp::Button _addNumberButton;
VehiclesListFactory _vehiclesListFactory;
gtkpp::ListView _vehiclesListView;
public:
explicit MainWindow(std::shared_ptr<gtkpp::Application> app);
explicit MainWindow();
void showCheckDialog();
};

View File

@ -31,6 +31,12 @@
</object>
</property>
<child type="start">
<object class="GtkButton" id="add_number_button">
<property name="icon-name">list-add</property>
</object>
</child>
<child type="end">
<object class="GtkMenuButton" id="gtk_btnHeaderHelp">
<property name="direction">none</property>

5
gui/custom/PlateView.c Normal file
View File

@ -0,0 +1,5 @@
//
// Created by selim on 01.06.2022.
//
#include "PlateView.h"

25
gui/custom/PlateView.h Normal file
View File

@ -0,0 +1,25 @@
//
// Created by selim on 01.06.2022.
//
#ifndef AUTOCAT_GNOME_PLATEVIEW_H
#define AUTOCAT_GNOME_PLATEVIEW_H
#include <gtk/gtk.h>
#define PLATEVIEW(obj) GTK_CHECK_CAST(obj, plateview_get_type(), PlateView)
#define PLATEVIEW_CLASS(klass) GTK_CHECK_CLASS_CAST(klass, plateview_get_type(), PlateViewClass)
typedef struct {
} PlateView;
typedef struct {
GtkWidgetClass parent_class;
} PlateViewClass;
guint plateview_get_type (void);
GtkWidget* plateview_new (void);
void plateview_clear (PlateView* view);
#endif //AUTOCAT_GNOME_PLATEVIEW_H

View File

@ -3,32 +3,31 @@
#include "services/Settings.h"
#include "gtkpp/Application.h"
#include "gtkpp/Window.h"
#include "App.h"
#include <memory>
#include <folly/init/Init.h>
#include <iostream>
std::shared_ptr<gtkpp::Window> createMainWindow(std::shared_ptr<gtkpp::Application> app) {
std::shared_ptr<gtkpp::Window> createMainWindow() {
auto settings = Settings::instance();
if(settings.user().token.empty()) {
return std::make_shared<LoginWindow>(app);
return std::make_shared<LoginWindow>();
} else {
return std::make_shared<MainWindow>(app);
return std::make_shared<MainWindow>();
}
}
int main(int argc, char* argv[]) {
folly::init(&argc, &argv);
g_resources_register(g_resource_load("resources.gresource", nullptr));
auto app = std::make_shared<gtkpp::Application>("pro.aliencat.autocat");
auto app = App::instance(); //std::make_shared<gtkpp::Application>("pro.aliencat.autocat");
app->onActivate([&](){
auto window = createMainWindow(app);
app->addWindow(window);
app.onActivate([&](){
auto window = createMainWindow();
app.addWindow(window);
window->show();
});
return app->run(argc, argv);
return app.run(argc, argv);
}

5
models/Engine.cpp Normal file
View File

@ -0,0 +1,5 @@
//
// Created by selim on 06.11.22.
//
#include "Engine.h"

21
models/Engine.h Normal file
View File

@ -0,0 +1,21 @@
//
// Created by selim on 06.11.22.
//
#ifndef AUTOCAT_GNOME_ENGINE_H
#define AUTOCAT_GNOME_ENGINE_H
#include <nlohmann/json.hpp>
class Engine {
public:
std::string number;
int volume;
float powerHp;
float powerKw;
std::string fuelType;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Engine, number, volume, powerHp, powerKw, fuelType)
};
#endif //AUTOCAT_GNOME_ENGINE_H

75
models/JsonOptional.h Normal file
View File

@ -0,0 +1,75 @@
//
// Created by selim on 06.11.22.
//
#ifndef AUTOCAT_GNOME_JSONOPTIONAL_H
#define AUTOCAT_GNOME_JSONOPTIONAL_H
#include <nlohmann/json.hpp>
#include <iostream>
namespace nlohmann {
template<class J, class T>
void optional_to_json(J& j, const char* name, const std::optional<T>& value)
{
if (value)
{
j[name] = *value;
}
}
template<class J, class T>
void optional_from_json(const J& j, const char* name, std::optional<T>& value)
{
const auto it = j.find(name);
if (it != j.end())
{
if(j.at(name).is_null()) {
value = std::nullopt;
} else {
value = it->template get<T>();
}
}
else
{
value = std::nullopt;
}
}
template <typename>
constexpr bool is_optional = false;
template <typename T>
constexpr bool is_optional<std::optional<T>> = true;
template <typename T>
void extended_to_json(const char *key, nlohmann::json &j, const T &value) {
if constexpr (is_optional<T>)
optional_to_json(j, key, value);
else
j[key] = value;
}
template <typename T>
void extended_from_json(const char *key, const nlohmann::json &j, T &value) {
//std::cout << "parsing key " << key << std::endl;
if constexpr (is_optional<T>) {
optional_from_json(j, key, value);
} else {
j.at(key).get_to(value);
}
}
#define EXTEND_JSON_TO(v1) extended_to_json(#v1, nlohmann_json_j, nlohmann_json_t.v1);
#define EXTEND_JSON_FROM(v1) extended_from_json(#v1, nlohmann_json_j, nlohmann_json_t.v1);
#define NLOHMANN_JSONIFY_ALL_THINGS(Type, ...) \
friend void to_json(nlohmann::json &nlohmann_json_j, const Type &nlohmann_json_t) { \
NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(EXTEND_JSON_TO, __VA_ARGS__)) \
} \
friend void from_json(const nlohmann::json &nlohmann_json_j, Type &nlohmann_json_t) { \
NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(EXTEND_JSON_FROM, __VA_ARGS__)) \
}
}
#endif //AUTOCAT_GNOME_JSONOPTIONAL_H

View File

@ -19,6 +19,7 @@ public:
public:
User() = default;
User(const User& user) = default;
User(User&&) = default;
User(std::string_view email, std::string_view token);
NLOHMANN_DEFINE_TYPE_INTRUSIVE(User, email, token)

91
models/Vehicle.cpp Normal file
View File

@ -0,0 +1,91 @@
//
// Created by selim on 03.10.22.
//
#include <fmt/core.h>
#include "Vehicle.h"
std::string Vehicle::createTableQuery() {
return R"(
CREATE TABLE IF NOT EXISTS vehicles(
number TEXT NOT NULL PRIMARY KEY,
currentNumber TEXT,
color TEXT,
category TEXT,
year INT,
addedDate REAL,
updatedDate REAL,
vin1 TEXT,
vin2 TEXT,
sts TEXT,
pts TEXT,
addedBy TEXT,
engineNumber TEXT,
engineVolume INT,
enginePowerHp REAL,
enginePowerKw REAL,
engineFuelType TEXT
);
)";
}
std::string Vehicle::insertQuery() {
return fmt::format(R"(
INSERT INTO vehicles(number,
currentNumber,
color,
category,
year,
addedDate,
updatedDate,
vin1, vin2,
sts, pts,
addedBy,
engineNumber,
engineVolume,
enginePowerHp,
enginePowerKw,
engineFuelType)
VALUES('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}')
)",
number,
currentNumber,
color.value_or("NULL"),
category,
year,
addedDate,
updatedDate,
vin1,
vin2.value_or("NULL"),
sts, pts,
addedBy,
engine.number,
engine.volume,
engine.powerHp,
engine.powerKw,
engine.fuelType);
}
std::string Vehicle::selectQuery() {
return "SELECT * FROM vehicles";
}
Vehicle::Vehicle(sqlite3_stmt *stmt) {
number = readString(stmt, 0);
currentNumber = readString(stmt, 1);
color = readStringOptional(stmt, 2);
category = readString(stmt, 3);
year = readInt(stmt, 4);
addedDate = readDouble(stmt, 5);
updatedDate = readDouble(stmt, 6);
vin1 = readString(stmt, 7);
vin2 = readStringOptional(stmt, 8);
sts = readString(stmt, 9);
pts = readString(stmt, 10);
addedBy = readString(stmt, 11);
engine.number = readString(stmt, 12);
engine.volume = readInt(stmt, 13);
engine.powerHp = readDouble(stmt, 14);
engine.powerKw = readDouble(stmt, 15);
engine.fuelType = readString(stmt, 16);
}

42
models/Vehicle.h Normal file
View File

@ -0,0 +1,42 @@
//
// Created by selim on 03.10.22.
//
#ifndef AUTOCAT_GNOME_VEHICLE_H
#define AUTOCAT_GNOME_VEHICLE_H
#include "../services/IDBEntity.h"
#include "Engine.h"
#include "JsonOptional.h"
#include <optional>
#include <sqlite3.h>
class Vehicle: public IDBEntity {
public:
std::string number;
std::string currentNumber;
std::optional<std::string> color;
std::string category;
int year;
double addedDate;
double updatedDate;
std::string vin1;
std::optional<std::string> vin2;
std::string sts;
std::string pts;
std::string addedBy;
Engine engine;
public:
Vehicle() = default;
explicit Vehicle(sqlite3_stmt* stmt);
static std::string createTableQuery();
static std::string selectQuery();
std::string insertQuery() override;
NLOHMANN_JSONIFY_ALL_THINGS(Vehicle, number, currentNumber, color, category, year, addedDate, updatedDate, vin1, vin2, sts, pts, addedBy, engine)
};
#endif //AUTOCAT_GNOME_VEHICLE_H

View File

@ -0,0 +1,21 @@
//
// Created by selim on 13.11.22.
//
#include "VehiclesListFactory.h"
#include "../gtkpp/Label.h"
#include <iostream>
VehiclesListFactory::VehiclesListFactory() {
}
GtkWidget* VehiclesListFactory::setup() {
gtkpp::Label label;
return label.gobj();
}
void VehiclesListFactory::bind(GtkWidget *widget) {
gtkpp::Label label(widget);
label.setText("qwe");
}

View File

@ -0,0 +1,20 @@
//
// Created by selim on 13.11.22.
//
#ifndef AUTOCAT_GNOME_VEHICLESLISTFACTORY_H
#define AUTOCAT_GNOME_VEHICLESLISTFACTORY_H
#include "../gtkpp/ListItemFactory.h"
class VehiclesListFactory: public gtkpp::ListItemFactory {
public:
VehiclesListFactory();
public:
GtkWidget* setup() override;
void bind(GtkWidget* widget) override;
};
#endif //AUTOCAT_GNOME_VEHICLESLISTFACTORY_H

View File

@ -5,53 +5,45 @@
#include "Api.h"
#include "Settings.h"
#include <memory>
#include <folly/futures/Promise.h>
template<class...Args>
struct Callback {
void(*function)(Args..., void*) = nullptr;
void* state = nullptr;
};
template<typename... Args, typename Lambda>
Callback<Args...> voidify(Lambda&& l) {
using Func = typename std::decay<Lambda>::type;
auto data = new Func(std::forward<Lambda>(l));
return {
+[](Args... args, void* v)->void {
Func* f = static_cast< Func* >(v);
(*f)(std::forward<Args>(args)...);
delete f;
},
data
};
}
const std::string Api::_baseUrl = "https://vps.aliencat.pro:8443/";
const std::string Api::_baseUrl = "http://127.0.0.1:3000/"; //"https://vps.aliencat.pro:8443/";
SoupSession* Api::_session = soup_session_new();
template<typename T>
folly::Future<T> Api::post(const std::string &method, const nlohmann::json& params) {
void callback(SoupSession* session, SoupMessage* message, gpointer userData) {
auto promise = reinterpret_cast<cc::result_promise<T>*>(userData);
if(message->status_code >= 200 && message->status_code < 300) {
auto responseString = std::string(message->response_body->data, message->response_body->length);
auto json = nlohmann::json::parse(responseString);
if(json["success"].get<bool>()) {
//std::cout << "response: " << responseString << std::endl;
auto data = json["data"].get<T>();
promise->set_result(data);
} else {
auto error = json["error"].get<std::string>();
promise->set_exception(std::make_exception_ptr(std::runtime_error(error)));
}
} else {
promise->set_exception(std::make_exception_ptr(std::runtime_error(message->reason_phrase)));
}
delete promise;
}
template<typename T>
cc::result<T> Api::post(const std::string &method, const nlohmann::json& params) {
std::string url = _baseUrl + method;
auto msg = soup_message_new(SOUP_METHOD_POST, url.c_str());
auto promise = std::make_shared<folly::Promise<T>>();
auto callback = voidify<SoupSession*, SoupMessage*>([&, promise](SoupSession* session, SoupMessage* message) {
if(message->status_code >= 200 && message->status_code < 300) {
auto responseString = std::string(message->response_body->data, message->response_body->length);
auto json = nlohmann::json::parse(responseString);
if(json["success"].get<bool>()) {
//std::cout << "response: " << responseString << std::endl;
auto user = json["data"].get<T>();
promise->setValue(user);
} else {
auto error = json["error"].get<std::string>();
promise->setException(std::runtime_error(error));
}
} else {
promise->setException(std::runtime_error(message->reason_phrase));
}
});
auto token = Settings::instance().user().token;
if(!token.empty()) {
auto authHeader = "Bearer " + token;
soup_message_headers_append(msg->request_headers, "Authorization", authHeader.c_str());
}
auto promise = new cc::result_promise<T>();
auto result = promise->get_result();
auto jsonStr = params.dump();
@ -60,12 +52,12 @@ folly::Future<T> Api::post(const std::string &method, const nlohmann::json& para
jsonStr.c_str(),
jsonStr.size());
soup_session_queue_message(_session, msg, callback.function, callback.state);
soup_session_queue_message(_session, msg, callback<T>, promise);
return promise->getFuture();
return result;
}
fc::Task<User> Api::login(std::string email, std::string password) {
cc::result<User> Api::login(std::string email, std::string password) {
nlohmann::json params = {
{ "email", email },
@ -81,3 +73,12 @@ fc::Task<User> Api::login(std::string email, std::string password) {
co_return user;
}
cc::result<Vehicle> Api::check(const std::string& number, bool force) {
nlohmann::json params = {
{ "number", number },
{ "forceUpdate", force }
};
return post<Vehicle>("vehicles/check", params);
}

View File

@ -8,13 +8,13 @@
#include <string>
#include <libsoup/soup.h>
#include <nlohmann/json.hpp>
#include <folly/futures/Future.h>
#include <folly/experimental/coro/Task.h>
#include <concurrencpp/concurrencpp.h>
#include "../models/User.h"
#include "../models/Vehicle.h"
#include "../coro/Coro.h"
namespace fc = folly::coro;
namespace cc = concurrencpp;
class Api {
private:
@ -23,10 +23,11 @@ private:
private:
template<typename T>
static folly::Future<T> post(const std::string& method, const nlohmann::json& params);
static cc::result<T> post(const std::string& method, const nlohmann::json& params);
public:
static fc::Task<User> login(std::string email, std::string password);
static cc::result<User> login(std::string email, std::string password);
static cc::result<Vehicle> check(const std::string& number, bool force = false);
};

41
services/IDBEntity.cpp Normal file
View File

@ -0,0 +1,41 @@
//
// Created by selim on 09.11.22.
//
#include "IDBEntity.h"
std::string IDBEntity::readString(sqlite3_stmt *stmt, int column) {
return { reinterpret_cast<const char*>(sqlite3_column_text(stmt, column)) };
}
int IDBEntity::readInt(sqlite3_stmt *stmt, int column) {
return sqlite3_column_int(stmt, column);
}
double IDBEntity::readDouble(sqlite3_stmt *stmt, int column) {
return sqlite3_column_double(stmt, column);
}
StringOptional IDBEntity::readStringOptional(sqlite3_stmt *stmt, int column) {
if(sqlite3_column_type(stmt, column) == SQLITE_NULL) {
return std::nullopt;
} else {
return readString(stmt, column);
}
}
IntOptional IDBEntity::readIntOptional(sqlite3_stmt *stmt, int column) {
if(sqlite3_column_type(stmt, column) == SQLITE_NULL) {
return std::nullopt;
} else {
return readInt(stmt, column);
}
}
DoubleOptional IDBEntity::readDoubleOptional(sqlite3_stmt *stmt, int column) {
if(sqlite3_column_type(stmt, column) == SQLITE_NULL) {
return std::nullopt;
} else {
return readDouble(stmt, column);
}
}

31
services/IDBEntity.h Normal file
View File

@ -0,0 +1,31 @@
//
// Created by selim on 06.11.22.
//
#ifndef AUTOCAT_GNOME_IDBENTITY_H
#define AUTOCAT_GNOME_IDBENTITY_H
#include <string>
#include <optional>
#include <variant>
#include <vector>
#include <sqlite3.h>
using StringOptional = std::optional<std::string>;
using IntOptional = std::optional<int>;
using DoubleOptional = std::optional<double>;
struct IDBEntity {
public:
virtual std::string insertQuery() = 0;
protected:
static std::string readString(sqlite3_stmt* stmt, int column);
static int readInt(sqlite3_stmt* stmt, int column);
static double readDouble(sqlite3_stmt* stmt, int column);
static StringOptional readStringOptional(sqlite3_stmt* stmt, int column);
static IntOptional readIntOptional(sqlite3_stmt* stmt, int column);
static DoubleOptional readDoubleOptional(sqlite3_stmt* stmt, int column);
};
#endif //AUTOCAT_GNOME_IDBENTITY_H

View File

@ -9,17 +9,15 @@
#include <iostream>
#include <fstream>
namespace fs = std::filesystem;
fs::path getDataPath() {
fs::path Settings::getDataPath() {
auto dataDir = std::getenv("XDG_DATA_HOME");
if(dataDir) {
return fs::path(dataDir);
return fs::path(dataDir) / "autocat";
}
auto home = std::getenv("HOME");
if(home) {
return fs::path(home) / ".local" / "share";
return fs::path(home) / ".local" / "share" / "autocat";
}
throw std::runtime_error("Failed to find data home directory");
@ -46,7 +44,7 @@ Settings Settings::instance() {
}
Settings::Settings() {
_dataPath = getDataPath() / "autocat";
_dataPath = getDataPath();
if(!fs::exists(_dataPath)) {
fs::create_directories(_dataPath);
}

View File

@ -8,6 +8,8 @@
#include "../models/User.h"
#include <filesystem>
namespace fs = std::filesystem;
class Settings {
private:
std::filesystem::path _dataPath;
@ -15,6 +17,7 @@ private:
public:
static Settings instance();
Settings();
static fs::path getDataPath();
[[nodiscard]] User user() const;
void setUser(const User& user);

42
services/Storage.cpp Normal file
View File

@ -0,0 +1,42 @@
//
// Created by selim on 25.10.22.
//
#include "Storage.h"
#include "Settings.h"
#include <fmt/core.h>
Storage Storage::instance() {
static Storage instance;
return instance;
}
Storage::Storage(): _sqlite(nullptr) {
auto path = Settings::getDataPath() / "vehicles.sqlite";
int result = sqlite3_open(path.c_str(), &_sqlite);
if(result != SQLITE_OK) {
throw std::runtime_error(sqlite3_errmsg(_sqlite));
}
executeQuery(Vehicle::createTableQuery());
}
Storage::~Storage() {
if(_sqlite) {
sqlite3_close(_sqlite);
}
}
void Storage::executeQuery(const std::string& query) {
char* error = nullptr;
sqlite3_exec(_sqlite, query.c_str(), nullptr, nullptr, &error);
if(error) {
throw std::runtime_error(error);
// TODO: error buffer should be deallocated with sqlite3_free()
}
}
void Storage::insert(IDBEntity *entity) {
executeQuery(entity->insertQuery());
}

64
services/Storage.h Normal file
View File

@ -0,0 +1,64 @@
//
// Created by selim on 25.10.22.
//
#ifndef AUTOCAT_GNOME_STORAGE_H
#define AUTOCAT_GNOME_STORAGE_H
#include "../models/Vehicle.h"
#include "IDBEntity.h"
#include <utility>
#include <sqlite3.h>
class Storage {
private:
sqlite3* _sqlite;
private:
void executeQuery(const std::string& query);
public:
Storage();
~Storage();
static Storage instance();
void insert(IDBEntity* entity);
template<typename T>
std::vector<T> select() {
auto sql = T::selectQuery();
sqlite3_stmt* stmt = nullptr;
std::vector<T> rows;
int result = sqlite3_prepare_v2(_sqlite, sql.c_str(), sql.size(), &stmt, nullptr);
if(result != SQLITE_OK) {
throw std::runtime_error(sqlite3_errmsg(_sqlite));
}
do {
result = sqlite3_step(stmt);
switch(result) {
case SQLITE_BUSY:
// TODO: Rollback transaction
break;
case SQLITE_ERROR:
throw std::runtime_error(sqlite3_errmsg(_sqlite));
case SQLITE_ROW:
rows.emplace_back(stmt);
break;
default:
break;
}
} while (result == SQLITE_ROW);
result = sqlite3_finalize(stmt);
if(result != SQLITE_OK) {
throw std::runtime_error(sqlite3_errmsg(_sqlite));
}
return rows;
}
};
#endif //AUTOCAT_GNOME_STORAGE_H