diff src/core/config.cc @ 318:3b355fa948c7

config: use TOML instead of INI unfortunately, INI is not enough, and causes some paths including semicolons to break with our current storage of the library folders. so, I decided to switch to TOML which does support real arrays...
author Paper <paper@paper.us.eu.org>
date Wed, 12 Jun 2024 05:25:41 -0400
parents b1f4d1867ab1
children 1b5c04268d6a
line wrap: on
line diff
--- a/src/core/config.cc	Wed Jun 12 04:07:10 2024 -0400
+++ b/src/core/config.cc	Wed Jun 12 05:25:41 2024 -0400
@@ -1,11 +1,6 @@
-/**
- * config.cpp:
- * parses the config... lol
- **/
 #include "core/config.h"
 #include "core/anime.h"
 #include "core/filesystem.h"
-#include "core/ini.h"
 #include "core/json.h"
 #include "core/strings.h"
 #include "gui/translate/anime.h"
@@ -20,59 +15,60 @@
 #include <fstream>
 #include <limits.h>
 
+#include <toml11/toml.hpp>
+
 #include <QFile>
 #include <QTextStream>
 
 #include <iostream>
 
-/* I'll use an INI-based config file instead of using an
- * XML file like Taiga.
- *
- * It technically isn't to spec, because I'm making these case-sensitive.
- * Boohoo.
- */
+/* NOTE: This config file is prone to breakage, as Minori is alpha software and
+ * as such nothing is constant. */
 
 int Config::Load() {
 	std::filesystem::path cfg_path = Filesystem::GetConfigPath();
 
-	mINI::INIFile file(cfg_path.u8string());
-	mINI::INIStructure ini;
-	file.read(ini);
+	std::ifstream ifs(cfg_path, std::ios_base::binary);
+	if (!ifs.good())
+		return 0;
 
-	service = Translate::ToService(INI::GetIniString(ini, "General", "Service", "None"));
+	toml::value data;
 
-	anime_list.score_format =
-	    Translate::ToScoreFormat(INI::GetIniString(ini, "Anime List", "Score format", "POINT_100"));
-	anime_list.language =
-	    Translate::ToLanguage(INI::GetIniString(ini, "Anime List", "Title language", "Romaji"));
-	anime_list.display_aired_episodes = INI::GetIniBool(ini, "Anime List", "Display only aired episodes", true);
-	anime_list.display_available_episodes =
-	    INI::GetIniBool(ini, "Anime List", "Display only available episodes in library", true);
-	anime_list.highlight_anime_if_available =
-	    INI::GetIniBool(ini, "Anime List", "Highlight anime if available", true);
+	try {
+		data = toml::parse(ifs);
+	} catch (const std::exception& ex) {
+		std::cerr << "config: failed to parse toml with error " << ex.what() << std::endl;
+		return 0;
+	}
+
+	service = Translate::ToService(toml::find_or(data, "General", "Service", "None"));
+	locale.RefreshAvailableLocales();
+	locale.SetActiveLocale(QLocale(Strings::ToQString(toml::find_or(data, "General", "Locale", "en_US"))));
+
 
-	if (anime_list.highlight_anime_if_available) // sanity check
-		anime_list.highlighted_anime_above_others =
-		    INI::GetIniBool(ini, "Anime List", "Display highlighted anime above others", false);
-	else
-		anime_list.highlighted_anime_above_others = false;
-
-	auth.anilist.auth_token = INI::GetIniString(ini, "Authentication/AniList", "Auth Token", "");
-	auth.anilist.user_id = INI::GetIniInteger<int>(ini, "Authentication/AniList", "User ID", 0);
+	anime_list.score_format = Translate::ToScoreFormat(toml::find_or(data, "Anime List", "Score format", "100-point"));
+	anime_list.language = Translate::ToLanguage(toml::find_or(data, "Anime List", "Title language", "Romaji"));
+	anime_list.display_aired_episodes = toml::find_or<bool>(data, "Anime List", "Display only aired episodes", true);
+	anime_list.display_available_episodes = toml::find_or<bool>(data, "Anime List", "Display only available episodes in library", true);
+	anime_list.highlight_anime_if_available = toml::find_or<bool>(data, "Anime List", "Highlight anime if available", true);
+	anime_list.highlighted_anime_above_others =
+		(anime_list.highlight_anime_if_available)
+		? toml::find_or<bool>(data, "Anime List", "Display highlighted anime above others", false)
+		: false;
 
-	auth.kitsu.access_token = INI::GetIniString(ini, "Authentication/Kitsu", "Access Token", "");
-	auth.kitsu.access_token_expiration = INI::GetIniInteger<Time::Timestamp>(ini, "Authentication/Kitsu", "Access Token Expiration", 0);
-	auth.kitsu.refresh_token = INI::GetIniString(ini, "Authentication/Kitsu", "Refresh Token", "");
-	auth.kitsu.user_id = INI::GetIniString(ini, "Authentication/Kitsu", "User ID", "");
+	auth.anilist.auth_token = toml::find_or(data, "Authentication/AniList", "Auth Token", "");
+	auth.anilist.user_id = toml::find_or<int>(data, "Authentication/AniList", "User ID", 0);
 
-	torrents.feed_link = INI::GetIniString(ini, "Torrents", "RSS feed",
-	                                       "https://www.tokyotosho.info/rss.php?filter=1,11&zwnj=0");
+	auth.kitsu.access_token = toml::find_or(data, "Authentication/Kitsu", "Access Token", "");
+	auth.kitsu.access_token_expiration = toml::find_or(data, "Authentication/Kitsu", "Access Token Expiration", 0LL);
+	auth.kitsu.refresh_token = toml::find_or(data, "Authentication/Kitsu", "Refresh Token", "");
+	auth.kitsu.user_id = toml::find_or(data, "Authentication/Kitsu", "User ID", "");
 
-	recognition.detect_media_players = INI::GetIniBool(ini, "Recognition", "Detect media players", true);
+	torrents.feed_link = toml::find_or(data, "Torrents", "RSS feed", "https://www.tokyotosho.info/rss.php?filter=1,11&zwnj=0");
 
-	/* lots of dumb logic to import the player data */
+	recognition.detect_media_players = toml::find_or(data, "Recognition", "Detect media players", true);
+
 	{
-		/* load the player data */
 		QFile f(":/players.anisthesia");
 		if (!f.exists())
 			return false;
@@ -94,28 +90,26 @@
 		switch (player.type) {
 			default:
 			case animone::PlayerType::Default:
-				enabled = INI::GetIniBool(ini, "Recognition/Players", player.name, true);
+				enabled = toml::find_or<bool>(data, "Recognition/Players", player.name, true);
 				break;
 			case animone::PlayerType::WebBrowser:
-				enabled = INI::GetIniBool(ini, "Recognition/Browsers", player.name, true);
+				enabled = toml::find_or<bool>(data, "Recognition/Browsers", player.name, true);
 				break;
 		}
 	}
 
-	locale.RefreshAvailableLocales();
-	locale.SetActiveLocale(
-	    QLocale(Strings::ToQString(INI::GetIniString(ini, "General", "Locale", "en_US"))));
+
+	theme.SetTheme(Translate::ToTheme(toml::find_or(data, "Appearance", "Theme", "Default")));
 
-	theme.SetTheme(Translate::ToTheme(INI::GetIniString(ini, "Appearance", "Theme", "Default")));
-
-	{
-		std::vector<std::string> v = Strings::Split(INI::GetIniString(ini, "Library", "Folders", ""), ";");
+	if (data.contains("Library") && data["Library"].contains("Folders")) {
+		const toml::value& folders = toml::find(data, "Library", "Folders");
+		std::vector<std::string> v = toml::get_or<std::vector<std::string>>(folders, {});
 		for (const auto& s : v)
 			if (!library.paths.count(s))
 				library.paths.insert(s);
 	}
 
-	library.real_time_monitor = INI::GetIniBool(ini, "Library", "Real-time monitor", true);
+	library.real_time_monitor = toml::find_or(data, "Library", "Real-time monitor", true);
 
 	return 0;
 }
@@ -124,42 +118,45 @@
 	std::filesystem::path cfg_path = Filesystem::GetConfigPath();
 	Filesystem::CreateDirectories(cfg_path);
 
-	mINI::INIFile file(cfg_path.string());
-	mINI::INIStructure ini;
-
-	ini["General"]["Service"] = Translate::ToString(service);
-	ini["General"]["Locale"] = Strings::ToUtf8String(locale.GetLocale().name());
-
-	ini["Anime List"]["Score format"] = Translate::ToString(anime_list.score_format);
-	ini["Anime List"]["Title language"] = Translate::ToString(anime_list.language);
-	ini["Anime List"]["Display only aired episodes"] = Strings::ToUtf8String(anime_list.display_aired_episodes);
-	ini["Anime List"]["Display only available episodes in library"] = Strings::ToUtf8String(anime_list.display_available_episodes);
-	ini["Anime List"]["Highlight anime if available"] = Strings::ToUtf8String(anime_list.highlight_anime_if_available);
-	ini["Anime List"]["Display highlighted anime above others"] = Strings::ToUtf8String(anime_list.highlighted_anime_above_others);
+	std::ofstream file(cfg_path);
+	if (!file.good())
+		return 0;
 
-	ini["Authentication/AniList"]["Auth Token"] = auth.anilist.auth_token;
-	ini["Authentication/AniList"]["User ID"] = Strings::ToUtf8String(auth.anilist.user_id);
+	toml::value data;
 
-	ini["Authentication/Kitsu"]["Access Token"] = auth.kitsu.access_token;
-	ini["Authentication/Kitsu"]["Access Token Expiration"] = Strings::ToUtf8String(auth.kitsu.access_token_expiration);
-	ini["Authentication/Kitsu"]["Refresh Token"] = auth.kitsu.refresh_token;
-	ini["Authentication/Kitsu"]["User ID"] = auth.kitsu.user_id;
-
-	ini["Appearance"]["Theme"] = Translate::ToString(theme.GetTheme());
-
-	ini["Torrents"]["RSS feed"] = torrents.feed_link;
-
-	ini["Recognition"]["Detect media players"] = Strings::ToUtf8String(recognition.detect_media_players);
+	data["Library"]["Folders"] = library.paths;
+	data["Library"]["Real-time monitor"] = library.real_time_monitor;
 
 	for (const auto& [enabled, player] : recognition.players) {
 		const std::string section = (player.type == animone::PlayerType::WebBrowser) ? "Recognition/Players" : "Recognition/Browsers";
-		ini[section][player.name] = Strings::ToUtf8String(enabled);
+		data[section][player.name] = enabled;
 	}
 
-	ini["Library"]["Folders"] = Strings::Implode(library.paths, ";");
-	ini["Library"]["Real-time monitor"] = Strings::ToUtf8String(library.real_time_monitor);
+	data["Recognition"]["Detect media players"] = recognition.detect_media_players;
+
+	data["Torrents"]["RSS feed"] = torrents.feed_link;
+
+	data["Authentication/Kitsu"]["Access Token"] = auth.kitsu.access_token;
+	data["Authentication/Kitsu"]["Access Token Expiration"] = auth.kitsu.access_token_expiration;
+	data["Authentication/Kitsu"]["Refresh Token"] = auth.kitsu.refresh_token;
+	data["Authentication/Kitsu"]["User ID"] = auth.kitsu.user_id;
+
+	data["Authentication/AniList"]["Auth Token"] = auth.anilist.auth_token;
+	data["Authentication/AniList"]["User ID"] = auth.anilist.user_id;
 
-	file.write(ini);
+	data["Anime List"]["Score format"] = Translate::ToString(anime_list.score_format);
+	data["Anime List"]["Title language"] = Translate::ToString(anime_list.language);
+	data["Anime List"]["Display only aired episodes"] = anime_list.display_aired_episodes;
+	data["Anime List"]["Display only available episodes in library"] = anime_list.display_available_episodes;
+	data["Anime List"]["Highlight anime if available"] = anime_list.highlight_anime_if_available;
+	data["Anime List"]["Display highlighted anime above others"] = anime_list.highlighted_anime_above_others;
+
+	data["Appearance"]["Theme"] = Translate::ToString(theme.GetTheme());
+
+	data["General"]["Service"] = Translate::ToString(service);
+	data["General"]["Locale"] = Strings::ToUtf8String(locale.GetLocale().name());
+
+	file << std::setw(0) << data;
 
 	return 0;
 }