From d30f8df5c1b82c4bd5649dabe6fd1e071dd37355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ortega=20Froysa?= Date: Fri, 11 Oct 2024 17:43:28 +0200 Subject: [PATCH] Put database in class and use throw exceptions. --- src/cmd.cpp | 70 ++++++------ src/db.cpp | 298 +++++++++++++++++++++++++-------------------------- src/db.hpp | 116 +++++++++++--------- src/main.cpp | 55 +++++----- 4 files changed, 272 insertions(+), 267 deletions(-) diff --git a/src/cmd.cpp b/src/cmd.cpp index 949c422..908477b 100644 --- a/src/cmd.cpp +++ b/src/cmd.cpp @@ -19,13 +19,14 @@ #include "db.hpp" #include "util.hpp" -#include #include +#include #include -#include #include +#include int cmd_add(void) { + db db; std::string name, description, ingredients, tags; int recipe_id, ingredient_id, tag_id; @@ -41,36 +42,34 @@ int cmd_add(void) { std::cout << "Tags (comma separated): "; getline(std::cin, tags); - if(not db_open()) { - std::cerr << "Failed to open database. Cannot add new entry." << std::endl; - return EXIT_FAILURE; - } + db.open(); - if((recipe_id = db_get_recipe_id(name)) <= 0) - recipe_id = db_add_recipe(name, description); + if((recipe_id = db.get_recipe_id(name)) <= 0) + recipe_id = db.add_recipe(name, description); for(auto &ingredient : split(ingredients, ",")) { trim(ingredient); - if((ingredient_id = db_get_ingredient_id(ingredient)) <= 0) - ingredient_id = db_add_ingredient(ingredient); - db_conn_recipe_ingredient(recipe_id, ingredient_id); + if((ingredient_id = db.get_ingredient_id(ingredient)) <= 0) + ingredient_id = db.add_ingredient(ingredient); + db.conn_recipe_ingredient(recipe_id, ingredient_id); } for(auto &tag : split(tags, ",")) { trim(tag); - if((tag_id = db_get_tag_id(tag)) <= 0) - tag_id = db_add_tag(tag); - db_conn_recipe_tag(recipe_id, tag_id); + if((tag_id = db.get_tag_id(tag)) <= 0) + tag_id = db.add_tag(tag); + db.conn_recipe_tag(recipe_id, tag_id); } - db_close(); + db.close(); return EXIT_SUCCESS; } int cmd_list(int argc, char *argv[]) { + db db; std::vector ingredients, tags; int opt; @@ -93,21 +92,18 @@ int cmd_list(int argc, char *argv[]) { } } - if(not db_open()) { - std::cerr << "Failed to open database. Cannot add new entry." << std::endl; - return EXIT_FAILURE; - } + db.open(); - for(const auto &recipe : db_get_recipes(ingredients, tags)) + for(const auto &recipe : db.get_recipes(ingredients, tags)) std::cout << recipe.id << " | " << recipe.name << " | " << recipe.description << std::endl; - db_close(); + db.close(); return EXIT_SUCCESS; } int cmd_delete(int argc, char *argv[]) { - int ret = EXIT_SUCCESS; + db db; std::vector recipe_ids; if(argc < 1) { @@ -115,15 +111,12 @@ int cmd_delete(int argc, char *argv[]) { return EXIT_FAILURE; } - if(not db_open()) { - std::cerr << "Failed to open database. Cannot add new entry." << std::endl; - return EXIT_FAILURE; - } + db.open(); for(int i = 0; i < argc; ++i) { const int id = std::stoi(argv[i]); - if(not db_recipe_exists(id)) { + if(not db.recipe_exists(id)) { std::cerr << "No recipe exists with ID " << id << "." << std::endl; return EXIT_FAILURE; } else { @@ -131,33 +124,30 @@ int cmd_delete(int argc, char *argv[]) { } } - ret = (db_del_recipes(recipe_ids)) ? EXIT_SUCCESS : EXIT_FAILURE; + db.del_recipes(recipe_ids); + db.close(); - db_close(); - - return ret; + return EXIT_SUCCESS; } int cmd_info(const int id) { + db db; struct recipe recipe; std::vector ingredients, tags; int ret = EXIT_SUCCESS; - if(not db_open()) { - std::cerr << "Failed to open database. Cannot add new entry." << std::endl; - return EXIT_FAILURE; - } + db.open(); - if(not db_recipe_exists(id)) { + if(not db.recipe_exists(id)) { std::cerr << "No recipe with ID '" << id << "'"; return EXIT_FAILURE; } - recipe = db_get_recipe(id); - ingredients = db_get_recipe_ingredients(id); - tags = db_get_recipe_tags(id); + recipe = db.get_recipe(id); + ingredients = db.get_recipe_ingredients(id); + tags = db.get_recipe_tags(id); - db_close(); + db.close(); std::cout << "Name: " << recipe.name << "\n" << "Description: " << recipe.description << "\n" diff --git a/src/db.cpp b/src/db.cpp index a309440..1da9a39 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -17,28 +17,22 @@ */ #include "db.hpp" -#include #include -#include #include #include -#include -#include +#include +#include +#include #define DB_VERSION 1 -static sqlite3 *db = nullptr; -int db_open(void) { +void db::open(void) { std::string xdg_data_home; std::string db_path; bool new_db = false; - int rc; - if((xdg_data_home = std::getenv("XDG_DATA_HOME")).empty()) { - std::cerr << "Cannot find environment variable XDG_DATA_HOME. Please define it before continuing. E.g.:\n" - "export XDG_DATA_HOME=\"$HOME/.local/share\"" << std::endl; - return 0; - } + if((xdg_data_home = std::getenv("XDG_DATA_HOME")).empty()) + throw std::runtime_error("Cannot find environment variable XDG_DATA_HOME. Please define it before continuing."); db_path = xdg_data_home + "/menu-helper"; @@ -52,26 +46,26 @@ int db_open(void) { new_db = true; } - rc = sqlite3_open(db_path.c_str(), &db); + if(sqlite3_open(db_path.c_str(), &sqlite_db) not_eq SQLITE_OK) + throw std::runtime_error("Failed to open database file " + db_path); - if(rc == SQLITE_OK and new_db) { - sqlite3_exec(db, "CREATE TABLE db_version(version INTEGER UNIQUE NOT nullptr);", nullptr, nullptr, nullptr); - sqlite3_exec(db, std::format("INSERT INTO db_version VALUES({});", DB_VERSION).c_str(), nullptr, nullptr, nullptr); - sqlite3_exec(db, "CREATE TABLE tags(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING UNIQUE);", nullptr, nullptr, nullptr); - sqlite3_exec(db, "CREATE TABLE ingredients(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING UNIQUE);", nullptr, nullptr, nullptr); - sqlite3_exec(db, "CREATE TABLE recipes(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING UNIQUE, description STRING);", nullptr, nullptr, nullptr); - sqlite3_exec(db, "CREATE TABLE recipe_tag(recipe_id INTEGER REFERENCES recipes(id) ON DELETE CASCADE, tag_id INTEGER REFERENCES tags(id) ON DELETE CASCADE);", nullptr, nullptr, nullptr); - sqlite3_exec(db, "CREATE TABLE recipe_ingredient(recipe_id INTEGER REFERENCES recipes(id) ON DELETE CASCADE, ingredient_id INTEGER REFERENCES ingredients(id) ON DELETE CASCADE);", nullptr, nullptr, nullptr); + if(new_db) { + sqlite3_exec(sqlite_db, "CREATE TABLE db_version(version INTEGER UNIQUE NOT nullptr);", nullptr, nullptr, nullptr); + sqlite3_exec(sqlite_db, std::format("INSERT INTO db_version VALUES({});", DB_VERSION).c_str(), nullptr, nullptr, nullptr); + sqlite3_exec(sqlite_db, "CREATE TABLE tags(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING UNIQUE);", nullptr, nullptr, nullptr); + sqlite3_exec(sqlite_db, "CREATE TABLE ingredients(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING UNIQUE);", nullptr, nullptr, nullptr); + sqlite3_exec(sqlite_db, "CREATE TABLE recipes(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING UNIQUE, description STRING);", nullptr, nullptr, nullptr); + sqlite3_exec(sqlite_db, "CREATE TABLE recipe_tag(recipe_id INTEGER REFERENCES recipes(id) ON DELETE CASCADE, tag_id INTEGER REFERENCES tags(id) ON DELETE CASCADE);", nullptr, nullptr, nullptr); + sqlite3_exec(sqlite_db, "CREATE TABLE recipe_ingredient(recipe_id INTEGER REFERENCES recipes(id) ON DELETE CASCADE, ingredient_id INTEGER REFERENCES ingredients(id) ON DELETE CASCADE);", nullptr, nullptr, nullptr); } - - return rc == SQLITE_OK; } -void db_close(void) { - if(not db) +void db::close(void) { + if(not sqlite_db) return; - sqlite3_close(db); + sqlite3_close(sqlite_db); + sqlite_db = nullptr; } int query_id_cb(void *recipe_id_var, int col_num, char **col_data, char **col_name) { @@ -89,43 +83,47 @@ int query_id_cb(void *recipe_id_var, int col_num, char **col_data, char **col_na return ret; } -int table_get_id_by_name(const std::string &table, const std::string &name) { +int db::table_get_id_by_name(const std::string &table, const std::string &name) { int id = 0; - if(sqlite3_exec(db, std::format("SELECT id FROM {} WHERE lower(name)=lower('{}');", table, name).c_str(), - query_id_cb, &id, nullptr) not_eq SQLITE_OK) - return -2; + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); + + if(sqlite3_exec(sqlite_db, std::format("SELECT id FROM {} WHERE lower(name)=lower('{}');", table, name).c_str(), + query_id_cb, &id, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error(std::format("Failed to get ID of '{}' from table '{}'.", name, table)); + } return id; } -int db_add_recipe(const std::string &name, const std::string &description) { - if(not db) - return -1; +int db::add_recipe(const std::string &name, const std::string &description) { + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); - if(sqlite3_exec(db, std::format("INSERT INTO recipes(name,description) VALUES('{}','{}');", name, description).c_str(), - nullptr, nullptr, nullptr) not_eq SQLITE_OK) - return -2; + if(sqlite3_exec(sqlite_db, std::format("INSERT INTO recipes(name,description) VALUES('{}','{}');", name, description).c_str(), + nullptr, nullptr, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error("Failed to insert new recipe into database."); + } - return db_get_recipe_id(name); + return get_recipe_id(name); } -bool db_del_recipe(const int id) { - if(not db) - return false; +void db::del_recipe(const int id) { + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); - if(sqlite3_exec(db, std::format("DELETE FROM recipes WHERE id={}", id).c_str(), - nullptr, nullptr, nullptr) not_eq SQLITE_OK) - return false; - - return true; + if(sqlite3_exec(sqlite_db, std::format("DELETE FROM recipes WHERE id={}", id).c_str(), + nullptr, nullptr, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error(std::format("Failed to delete recipe with ID {} from database.", id)); + } } -bool db_del_recipes(const std::vector &ids) { +void db::del_recipes(const std::vector &ids) { std::string stmt = "DELETE FROM recipes WHERE id IN ("; - if(not db) - return false; + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); bool first = true; for(auto id : ids) { @@ -139,53 +137,54 @@ bool db_del_recipes(const std::vector &ids) { stmt += ");"; - if(sqlite3_exec(db, stmt.c_str(), nullptr, nullptr, nullptr) not_eq SQLITE_OK) - return false; - - return true; + if(sqlite3_exec(sqlite_db, stmt.c_str(), nullptr, nullptr, nullptr) not_eq SQLITE_OK) + throw std::runtime_error("Failed to delete recipes from database."); } -bool db_recipe_exists(const int id) { +bool db::recipe_exists(const int id) { bool exists = false; - if(not db) - return false; + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); - sqlite3_exec(db, std::format("SELECT id FROM recipes WHERE id={}", id).c_str(), - [](void *found,int,char**,char**) { - *static_cast(found) = true; - return 0; - }, &exists, nullptr); + if(sqlite3_exec(sqlite_db, std::format("SELECT id FROM recipes WHERE id={}", id).c_str(), + [](void *found,int,char**,char**) { + *static_cast(found) = true; + return 0; + }, &exists, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error("Failed to select from database."); + } return exists; } -int db_get_recipe_id(const std::string &name) { - if(not db) - return -1; - - return table_get_id_by_name("recipes", name); -} - -struct recipe db_get_recipe(const int id) { +struct recipe db::get_recipe(const int id) { struct recipe recipe; - sqlite3_exec(db, std::format("SELECT * FROM recipes WHERE id={};", id).c_str(), - [](void *recipe, int, char **col_data, char**) { - *static_cast(recipe) = { std::atoi(col_data[0]), col_data[1], col_data[2] }; - return 0; - }, &recipe, nullptr); + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); + + if(sqlite3_exec(sqlite_db, std::format("SELECT * FROM recipes WHERE id={};", id).c_str(), + [](void *recipe, int, char **col_data, char**) { + *static_cast(recipe) = { std::atoi(col_data[0]), col_data[1], col_data[2] }; + return 0; + }, &recipe, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error("Failed to select from database."); + } return recipe; } -std::vector db_get_recipes(const std::vector &ingredients, - const std::vector &tags) +std::vector db::get_recipes(const std::vector &ingredients, + const std::vector &tags) { std::vector recipes; std::string stmt = "SELECT id,name,description FROM recipes"; std::string filters; + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); + if(not ingredients.empty() or not tags.empty()) filters += " WHERE"; @@ -201,14 +200,10 @@ std::vector db_get_recipes(const std::vector &ingred filters += " id IN (SELECT recipe_id FROM recipe_ingredient WHERE ingredient_id="; - // TODO: use throw? - if((id = db_get_ingredient_id(i)) < 0) { - std::cerr << "Failed to find ingredient '" << i << "'" << std::endl; - return std::vector(); - } else { - filters += std::to_string(id); - } + if((id = get_ingredient_id(i)) <= 0) + throw std::runtime_error(std::format("Failed to find ingredient '{}'", i)); + filters += std::to_string(id); filters += ")"; } } @@ -228,111 +223,106 @@ std::vector db_get_recipes(const std::vector &ingred filters += " id IN (SELECT recipe_id FROM recipe_tag WHERE tag_id="; - // TODO: use throw? - if((id = db_get_tag_id(i)) < 0) { - std::cerr << "Failed to find tag '" << i << "'" << std::endl; - return std::vector(); - } else { - filters += std::to_string(id); - } + if((id = get_tag_id(i)) <= 0) + throw std::runtime_error("Failed to find tag '{}'"); + filters += std::to_string(id); filters += ")"; } } stmt += filters + ";"; - sqlite3_exec(db, stmt.c_str(), - [](void *recipe_list, int, char **col_data, char**) { - auto recipe_vec = static_cast*>(recipe_list); - recipe_vec->push_back({ - std::atoi(col_data[0]), - col_data[1], - col_data[2] }); + if(sqlite3_exec(sqlite_db, stmt.c_str(), + [](void *recipe_list, int, char **col_data, char**) { + static_cast*>(recipe_list)->push_back({ + std::atoi(col_data[0]), + col_data[1], + col_data[2] }); return 0; - }, &recipes, nullptr); + }, &recipes, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error("Failed to select recipes."); + } return recipes; } -int db_add_ingredient(const std::string &name) { - if(not db) - return -1; +int db::add_ingredient(const std::string &name) { + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); - if(sqlite3_exec(db, std::format("INSERT INTO ingredients(name) VALUES(lower('{}'));", name).c_str(), - nullptr, nullptr, nullptr) not_eq SQLITE_OK) - return -2; + if(sqlite3_exec(sqlite_db, std::format("INSERT INTO ingredients(name) VALUES(lower('{}'));", name).c_str(), + nullptr, nullptr, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error(std::format("Failed to instert ingredient '{}'.", name)); + } - return db_get_ingredient_id(name); + return get_ingredient_id(name); } -std::vector db_get_recipe_ingredients(const int id) { +std::vector db::get_recipe_ingredients(const int id) { std::vector ingredients; - sqlite3_exec(db, std::format("SELECT name FROM ingredients WHERE id IN (SELECT ingredient_id FROM recipe_ingredient WHERE recipe_id={});", id).c_str(), - [](void *ingredients, int, char **col_data, char**) { - static_cast*>(ingredients)->push_back(col_data[0]); - return 0; - }, &ingredients, nullptr); + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); + + if(sqlite3_exec(sqlite_db, std::format("SELECT name FROM ingredients WHERE id IN (SELECT ingredient_id FROM recipe_ingredient WHERE recipe_id={});", id).c_str(), + [](void *ingredients, int, char **col_data, char**) { + static_cast*>(ingredients)->push_back(col_data[0]); + return 0; + }, &ingredients, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error(std::format("Failed to select ingredients from recipe with ID {}", id)); + } return ingredients; } -int db_get_ingredient_id(const std::string &name) { - if(not db) - return -1; +int db::add_tag(const std::string &name) { + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); - return table_get_id_by_name("ingredients", name); + if(sqlite3_exec(sqlite_db, std::format("INSERT INTO tags(name) VALUES('{}');", name).c_str(), + nullptr, nullptr, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error(std::format("Failed to insert tag '{}'", name)); + } + + return get_tag_id(name); } -int db_add_tag(const std::string &name) { - if(not db) - return -1; - - if(sqlite3_exec(db, std::format("INSERT INTO tags(name) VALUES('{}');", name).c_str(), - nullptr, nullptr, nullptr) not_eq SQLITE_OK) - return -2; - - return db_get_tag_id(name); -} - -std::vector db_get_recipe_tags(const int id) { +std::vector db::get_recipe_tags(const int id) { std::vector tags; - sqlite3_exec(db, std::format("SELECT name FROM tags WHERE id IN (SELECT tag_id FROM recipe_tag WHERE recipe_id={});", id).c_str(), - [](void *tags, int, char **col_data, char**) { - static_cast*>(tags)->push_back(col_data[0]); - return 0; - }, &tags, nullptr); + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); + + if(sqlite3_exec(sqlite_db, std::format("SELECT name FROM tags WHERE id IN (SELECT tag_id FROM recipe_tag WHERE recipe_id={});", id).c_str(), + [](void *tags, int, char **col_data, char**) { + static_cast*>(tags)->push_back(col_data[0]); + return 0; + }, &tags, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error(std::format("Failed to select tags for recipe with ID {}", id)); + } return tags; } -int db_get_tag_id(const std::string &name) { - if(not db) - return -1; +void db::conn_recipe_ingredient(int recipe_id, int ingredient_id) { + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); - return table_get_id_by_name("tags", name); + if(sqlite3_exec(sqlite_db, std::format("INSERT INTO recipe_ingredient(recipe_id, ingredient_id) VALUES({},{});", recipe_id, ingredient_id).c_str(), + nullptr, nullptr, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error(std::format("Failed to connect recipe with ID {} to ingredient with ID {}", + recipe_id, ingredient_id)); + } } -int db_conn_recipe_ingredient(int recipe_id, int ingredient_id) { - if(not db) - return -1; +void db::conn_recipe_tag(int recipe_id, int tag_id) { + if(not sqlite_db) + throw std::runtime_error(std::format("{}: Database not open! Please contact a developer.", __PRETTY_FUNCTION__)); - if(sqlite3_exec(db, std::format("INSERT INTO recipe_ingredient(recipe_id, ingredient_id) VALUES({},{});", recipe_id, ingredient_id).c_str(), - nullptr, nullptr, nullptr) not_eq SQLITE_OK) - return -2; - - return 1; -} - -int db_conn_recipe_tag(int recipe_id, int tag_id) { - if(not db) - return -1; - - if(sqlite3_exec(db, std::format("INSERT INTO recipe_tag(recipe_id, tag_id) VALUES({},{});", recipe_id, tag_id).c_str(), - nullptr, nullptr, nullptr) not_eq SQLITE_OK) - return -2; - - return 1; + if(sqlite3_exec(sqlite_db, std::format("INSERT INTO recipe_tag(recipe_id, tag_id) VALUES({},{});", recipe_id, tag_id).c_str(), + nullptr, nullptr, nullptr) not_eq SQLITE_OK) { + throw std::runtime_error(std::format("Failed to connect recipe with ID {} to tag with ID {}", + recipe_id, tag_id)); + } } diff --git a/src/db.hpp b/src/db.hpp index 0993d2b..1fcf2ae 100644 --- a/src/db.hpp +++ b/src/db.hpp @@ -17,6 +17,7 @@ */ #pragma once +#include #include #include @@ -26,56 +27,73 @@ struct recipe { std::string description; }; -int db_open(void); -void db_close(void); +class db { +private: + sqlite3 *sqlite_db; + int table_get_id_by_name(const std::string &table, const std::string &name); -/** - * @brief Add a new recipe to the database. - * - * @param name Name of the new recipe. - * @param description Short description. - * - * @return ID of newly created recipe, -1 if DB isn't open, -2 on other failure. - */ -int db_add_recipe(const std::string &name, const std::string &description); -bool db_del_recipe(const int id); -bool db_del_recipes(const std::vector &ids); -int db_get_recipe_id(const std::string &name); -bool db_recipe_exists(const int id); -static inline bool db_recipe_exists(const std::string &name) { - return (db_get_recipe_id(name) > 0); -} -struct recipe db_get_recipe(const int id); -std::vector db_get_recipes(const std::vector &ingredients, - const std::vector &tags); +public: + db() : sqlite_db(nullptr) {} + ~db() { + sqlite3_close(sqlite_db); + } + void open(void); + void close(void); -/** - * @brief Add a new ingredient to the database. - * - * @param name Name of the new ingredient. - * - * @return ID of newly created ingredient, -1 if DB isn't open, -2 on other failure. - */ -int db_add_ingredient(const std::string &name); -std::vector db_get_recipe_ingredients(const int id); -int db_get_ingredient_id(const std::string &name); -static inline bool db_ingredient_exists(const std::string &name) { - return (db_get_ingredient_id(name) > 0); -} + /** + * @brief Add a new recipe to the database. + * + * @param name Name of the new recipe. + * @param description Short description. + * + * @return ID of newly created recipe. + */ + int add_recipe(const std::string &name, const std::string &description); + void del_recipe(const int id); + void del_recipes(const std::vector &ids); + inline int get_recipe_id(const std::string &name) { + return table_get_id_by_name("recipes", name); + } + inline bool db_recipe_exists(const std::string &name) { + return (get_recipe_id(name) > 0); + } + bool recipe_exists(const int id); + struct recipe get_recipe(const int id); + std::vector get_recipes(const std::vector &ingredients, + const std::vector &tags); -/** - * @brief Add a new tag to the database. - * - * @param name Name of the new tag. - * - * @return ID of newly created tag, -1 if DB isn't open, -2 on other failure. - */ -int db_add_tag(const std::string &name); -std::vector db_get_recipe_tags(const int id); -int db_get_tag_id(const std::string &name); -static inline bool db_tag_exists(const std::string &name) { - return (db_get_tag_id(name) > 0); -} + /** + * @brief Add a new ingredient to the database. + * + * @param name Name of the new ingredient. + * + * @return ID of newly created ingredient. + */ + int add_ingredient(const std::string &name); + std::vector get_recipe_ingredients(const int id); + inline int get_ingredient_id(const std::string &name) { + return table_get_id_by_name("ingredients", name); + } + inline bool db_ingredient_exists(const std::string &name) { + return (get_ingredient_id(name) > 0); + } -int db_conn_recipe_ingredient(int recipe_id, int ingredient_id); -int db_conn_recipe_tag(int recipe_id, int tag_id); + /** + * @brief Add a new tag to the database. + * + * @param name Name of the new tag. + * + * @return ID of newly created tag, -1 if DB isn't open, -2 on other failure. + */ + int add_tag(const std::string &name); + std::vector get_recipe_tags(const int id); + inline int get_tag_id(const std::string &name) { + return table_get_id_by_name("tags", name); + } + inline bool tag_exists(const std::string &name) { + return (get_tag_id(name) > 0); + } + + void conn_recipe_ingredient(int recipe_id, int ingredient_id); + void conn_recipe_tag(int recipe_id, int tag_id); +}; diff --git a/src/main.cpp b/src/main.cpp index 29eb784..a476c66 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,8 +16,9 @@ * along with this program. If not, see . */ -#include #include +#include +#include #include #include "arg_parse.hpp" @@ -35,29 +36,35 @@ int main(int argc, char *argv[]) { id = parse_args(argv[1]); - switch(id) { - case CMD_ADD: - ret = cmd_add(); - break; - case CMD_LIST: - ret = cmd_list(argc - 1, argv + 1); - break; - case CMD_DEL: - ret = cmd_delete(argc - 2, argv + 2); - break; - case CMD_INFO: - ret = cmd_info(std::stoi(argv[2])); - break; - case CMD_HELP: - print_help(); - break; - case CMD_VERSION: - print_version(); - break; - default: - std::cerr << "No such command '" << argv[1] << "'. Use 'help' sub-command." << std::endl; - print_usage(); - return EXIT_FAILURE; + try { + switch(id) { + case CMD_ADD: + ret = cmd_add(); + break; + case CMD_LIST: + ret = cmd_list(argc - 1, argv + 1); + break; + case CMD_DEL: + ret = cmd_delete(argc - 2, argv + 2); + break; + case CMD_INFO: + ret = cmd_info(std::stoi(argv[2])); + break; + case CMD_HELP: + print_help(); + break; + case CMD_VERSION: + print_version(); + break; + default: + std::cerr << "No such command '" << argv[1] << "'. Use 'help' sub-command." << std::endl; + print_usage(); + ret = EXIT_FAILURE; + break; + } + } catch(const std::exception &e) { + std::cerr << e.what() << std::endl; + ret = EXIT_FAILURE; } return ret;