From 8295c5ddf9c06ef848dbaf9a03e03ae9da9f66d6 Mon Sep 17 00:00:00 2001 From: etenie Date: Wed, 16 Oct 2024 14:02:16 +0200 Subject: [PATCH 1/9] regex experimentations --- .gitignore | 5 +++-- CMakeLists.txt | 21 ++++++++++++++++++--- src/main.cpp | 24 ++++++++++++++++++------ 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 436c580..c2772e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ cmake-build* - -.idea \ No newline at end of file +build +.cache +.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 2881d3e..fa87c65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,13 +2,28 @@ cmake_minimum_required(VERSION 3.30) project(nutri) set(CMAKE_CXX_STANDARD 26) +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) -find_package(fmt REQUIRED) -find_package(cpr REQUIRED) find_package(nlohmann_json REQUIRED) include_directories(${CMAKE_SOURCE_DIR}/external/ctre/include) -add_subdirectory(external/ctre/) + +include(FetchContent) + +FetchContent_Declare( + ctre + GIT_REPOSITORY https://github.com/hanickadot/compile-time-regular-expressions.git + GIT_TAG v3.9.0 + GIT_SHALLOW TRUE +) +FetchContent_Declare( + cpr + GIT_REPOSITORY https://github.com/libcpr/cpr.git + GIT_TAG 1.11.0 + GIT_SHALLOW TRUE +) + +FetchContent_MakeAvailable(ctre cpr) add_executable( nutri diff --git a/src/main.cpp b/src/main.cpp index 0467843..3a57645 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,18 @@ #include "ArgParser.hpp" #include "Helpers/WolframAlpha.hpp" +#include #include #include #include +#include + +void getVal(std::string_view sv) { + auto m = ctre::search<"(?<=protein\\s)(\\d+)\\s*(\\w+)">(sv); + double d = m.get<1>().to_number(); + std::string u = m.get<2>().to_string(); + + std::println("1: {}\n2: {}", d, u); +} int main(const int argc, char* argv[]) { try { @@ -13,13 +23,15 @@ int main(const int argc, char* argv[]) { args.addArg("-h", "--help", "Print help", false); args.parse(argc, argv); - const auto food = args.get("--food"); - const double amount = args.get("--amount") / 100; + // const auto food = args.get("--food"); + // const double amount = args.get("--amount") / 100; + + // if (!args.has("--source") || args.get("--source") == "Wolfram") { + // WolframAlpha wa(food, amount); + // std::println("Calories: {:.2f} kcal\nProtein: {:.2f} g\nCarbs: {:.2f} g\nFat: {:.2f} g", wa.getCalories(), wa.getProtein(), wa.getCarbs(), wa.getFat()); + // } + getVal("carbs 14 g | protein 20 mg | fat 11 g"); - if (!args.has("--source") || args.get("--source") == "Wolfram") { - WolframAlpha wa(food, amount); - std::println("Calories: {:.2f} kcal\nProtein: {:.2f} g\nCarbs: {:.2f} g\nFat: {:.2f} g", wa.getCalories(), wa.getProtein(), wa.getCarbs(), wa.getFat()); - } } catch (const std::exception& e) { std::println(stderr, "Error: {}", e.what()); return 1; -- 2.34.1 From 15cef7f49b3786eb2182fc7d5962f119c631c147 Mon Sep 17 00:00:00 2001 From: etenie Date: Wed, 16 Oct 2024 14:05:13 +0200 Subject: [PATCH 2/9] replace submodules with CMakes FetchContent --- .gitmodules | 3 --- external/ctre | 1 - 2 files changed, 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 external/ctre diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 46c24d6..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "external/ctre"] - path = external/ctre - url = https://github.com/hanickadot/compile-time-regular-expressions.git diff --git a/external/ctre b/external/ctre deleted file mode 160000 index 626313d..0000000 --- a/external/ctre +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 626313dd1563e6f3a02e35427052d808df636cd8 -- 2.34.1 From 47366a6d624b90bc3ded36544a7c765ad007cba0 Mon Sep 17 00:00:00 2001 From: etenie Date: Wed, 16 Oct 2024 14:22:01 +0200 Subject: [PATCH 3/9] fix clangd errors --- .clangd | 3 +++ CMakeLists.txt | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..2212c2c --- /dev/null +++ b/.clangd @@ -0,0 +1,3 @@ +# clangd currently doesnt support modules +CompileFlags: + Remove: [-fmodule*, -fdeps-format*] diff --git a/CMakeLists.txt b/CMakeLists.txt index fa87c65..0004b5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS 1) find_package(nlohmann_json REQUIRED) -include_directories(${CMAKE_SOURCE_DIR}/external/ctre/include) - include(FetchContent) FetchContent_Declare( -- 2.34.1 From 537cab18ed8f7b09ad7e6195ede4f90fb2f87203 Mon Sep 17 00:00:00 2001 From: etenie Date: Wed, 16 Oct 2024 17:30:37 +0200 Subject: [PATCH 4/9] update the way we get compounds, make the WolframAlpha::get fucntion safer and restore main.cpp --- src/ArgParser.hpp | 16 +++++++--------- src/Helpers/WolframAlpha.cpp | 33 ++++++++++++--------------------- src/main.cpp | 23 ++++++----------------- 3 files changed, 25 insertions(+), 47 deletions(-) diff --git a/src/ArgParser.hpp b/src/ArgParser.hpp index 23325c8..9ea27c3 100644 --- a/src/ArgParser.hpp +++ b/src/ArgParser.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -54,21 +55,18 @@ public: template T get(const std::string& arg) const { std::string value; - if (m_longOptMap.contains(arg)) { + if (m_longOptMap.contains(arg) && !m_longOptMap.at(arg)->value.empty()) { value = m_longOptMap.at(arg)->value; - } else if (m_shortOptMap.contains(arg)) { + } else if (m_shortOptMap.contains(arg) && !m_shortOptMap.at(arg)->value.empty()) { value = m_shortOptMap.at(arg)->value; } else { throw std::runtime_error(arg + " has no value"); } - if constexpr (std::is_same_v) { - return std::stoi(value); - } else if constexpr (std::is_same_v) { - return std::stof(value); - } else if constexpr (std::is_same_v) { - return std::stod(value); - } else { + if constexpr (std::is_same_v) { return value; + } else { + std::cout << std::format("the value is: {}", value); + return std::stoi(value); } } diff --git a/src/Helpers/WolframAlpha.cpp b/src/Helpers/WolframAlpha.cpp index d3872be..b69a482 100644 --- a/src/Helpers/WolframAlpha.cpp +++ b/src/Helpers/WolframAlpha.cpp @@ -16,33 +16,24 @@ double WolframAlpha::getCalories() { } double WolframAlpha::getProtein() { - if (auto match = ctre::search(getPod())) { - const std::string val = std::string(match.to_view().substr(0, match.begin() - match.end())); - std::string unit = std::string(ctre::search(match.to_view())); - - return unit == "g" ? std::stod(val) * m_amount : std::stod(val) / 1000 * m_amount; - } - throw std::runtime_error("Protein information not found."); + auto m = ctre::search<"(?<=protein\\s)(\\d+)\\s*(\\w+)">(getPod()); + double d = m.get<1>().to_number(); + std::string u = m.get<2>().to_string(); + return u == "g" ? d : d / 1000; } double WolframAlpha::getCarbs() { - if (auto match = ctre::search(getPod())) { - const std::string val = std::string(match.to_view().substr(0, match.begin() - match.end())); - std::string unit = std::string(ctre::search(match.to_view())); - - return unit == "g" ? std::stod(val) * m_amount : std::stod(val) / 1000 * m_amount; - } - throw std::runtime_error("Carbohydrates information not found."); + auto m = ctre::search<"(?<=total carbohydrates\\s)(\\d+)\\s*(\\w+)">(getPod()); + double d = m.get<1>().to_number(); + std::string u = m.get<2>().to_string(); + return u == "g" ? d : d / 1000; } double WolframAlpha::getFat() { - if (auto match = ctre::search(getPod())) { - const std::string val = std::string(match.to_view().substr(0, match.begin() - match.end())); - std::string unit = std::string(ctre::search(match.to_view())); - - return unit == "g" ? std::stod(val) * m_amount : std::stod(val) / 1000 * m_amount; - } - throw std::runtime_error("Fat information not found."); + auto m = ctre::search<"(?<=total fat\\s)(\\d+)\\s*(\\w+)">(getPod()); + double d = m.get<1>().to_number(); + std::string u = m.get<2>().to_string(); + return u == "g" ? d : d / 1000; } nlohmann::json WolframAlpha::fetchJson(const std::string& query) { diff --git a/src/main.cpp b/src/main.cpp index 3a57645..37f3844 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,8 @@ #include "ArgParser.hpp" #include "Helpers/WolframAlpha.hpp" #include -#include #include #include -#include - -void getVal(std::string_view sv) { - auto m = ctre::search<"(?<=protein\\s)(\\d+)\\s*(\\w+)">(sv); - double d = m.get<1>().to_number(); - std::string u = m.get<2>().to_string(); - - std::println("1: {}\n2: {}", d, u); -} int main(const int argc, char* argv[]) { try { @@ -23,14 +13,13 @@ int main(const int argc, char* argv[]) { args.addArg("-h", "--help", "Print help", false); args.parse(argc, argv); - // const auto food = args.get("--food"); - // const double amount = args.get("--amount") / 100; + const auto food = args.get("--food"); + const double amount = args.get("--amount") / 100; - // if (!args.has("--source") || args.get("--source") == "Wolfram") { - // WolframAlpha wa(food, amount); - // std::println("Calories: {:.2f} kcal\nProtein: {:.2f} g\nCarbs: {:.2f} g\nFat: {:.2f} g", wa.getCalories(), wa.getProtein(), wa.getCarbs(), wa.getFat()); - // } - getVal("carbs 14 g | protein 20 mg | fat 11 g"); + if (!args.has("--source") || args.get("--source") == "Wolfram") { + WolframAlpha wa(food, amount); + std::println("Calories: {:.2f} kcal\nProtein: {:.2f} g\nCarbs: {:.2f} g\nFat: {:.2f} g", wa.getCalories(), wa.getProtein(), wa.getCarbs(), wa.getFat()); + } } catch (const std::exception& e) { std::println(stderr, "Error: {}", e.what()); -- 2.34.1 From 467580e1b98d5cf5c05f225ea87fb2739fe5c2cb Mon Sep 17 00:00:00 2001 From: etenie Date: Wed, 16 Oct 2024 17:33:58 +0200 Subject: [PATCH 5/9] remove debug print --- src/ArgParser.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ArgParser.hpp b/src/ArgParser.hpp index 9ea27c3..faebf69 100644 --- a/src/ArgParser.hpp +++ b/src/ArgParser.hpp @@ -65,7 +65,6 @@ public: if constexpr (std::is_same_v) { return value; } else { - std::cout << std::format("the value is: {}", value); return std::stoi(value); } } -- 2.34.1 From 302f32b234b4f022eab83a0d541c5df5c86d1e51 Mon Sep 17 00:00:00 2001 From: etenie Date: Wed, 16 Oct 2024 17:38:59 +0200 Subject: [PATCH 6/9] remove debug print --- src/ArgParser.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ArgParser.hpp b/src/ArgParser.hpp index faebf69..b071406 100644 --- a/src/ArgParser.hpp +++ b/src/ArgParser.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include -- 2.34.1 From 579f82f4fb107481b93b017b119c430136d91901 Mon Sep 17 00:00:00 2001 From: etenie Date: Wed, 30 Oct 2024 18:15:21 +0100 Subject: [PATCH 7/9] migrate to glaze --- CMakeLists.txt | 13 ++++-- src/Helpers/WolframAlpha.cpp | 51 ----------------------- src/Helpers/WolframAlpha.hpp | 79 +++++++++++++++++++++++++++++++----- src/main.cpp | 11 ++--- 4 files changed, 82 insertions(+), 72 deletions(-) delete mode 100644 src/Helpers/WolframAlpha.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0004b5b..a98d598 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,8 +20,14 @@ FetchContent_Declare( GIT_TAG 1.11.0 GIT_SHALLOW TRUE ) +FetchContent_Declare( + glaze + GIT_REPOSITORY https://github.com/stephenberry/glaze.git + GIT_TAG main + GIT_SHALLOW TRUE +) -FetchContent_MakeAvailable(ctre cpr) +FetchContent_MakeAvailable(ctre cpr glaze) add_executable( nutri @@ -30,6 +36,5 @@ add_executable( src/Helpers/Utility.cpp src/Helpers/Utility.hpp src/Helpers/WolframAlpha.hpp - src/Helpers/WolframAlpha.cpp) - -target_link_libraries(nutri PRIVATE cpr::cpr nlohmann_json::nlohmann_json ctre::ctre) + ) +target_link_libraries(nutri PRIVATE cpr::cpr nlohmann_json::nlohmann_json ctre::ctre glaze::glaze) diff --git a/src/Helpers/WolframAlpha.cpp b/src/Helpers/WolframAlpha.cpp deleted file mode 100644 index b69a482..0000000 --- a/src/Helpers/WolframAlpha.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "WolframAlpha.hpp" - -#include "Utility.hpp" -#include -#include -#include -#include - -WolframAlpha::WolframAlpha(const std::string& query, double amount) : m_json(fetchJson(query)), m_amount(amount) {} - -double WolframAlpha::getCalories() { - if (auto match = ctre::search(getPod())) { - return std::stod(match.data()) * m_amount; - } - throw std::runtime_error("Calorie information not found."); -} - -double WolframAlpha::getProtein() { - auto m = ctre::search<"(?<=protein\\s)(\\d+)\\s*(\\w+)">(getPod()); - double d = m.get<1>().to_number(); - std::string u = m.get<2>().to_string(); - return u == "g" ? d : d / 1000; -} - -double WolframAlpha::getCarbs() { - auto m = ctre::search<"(?<=total carbohydrates\\s)(\\d+)\\s*(\\w+)">(getPod()); - double d = m.get<1>().to_number(); - std::string u = m.get<2>().to_string(); - return u == "g" ? d : d / 1000; -} - -double WolframAlpha::getFat() { - auto m = ctre::search<"(?<=total fat\\s)(\\d+)\\s*(\\w+)">(getPod()); - double d = m.get<1>().to_number(); - std::string u = m.get<2>().to_string(); - return u == "g" ? d : d / 1000; -} - -nlohmann::json WolframAlpha::fetchJson(const std::string& query) { - const auto WOLFRAM_KEY = utl::getEnv("NUTRI_WA_KEY"); - cpr::Response json = Get(cpr::Url{"https://api.wolframalpha.com/v2/query"}, - cpr::Parameters{{"input", query + "100g nutrition"}, {"appid", WOLFRAM_KEY}, {"output", "JSON"}, {"format", "plaintext"}}); - return nlohmann::json::parse(json.text); -} - -std::string WolframAlpha::getPod() { - return to_string(m_json["queryresult"]["pods"][1]["subpods"][0]["plaintext"]); -} - -nlohmann::json m_json{}; -double m_amount{}; diff --git a/src/Helpers/WolframAlpha.hpp b/src/Helpers/WolframAlpha.hpp index 401d3de..d2cf51f 100644 --- a/src/Helpers/WolframAlpha.hpp +++ b/src/Helpers/WolframAlpha.hpp @@ -1,21 +1,80 @@ #pragma once -#include +#include "Utility.hpp" +#include "ctll/fixed_string.hpp" +#include +#include +#include +#include +#include #include +// Example regex patern: (?<=protein\s)(\d+)\s(\w) + +struct Data { + double cals{}; + double proteins{}; + double carbs{}; + double fats{}; +}; + class WolframAlpha { public: - explicit WolframAlpha(const std::string& query, double amount); + WolframAlpha(const std::string& query, double amount) : m_plaintext{getData(query)}, m_amount{amount / 100} {} - double getCalories(); - double getProtein(); - double getCarbs(); - double getFat(); + //debug + std::string getPlain() { + return m_plaintext; + } + + Data getNutrition() { + return m_data; + } private: - nlohmann::json fetchJson(const std::string& query); - std::string getPod(); + void getMacro() { + static constexpr ctll::fixed_string calories_regex{R"((?<=total calories\s)\d+)"}; + static constexpr ctll::fixed_string proteins_regex{R"((?<=protein\s)(\d+)\s(\w))"}; + static constexpr ctll::fixed_string carbs_regex{R"((?<=total carbohydrates\s)(\d+)\s(\w))"}; + static constexpr ctll::fixed_string fats_regex{R"((?<=total fat\s)(\d+)\s(\w))"}; - nlohmann::json m_json{}; - double m_amount{}; + const auto convertToGrams = [this](const auto& match, const std::string& unit) -> double { + const double value = (unit == "g") ? match.to_number() : match.to_number() / 1000; + return value * m_amount; + }; + + if (auto match = ctre::search(m_plaintext); match) { + m_data.proteins = convertToGrams(match.get<1>(), match.get<2>().to_string()); + } + + if (auto match = ctre::search(m_plaintext); match) { + m_data.carbs = convertToGrams(match.get<1>(), match.get<2>().to_string()); + } + + if (auto match = ctre::search(m_plaintext); match) { + m_data.fats = convertToGrams(match.get<1>(), match.get<2>().to_string()); + } + + if (auto match = ctre::search(m_plaintext); match) { + m_data.cals = match.get<0>().to_number(); + } + } + std::string getData(const std::string& query) { + const auto WOLFRAM_KEY = utl::getEnv("NUTRI_WA_KEY"); + cpr::Response response = Get(cpr::Url{"https://api.wolframalpha.com/v2/query"}, + cpr::Parameters{{"input", query + "100g nutrition"}, {"appid", WOLFRAM_KEY}, {"output", "JSON"}, {"format", "plaintext"}}); + + glz::json_t json{}; + // Pretty sure this is wrong and i always is false + if (glz::read_json(json, response.text)) { + throw std::runtime_error("Failed to parse the JSON"); + } else { + return json["queryresult"]["pods"][1]["subpods"][0]["plaintext"].get(); + } + } + + Data m_data{}; + std::string m_pod{}; + std::string m_plaintext{}; + double m_amount{}; }; diff --git a/src/main.cpp b/src/main.cpp index 37f3844..fbf3916 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,13 +1,10 @@ #include "ArgParser.hpp" #include "Helpers/WolframAlpha.hpp" -#include -#include #include int main(const int argc, char* argv[]) { try { ArgParser args; - args.addArg("-s", "--source", "Specify the source of the data (Wolfram|OpenFoodFacts)"); args.addArg("-f", "--food", "Specify the food item"); args.addArg("-a", "--amount", "Specify the amount (in grams)"); args.addArg("-h", "--help", "Print help", false); @@ -16,10 +13,10 @@ int main(const int argc, char* argv[]) { const auto food = args.get("--food"); const double amount = args.get("--amount") / 100; - if (!args.has("--source") || args.get("--source") == "Wolfram") { - WolframAlpha wa(food, amount); - std::println("Calories: {:.2f} kcal\nProtein: {:.2f} g\nCarbs: {:.2f} g\nFat: {:.2f} g", wa.getCalories(), wa.getProtein(), wa.getCarbs(), wa.getFat()); - } + WolframAlpha wa(food, amount); + + auto a = wa.getNutrition(); + std::println("cals: {}\nprotein: {}\ncarbs: {}\nfat: {}", a.cals, a.proteins, a.carbs, a.fats); } catch (const std::exception& e) { std::println(stderr, "Error: {}", e.what()); -- 2.34.1 From 467f1078096e96ff37110938ff84af7c5dad6781 Mon Sep 17 00:00:00 2001 From: etenie Date: Sun, 10 Nov 2024 18:26:07 +0100 Subject: [PATCH 8/9] fix --- CMakeLists.txt | 70 ++++++++++-------- src/ArgParser.hpp | 139 +++++++++++++++++++---------------- src/Helpers/WolframAlpha.hpp | 73 ++++++++++-------- src/main.cpp | 15 ++-- 4 files changed, 166 insertions(+), 131 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a98d598..5d4a2fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,38 +3,50 @@ project(nutri) set(CMAKE_CXX_STANDARD 26) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) - -find_package(nlohmann_json REQUIRED) +set(CMAKE_CXX_FLAGS -fdiagnostics-color=always) include(FetchContent) -FetchContent_Declare( - ctre - GIT_REPOSITORY https://github.com/hanickadot/compile-time-regular-expressions.git - GIT_TAG v3.9.0 - GIT_SHALLOW TRUE -) -FetchContent_Declare( - cpr - GIT_REPOSITORY https://github.com/libcpr/cpr.git - GIT_TAG 1.11.0 - GIT_SHALLOW TRUE -) -FetchContent_Declare( - glaze - GIT_REPOSITORY https://github.com/stephenberry/glaze.git - GIT_TAG main - GIT_SHALLOW TRUE -) +find_package(ctre QUIET) +if (NOT ctre_FOUND) + FetchContent_Declare( + ctre + GIT_REPOSITORY https://github.com/hanickadot/compile-time-regular-expressions.git + GIT_TAG v3.9.0 + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(ctre) +endif() -FetchContent_MakeAvailable(ctre cpr glaze) +find_package(cpr QUIET) +if (NOT cpr_FOUND) + FetchContent_Declare( + cpr + GIT_REPOSITORY https://github.com/libcpr/cpr.git + GIT_TAG 1.11.0 + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(cpr) +endif() + +find_package(glaze QUIET) +if (NOT glaze_FOUND) + FetchContent_Declare( + glaze + GIT_REPOSITORY https://github.com/stephenberry/glaze.git + GIT_TAG main + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(glaze) +endif() add_executable( - nutri - src/main.cpp - src/ArgParser.hpp - src/Helpers/Utility.cpp - src/Helpers/Utility.hpp - src/Helpers/WolframAlpha.hpp - ) -target_link_libraries(nutri PRIVATE cpr::cpr nlohmann_json::nlohmann_json ctre::ctre glaze::glaze) + nutri + src/main.cpp + src/ArgParser.hpp + src/Helpers/Utility.cpp + src/Helpers/Utility.hpp + src/Helpers/WolframAlpha.hpp +) + +target_link_libraries(nutri PRIVATE cpr::cpr ctre::ctre glaze::glaze) diff --git a/src/ArgParser.hpp b/src/ArgParser.hpp index b071406..3f96b6b 100644 --- a/src/ArgParser.hpp +++ b/src/ArgParser.hpp @@ -1,41 +1,43 @@ #pragma once +#include #include +#include #include #include #include class ArgParser { public: - void addArg(const std::string& shortOpt, const std::string& longOpt, const std::string& description, bool hasValue = true) { - m_arguments.emplace_back(shortOpt, longOpt, description, "", hasValue); - if (!shortOpt.empty()) { - m_shortOptMap[shortOpt] = &m_arguments.back(); - } - if (!longOpt.empty()) { - m_longOptMap[longOpt] = &m_arguments.back(); - } + void add(const std::string& shortOpt, const std::string& longOpt, const std::string& description, bool requiresValue = true) { + Argument arg{shortOpt, longOpt, description, std::nullopt, requiresValue, false}; + m_arguments.push_back(arg); + if (!shortOpt.empty()) + m_optMap[shortOpt] = &m_arguments.back(); + if (!longOpt.empty()) + m_optMap[longOpt] = &m_arguments.back(); } - bool parse(const int argc, char* argv[]) { - for (int i{1}; i < argc; ++i) { - if (const std::string arg{argv[i]}; m_shortOptMap.contains(arg)) { - if (arg == "--help" || arg == "-h") { - printHelp(); - return false; - } - m_shortOptMap[arg]->exists = true; - if (m_shortOptMap[arg]->hasValue && i + 1 < argc) { - m_shortOptMap[arg]->value = argv[++i]; - } - } else if (m_longOptMap.contains(arg)) { - if (arg == "--help" || arg == "-h") { - printHelp(); - return false; - } - m_longOptMap[arg]->exists = true; - if (m_longOptMap[arg]->hasValue && i + 1 < argc) { - m_longOptMap[arg]->value = argv[++i]; + bool parse(int argc, char* argv[]) { + if (argc == 1) { + printHelp(); + return false; + } + + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + + if (arg == "-h" || arg == "--help") { + printHelp(); + return false; + } + + if (auto* argument = findArgument(arg); argument) { + argument->exists = true; + if (argument->requiresValue) { + if (i + 1 >= argc) + throw std::invalid_argument("Missing value for " + arg); + argument->value = argv[++i]; } } else { throw std::invalid_argument("Unknown option: " + arg); @@ -44,6 +46,20 @@ public: return true; } + template + T get(const std::string& arg) const { + const Argument* argument = findArgument(arg); + if (!argument || !argument->value) + throw std::runtime_error(arg + " has no value"); + + return convertToType(*argument->value); + } + + bool has(const std::string& arg) const { + const Argument* argument = findArgument(arg); + return argument && argument->exists; + } + void printHelp() const { std::println("Usage: [options]"); for (const auto& arg : m_arguments) { @@ -51,44 +67,41 @@ public: } } - template - T get(const std::string& arg) const { - std::string value; - if (m_longOptMap.contains(arg) && !m_longOptMap.at(arg)->value.empty()) { - value = m_longOptMap.at(arg)->value; - } else if (m_shortOptMap.contains(arg) && !m_shortOptMap.at(arg)->value.empty()) { - value = m_shortOptMap.at(arg)->value; - } else { - throw std::runtime_error(arg + " has no value"); - } - if constexpr (std::is_same_v) { - return value; - } else { - return std::stoi(value); - } - } - - bool has(const std::string& arg) const { - if (const auto longOptIt = m_longOptMap.find(arg); longOptIt != m_longOptMap.end()) { - return longOptIt->second->exists; - } - if (const auto shortOptIt = m_shortOptMap.find(arg); shortOptIt != m_shortOptMap.end()) { - return shortOptIt->second->exists; - } - return false; - } - private: struct Argument { - std::string shortOpt; - std::string longOpt; - std::string description; - std::string value; - bool hasValue = true; - bool exists = false; + std::string shortOpt; + std::string longOpt; + std::string description; + std::optional value; + bool requiresValue; + bool exists = false; }; - std::vector m_arguments{}; - std::unordered_map m_shortOptMap{}; - std::unordered_map m_longOptMap{}; + Argument* findArgument(const std::string& opt) { + if (auto it = m_optMap.find(opt); it != m_optMap.end()) + return it->second; + return nullptr; + } + + const Argument* findArgument(const std::string& opt) const { + if (auto it = m_optMap.find(opt); it != m_optMap.end()) + return it->second; + return nullptr; + } + + template + T convertToType(const std::string& value) const { + if constexpr (std::is_same_v) { + return value; + } else if constexpr (std::is_same_v) { + return std::stoi(value); + } else if constexpr (std::is_same_v) { + return std::stod(value); + } else { + throw std::runtime_error("Unsupported type for argument conversion"); + } + } + + std::vector m_arguments; + std::unordered_map m_optMap; }; diff --git a/src/Helpers/WolframAlpha.hpp b/src/Helpers/WolframAlpha.hpp index d2cf51f..8472742 100644 --- a/src/Helpers/WolframAlpha.hpp +++ b/src/Helpers/WolframAlpha.hpp @@ -6,11 +6,10 @@ #include #include #include +#include #include #include -// Example regex patern: (?<=protein\s)(\d+)\s(\w) - struct Data { double cals{}; double proteins{}; @@ -20,61 +19,71 @@ struct Data { class WolframAlpha { public: - WolframAlpha(const std::string& query, double amount) : m_plaintext{getData(query)}, m_amount{amount / 100} {} - - //debug - std::string getPlain() { - return m_plaintext; + WolframAlpha(const std::string& query, double amount) : m_plaintext{initPlaintext(query)}, m_amount{amount} { + initData(); } - Data getNutrition() { - return m_data; + void print() { + std::println("Cals: {:g} kcal\nProtein: {:g} g\nCarbs: {:g} g\nFats: {:g} g", m_data.cals, m_data.proteins, m_data.carbs, m_data.fats); } private: - void getMacro() { - static constexpr ctll::fixed_string calories_regex{R"((?<=total calories\s)\d+)"}; - static constexpr ctll::fixed_string proteins_regex{R"((?<=protein\s)(\d+)\s(\w))"}; - static constexpr ctll::fixed_string carbs_regex{R"((?<=total carbohydrates\s)(\d+)\s(\w))"}; - static constexpr ctll::fixed_string fats_regex{R"((?<=total fat\s)(\d+)\s(\w))"}; - - const auto convertToGrams = [this](const auto& match, const std::string& unit) -> double { - const double value = (unit == "g") ? match.to_number() : match.to_number() / 1000; - return value * m_amount; + void initData() { + const auto convertToGrams = [this](const double& match, const std::string& unit) { + if (unit == "g") { + return match * m_amount; + } else if (unit == "mg") { + return (match / 1000) * m_amount; + } else { + throw std::invalid_argument("Unsupported unit: " + unit); + } }; + constexpr ctll::fixed_string calories_regex{R"((?<=total calories\s)\d+)"}; + constexpr ctll::fixed_string proteins_regex{R"((?<=protein\s)(\d+)\s(\w+))"}; + constexpr ctll::fixed_string carbs_regex{R"((?<=total carbohydrates\s)(\d+)\s(\w+))"}; + constexpr ctll::fixed_string fats_regex{R"((?<=total fat\s)(\d+)\s(\w+))"}; + if (auto match = ctre::search(m_plaintext); match) { - m_data.proteins = convertToGrams(match.get<1>(), match.get<2>().to_string()); + m_data.proteins = convertToGrams(match.get<1>().to_number(), match.get<2>().to_string()); } if (auto match = ctre::search(m_plaintext); match) { - m_data.carbs = convertToGrams(match.get<1>(), match.get<2>().to_string()); + m_data.carbs = convertToGrams(match.get<1>().to_number(), match.get<2>().to_string()); } if (auto match = ctre::search(m_plaintext); match) { - m_data.fats = convertToGrams(match.get<1>(), match.get<2>().to_string()); + m_data.fats = convertToGrams(match.get<1>().to_number(), match.get<2>().to_string()); } if (auto match = ctre::search(m_plaintext); match) { - m_data.cals = match.get<0>().to_number(); + m_data.cals = match.get<0>().to_number() * m_amount; } } - std::string getData(const std::string& query) { - const auto WOLFRAM_KEY = utl::getEnv("NUTRI_WA_KEY"); - cpr::Response response = Get(cpr::Url{"https://api.wolframalpha.com/v2/query"}, - cpr::Parameters{{"input", query + "100g nutrition"}, {"appid", WOLFRAM_KEY}, {"output", "JSON"}, {"format", "plaintext"}}); - glz::json_t json{}; - // Pretty sure this is wrong and i always is false - if (glz::read_json(json, response.text)) { - throw std::runtime_error("Failed to parse the JSON"); - } else { + std::string initPlaintext(const std::string& query) { + const auto WOLFRAM_KEY = utl::getEnv("NUTRI_WA_KEY"); + + // clang-format off + cpr::Response response = Get( + cpr::Url{"https://api.wolframalpha.com/v2/query"}, + cpr::Parameters{ + {"input", query + "100g nutrition"}, + {"appid", WOLFRAM_KEY}, + {"output", "JSON"}, + {"format", "plaintext"}}); + // clang-format on + + glz::json_t json{}; + auto ec = glz::read_json(json, response.text); + if (ec != glz::error_code::missing_key) { return json["queryresult"]["pods"][1]["subpods"][0]["plaintext"].get(); + } else { + throw std::invalid_argument("JSON Key not found"); } } Data m_data{}; - std::string m_pod{}; std::string m_plaintext{}; double m_amount{}; }; diff --git a/src/main.cpp b/src/main.cpp index fbf3916..2f8f998 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,19 +5,20 @@ int main(const int argc, char* argv[]) { try { ArgParser args; - args.addArg("-f", "--food", "Specify the food item"); - args.addArg("-a", "--amount", "Specify the amount (in grams)"); - args.addArg("-h", "--help", "Print help", false); - args.parse(argc, argv); + args.add("-h", "--help", "Print help", false); + args.add("-f", "--food", "Specify the food item"); + args.add("-a", "--amount", "Specify the amount (in grams)"); + + if (!args.parse(argc, argv)) { + return 0; + } const auto food = args.get("--food"); const double amount = args.get("--amount") / 100; WolframAlpha wa(food, amount); - auto a = wa.getNutrition(); - std::println("cals: {}\nprotein: {}\ncarbs: {}\nfat: {}", a.cals, a.proteins, a.carbs, a.fats); - + wa.print(); } catch (const std::exception& e) { std::println(stderr, "Error: {}", e.what()); return 1; -- 2.34.1 From 377383d5a09eec9d5f2d1c5ebe6d8bd502975475 Mon Sep 17 00:00:00 2001 From: etenie Date: Sun, 10 Nov 2024 19:33:18 +0100 Subject: [PATCH 9/9] fix std::get error on invalid query --- src/Helpers/WolframAlpha.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Helpers/WolframAlpha.hpp b/src/Helpers/WolframAlpha.hpp index 8472742..22dbda2 100644 --- a/src/Helpers/WolframAlpha.hpp +++ b/src/Helpers/WolframAlpha.hpp @@ -76,10 +76,10 @@ private: glz::json_t json{}; auto ec = glz::read_json(json, response.text); - if (ec != glz::error_code::missing_key) { + if (ec != glz::error_code::parse_error && json["queryresult"]["success"].get_boolean()) { return json["queryresult"]["pods"][1]["subpods"][0]["plaintext"].get(); } else { - throw std::invalid_argument("JSON Key not found"); + throw std::invalid_argument("WolframAlpha query failed"); } } -- 2.34.1