Mercurial > minori
view dep/animia/src/player.cc @ 198:bc1ae1810855
dep/animia: switch from using classes to global functions
the old idea was ok, but sort of hackish; this method doesn't use classes
at all, and this way (especially important!) we can do wayland stuff AND x11
at the same time, which wasn't really possible without stupid workarounds in
the other method
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Sun, 24 Dec 2023 02:59:42 -0500 |
parents | cdf79282d647 |
children |
line wrap: on
line source
#include "animia/player.h" #include "animia/util.h" #include <map> #include <sstream> #include <string> #include <vector> namespace animia { 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 animia