diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 0f1a82e..43c4947 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -68,3 +68,12 @@ if (NOT TARGET solanaceae_tox_contacts AND NOT TARGET solanaceae_tox_messages) FetchContent_MakeAvailable(solanaceae_tox) endif() +if (NOT TARGET nlohmann_json::nlohmann_json) + FetchContent_Declare(json + URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz + URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d + EXCLUDE_FROM_ALL + ) + FetchContent_MakeAvailable(json) +endif() + diff --git a/flake.nix b/flake.nix index b044835..fc56154 100644 --- a/flake.nix +++ b/flake.nix @@ -25,8 +25,8 @@ }; solanaceae_util-src = pkgs.fetchFromGitHub { owner = "Green-Sky"; repo = "solanaceae_util"; - rev = "92eee153f2c14f97e50f44b10e2a0aeb5f8b190d"; - hash = "sha256-otzjzY8lxanKlryahRe+S0UL7yXsRKDJbh2DYNV71QM="; + rev = "57d7178e76b41c5d0f8117fc8fb5791b4108cdc0"; + hash = "sha256-CjBj1iYlJsLMsZvp857FrsmStV4AJ7SD7L+hzOgZMpg="; }; solanaceae_contact-src = pkgs.fetchFromGitHub { owner = "Green-Sky"; repo = "solanaceae_contact"; @@ -70,13 +70,14 @@ cmakeFlags = [ #"-DFETCHCONTENT_SOURCE_DIR_TOXCORE=${pkgs.libtoxcore.src}" "-DFETCHCONTENT_SOURCE_DIR_TOXCORE=${toxcore-src}" - "-DFETCHCONTENT_SOURCE_DIR_ENTT=${entt-src}" + "-DFETCHCONTENT_SOURCE_DIR_ENTT=${entt-src}" # the version is important "-DFETCHCONTENT_SOURCE_DIR_SOLANACEAE_UTIL=${solanaceae_util-src}" "-DFETCHCONTENT_SOURCE_DIR_SOLANACEAE_CONTACT=${solanaceae_contact-src}" "-DFETCHCONTENT_SOURCE_DIR_SOLANACEAE_MESSAGE3=${solanaceae_message3-src}" "-DFETCHCONTENT_SOURCE_DIR_SOLANACEAE_PLUGIN=${solanaceae_plugin-src}" "-DFETCHCONTENT_SOURCE_DIR_SOLANACEAE_TOXCORE=${solanaceae_toxcore-src}" "-DFETCHCONTENT_SOURCE_DIR_SOLANACEAE_TOX=${solanaceae_tox-src}" + "-DFETCHCONTENT_SOURCE_DIR_JSON=${pkgs.nlohmann_json.src}" # we care less about version here ]; buildInputs = with pkgs; [ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fd773c8..09b1909 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,5 +20,7 @@ target_link_libraries(totato PUBLIC solanaceae_toxcore solanaceae_tox_contacts solanaceae_tox_messages + + nlohmann_json::nlohmann_json ) diff --git a/src/main.cpp b/src/main.cpp index a2f1d10..2a39214 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,10 +13,14 @@ #include "./tox_client.hpp" #include "./auto_dirty.hpp" +#include + #include #include #include #include +#include +#include #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) #include @@ -35,6 +39,65 @@ void sigint_handler(int signo) { } #endif +bool load_json_into_config(const nlohmann::ordered_json& config_json, SimpleConfigModel& conf) { + if (!config_json.is_object()) { + std::cout << "TOTATO error: config file is not an json object!!!\n"; + return false; + } + for (const auto& [mod, cats] : config_json.items()) { + for (const auto& [cat, cat_v] : cats.items()) { + if (cat_v.is_object()) { + if (cat_v.contains("default")) { + const auto& value = cat_v["default"]; + if (value.is_string()) { + conf.set(mod, cat, value.get_ref()); + } else if (value.is_boolean()) { + conf.set(mod, cat, value.get_ref()); + } else if (value.is_number_float()) { + conf.set(mod, cat, value.get_ref()); + } else if (value.is_number_integer()) { + conf.set(mod, cat, value.get_ref()); + } else { + std::cerr << "JSON error: wrong value type in " << mod << "::" << cat << " = " << value << "\n"; + return false; + } + } + if (cat_v.contains("entries")) { + for (const auto& [ent, ent_v] : cat_v["entries"].items()) { + if (ent_v.is_string()) { + conf.set(mod, cat, ent, ent_v.get_ref()); + } else if (ent_v.is_boolean()) { + conf.set(mod, cat, ent, ent_v.get_ref()); + } else if (ent_v.is_number_float()) { + conf.set(mod, cat, ent, ent_v.get_ref()); + } else if (ent_v.is_number_integer()) { + conf.set(mod, cat, ent, ent_v.get_ref()); + } else { + std::cerr << "JSON error: wrong value type in " << mod << "::" << cat << "::" << ent << " = " << ent_v << "\n"; + return false; + } + } + } + } else { + if (cat_v.is_string()) { + conf.set(mod, cat, cat_v.get_ref()); + } else if (cat_v.is_boolean()) { + conf.set(mod, cat, cat_v.get_ref()); + } else if (cat_v.is_number_float()) { + conf.set(mod, cat, cat_v.get_ref()); + } else if (cat_v.is_number_integer()) { + conf.set(mod, cat, cat_v.get_ref()); + } else { + std::cerr << "JSON error: wrong value type in " << mod << "::" << cat << " = " << cat_v << "\n"; + return false; + } + } + } + } + + return true; +} + int main(int argc, char** argv) { #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) struct sigaction sigint_action; @@ -45,23 +108,73 @@ int main(int argc, char** argv) { #elif defined (_WIN32) signal(SIGINT, sigint_handler); #endif + auto last_time = std::chrono::steady_clock::now(); - // TODO: parse arg - // totato -p + std::string config_path {"config.json"}; - // HACK: config - std::string tox_profile_path {"totato.tox"}; + // totato -p + // TODO: parse arg + if (argc == 2) { + config_path = argv[1]; + } SimpleConfigModel conf; + + { // read conf from json TODO: refactor extract this for reuse + auto config_file = std::ifstream(config_path); + if (!config_file.is_open()) { + std::cerr << "TOTATO error: failed to open config file '" << config_path << "', exiting...\n"; + return -1; + } + + auto config_json = nlohmann::ordered_json::parse(std::ifstream(config_path)); + if (!load_json_into_config(config_json, conf)) { + std::cerr << "TOTATO error in config json, exiting...\n"; + return -1; + } + } + + { // fill in defaults + // config file folder + const auto config_path_base = std::filesystem::path{config_path}.parent_path(); + + if (!conf.has_string("tox", "save_file_path")) { + // default to totato.tox relative to config file + conf.set("tox", "save_file_path", (config_path_base / "totato.tox").u8string()); + } else { // transform relative to config to absolute + const auto tox_conf_path = std::filesystem::path{conf.get_string("tox", "save_file_path").value()}; + if (tox_conf_path.has_relative_path()) { + // is relative to config + conf.set("tox", "save_file_path", std::filesystem::canonical(tox_conf_path).u8string()); + } + } + + // TODO: name + } + + conf.dump(); + Contact3Registry cr; RegistryMessageModel rmm{cr}; MessageTimeSort mts{rmm}; PluginManager pm; - ToxEventLogger tel{std::cout}; - ToxClient tc{tox_profile_path, ""}; + ToxEventLogger tel{std::cout}; // TODO: config + + // TODO: password? + ToxClient tc{conf.get_string("tox", "save_file_path").value(), ""}; + tel.subscribeAll(tc); + { // name stuff + auto name = tc.toxSelfGetName(); + if (name.empty()) { + name = "totato"; + } + conf.set("tox", "name", name); + tc.setSelfName(name); // TODO: this is ugly + } + ToxPrivateImpl tpi{tc.getTox()}; AutoDirty ad{tc}; ToxContactModel2 tcm{cr, tc, tc}; @@ -73,14 +186,15 @@ int main(int argc, char** argv) { g_provideInstance("Contact3Registry", "host", &cr); g_provideInstance("RegistryMessageModel", "host", &rmm); - //g_provideInstance("ToxI", "host", &tc); - //g_provideInstance("ToxPrivateI", "host", &tpi); - //g_provideInstance("ToxEventProviderI", "host", &tc); - //g_provideInstance("ToxContactModel2", "host", &tcm); + g_provideInstance("ToxI", "host", &tc); + g_provideInstance("ToxPrivateI", "host", &tpi); + g_provideInstance("ToxEventProviderI", "host", &tc); + g_provideInstance("ToxContactModel2", "host", &tcm); // TODO: pm? } + // load from config!!! for (const auto& ppath : {"../../solanaceae_ecosystem/build/bin/libplugin_transfer_auto_accept.so"}) { if (!pm.add(ppath)) { std::cerr << "TOTATO error: loading plugin '" << ppath << "' failed!\n";