Mercurial > minori
view dep/animone/src/player.cc @ 327:b5d6c27c308f
anime: refactor Anime::SeriesSeason to Season class
ToLocalString has also been altered to take in both season
and year because lots of locales actually treat formatting
seasons differently! most notably is Russian which adds a
suffix at the end to notate seasons(??)
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Thu, 13 Jun 2024 01:49:18 -0400 |
parents | b1f625b0227c |
children | a7d4e5107531 |
line wrap: on
line source
#include "animone/player.h" #include "animone/util.h" #include <map> #include <sstream> #include <string> #include <vector> namespace animone { namespace internal::parser { enum class State { ExpectPlayerName, ExpectSection, ExpectWindow, ExpectExecutable, ExpectStrategy, ExpectType, ExpectWindowTitle, }; size_t GetIndentation(const std::string& line) { return line.find_first_not_of('\t'); } bool HandleIndentation(const size_t current, const std::vector<Player>& players, State& state) { // Each state has a definitive expected indentation const auto expected = [&state]() -> size_t { switch (state) { default: case State::ExpectPlayerName: return 0; case State::ExpectSection: return 1; case State::ExpectWindow: case State::ExpectExecutable: case State::ExpectStrategy: case State::ExpectType: return 2; case State::ExpectWindowTitle: return 3; } }(); if (current > expected) return false; // Disallow excessive indentation if (current < expected) { auto fix_state = [&]() { state = !current ? State::ExpectPlayerName : State::ExpectSection; }; switch (state) { case State::ExpectWindow: if (players.back().windows.empty()) return false; fix_state(); break; case State::ExpectExecutable: if (players.back().executables.empty()) return false; fix_state(); break; case State::ExpectStrategy: if (players.back().strategies.empty()) return false; fix_state(); break; case State::ExpectType: fix_state(); break; case State::ExpectWindowTitle: return false; } } return true; } bool HandleState(std::string& line, std::vector<Player>& players, State& state) { switch (state) { case State::ExpectPlayerName: players.push_back(Player()); players.back().name = line; state = State::ExpectSection; break; case State::ExpectSection: { static const std::map<std::string, State> sections = { {"windows", State::ExpectWindow }, {"executables", State::ExpectExecutable}, {"strategies", State::ExpectStrategy }, {"type", State::ExpectType }, }; util::TrimRight(line, ":"); const auto it = sections.find(line); if (it == sections.end()) return false; state = it->second; break; } case State::ExpectWindow: players.back().windows.push_back(line); break; case State::ExpectExecutable: players.back().executables.push_back(line); break; case State::ExpectStrategy: { static const std::map<std::string, Strategy> strategies = { {"window_title", Strategy::WindowTitle }, {"open_files", Strategy::OpenFiles }, {"ui_automation", Strategy::UiAutomation}, }; util::TrimRight(line, ":"); const auto it = strategies.find(line); if (it == strategies.end()) return false; const auto strategy = it->second; players.back().strategies.push_back(strategy); switch (strategy) { case Strategy::WindowTitle: state = State::ExpectWindowTitle; break; } break; } case State::ExpectType: { static const std::map<std::string, PlayerType> types = { {"default", PlayerType::Default }, {"web_browser", PlayerType::WebBrowser}, }; const auto it = types.find(line); if (it == types.end()) return false; players.back().type = it->second; break; } case State::ExpectWindowTitle: players.back().window_title_format = line; state = State::ExpectStrategy; break; } return true; } } // namespace internal::parser //////////////////////////////////////////////////////////////////////////////// bool ParsePlayersData(const std::string& data, std::vector<Player>& players) { if (data.empty()) return false; std::istringstream stream(data); std::string line; size_t indentation = 0; auto state = internal::parser::State::ExpectPlayerName; while (std::getline(stream, line, '\n')) { if (line.empty()) continue; // Ignore empty lines indentation = internal::parser::GetIndentation(line); internal::util::TrimLeft(line, "\t"); internal::util::TrimRight(line, "\n\r"); if (line.empty() || line.front() == '#') continue; // Ignore empty lines and comments if (!internal::parser::HandleIndentation(indentation, players, state)) return false; if (!internal::parser::HandleState(line, players, state)) return false; } return !players.empty(); } bool ParsePlayersFile(const std::string& path, std::vector<Player>& players) { std::string data; if (!internal::util::ReadFile(path, data)) return false; return ParsePlayersData(data, players); } } // namespace animone