#include "command2.h" #include "escape.h" #include #include using namespace std; using namespace ts; #define CONVERTER_ST(type, m_decode, m_encode) \ CONVERTER_METHOD_DECODE(type, impl::converter_ ##type ##_decode) { \ m_decode; \ } \ \ CONVERTER_METHOD_ENCODE(type, impl::converter_ ##type ##_encode) { \ m_encode \ } #define CONVERTER_PRIMITIVE_ST(type, m_decode) CONVERTER_ST(type, \ return m_decode;, \ return std::to_string(std::any_cast(value)); \ ) CONVERTER_PRIMITIVE_ST(int8_t, std::stol(str) & 0xFF); CONVERTER_PRIMITIVE_ST(uint8_t, std::stoul(str) & 0xFF); CONVERTER_PRIMITIVE_ST(int16_t, std::stol(str) & 0xFFFF); CONVERTER_PRIMITIVE_ST(uint16_t, std::stoul(str) & 0xFFFF); CONVERTER_PRIMITIVE_ST(int32_t, std::stol(str) & 0xFFFFFFFF); CONVERTER_PRIMITIVE_ST(uint32_t, std::stoul(str) & 0xFFFFFFFF); CONVERTER_PRIMITIVE_ST(int64_t, std::stoll(str)); CONVERTER_PRIMITIVE_ST(uint64_t, std::stoull(str)) CONVERTER_PRIMITIVE_ST(bool, str == "1" || str == "true"); CONVERTER_PRIMITIVE_ST(float, std::stof(str)); CONVERTER_PRIMITIVE_ST(double, std::stod(str)); CONVERTER_PRIMITIVE_ST(long_double, std::stold(str)); CONVERTER_ST(std__string, return str;, return std::any_cast(value);); CONVERTER_ST(const_char__ , return str.c_str();, return std::string(std::any_cast(value));); command::command(const std::string& command, bool editable) { this->handle = make_shared(); this->handle->editiable = editable; this->handle->command = command; } std::string command::identifier() const { return this->handle->command; } void command::set_identifier(const std::string &command) { this->handle->command = command; } command_bulk command::bulk(size_t index) { if(this->handle->bulks.size() <= index) { if(!this->handle->editiable) throw command_bulk_exceed_index_exception(); while(this->handle->bulks.size() <= index) { auto bulk = make_shared(); bulk->handle = this->handle.get(); this->handle->bulks.push_back(std::move(bulk)); } } return {index, this->handle->bulks[index]}; } const command_bulk command::bulk(size_t index) const { if(this->handle->bulks.size() <= index) throw command_bulk_exceed_index_exception(); return {index, this->handle->bulks[index]}; } size_t command::bulk_count() const { return this->handle->bulks.size(); } command_entry command::value(const std::string &key) { return this->bulk(0)[key]; } const command_entry command::value(const std::string &key) const { return this->bulk(0)[key]; } bool command::has_value(const std::string &key) const { if(this->bulk_count() == 0) return false; return this->bulk(0).has(key); } bool command::has_trigger(const std::string &key) const { for(const auto& trigger : this->handle->triggers) if(trigger == key) return true; return false; } void command::set_trigger(const std::string &key, bool value) { auto& triggers = this->handle->triggers; auto it = find(triggers.begin(), triggers.end(), key); if(it == triggers.end() && value) { triggers.push_back(key); } else if(it != triggers.end() && !value) { triggers.erase(it); } } command command::parse(const std::string_view &data, bool expect_type, bool drop_non_utf8) { command result; size_t current_index = std::string::npos, end_index; if(expect_type) { current_index = data.find(' ', 0); if(current_index == std::string::npos){ result.set_identifier(std::string(data)); return result; } else { result.set_identifier(std::string(data.substr(0, current_index))); } } size_t bulk_index = 0; while(++current_index > 0 || (current_index == 0 && !expect_type && (expect_type = true))) { end_index = data.find_first_of(" |", current_index); if(end_index != current_index) { /* else we've found another space or a pipe */ if(data[current_index] == '-') { string trigger(data.substr(current_index + 1, end_index - current_index - 1)); result.set_trigger(trigger); } else { auto index_assign = data.find_first_of('=', current_index); string key, value; if(index_assign == string::npos || index_assign > end_index) { key = data.substr(current_index, end_index - current_index); } else { key = data.substr(current_index, index_assign - current_index); try { value = query::unescape(string(data.substr(index_assign + 1, end_index - index_assign - 1)), true); } catch(std::invalid_argument& ex) { /* invalid character at index X */ if(!drop_non_utf8) throw; goto skip_assign; } } { const static auto key_validator = [](char c){ return !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-'); }; auto invalid_index = find_if(key.begin(), key.end(), key_validator); if(invalid_index != key.end()) throw command_malformed_exception(current_index + distance(key.begin(), invalid_index)); if(!key.empty() && !result[bulk_index].has(key)) result[bulk_index][key] = value; } skip_assign:; } } if(end_index < data.length() && data[end_index] == '|') bulk_index++; current_index = end_index; } return result; } std::string command::build(ts::command::format::value type) { if(type == format::QUERY || type == format::BRACE_ESCAPED_QUERY) { std::stringstream ss; if(!this->handle->command.empty()) ss << this->handle->command << " "; if(!this->handle->bulks.empty()) { auto max_bulk_index = this->handle->bulks.size() - 1; for(size_t bulk_index = 0; bulk_index <= max_bulk_index; bulk_index++) { auto& bulk = this->handle->bulks[bulk_index]; if(bulk->values.empty()) continue; //Do not remove me! auto max_pair_index = bulk->values.size() - 1; auto pair_it = bulk->values.begin(); for(size_t pair_index = 0; pair_index <= max_pair_index; pair_index++) { auto pair = *(pair_it++); auto value = pair.second->casted ? pair.second->to_string(pair.second->value) : pair.second->value.has_value() ? any_cast(pair.second->value) : ""; ss << pair.first << "="; if(type == format::BRACE_ESCAPED_QUERY) { ss << "\"" << value << "\""; } else ss << query::escape(value); if(pair_index != max_pair_index) ss << " "; } if(bulk_index != max_bulk_index) ss << " | "; } } if(!this->handle->triggers.empty()) { auto max_trigger_index = this->handle->triggers.size() - 1; for(size_t trigger_index = 0; trigger_index <= max_trigger_index; trigger_index++) { ss << " -" << this->handle->triggers[trigger_index]; } } return ss.str(); } return ""; } bool command_bulk::has(const std::string &key) const { auto& values = this->handle->values; auto index = values.find(key); return index != values.end(); } command_entry command_bulk::value(const std::string &key) { auto& values = this->handle->values; auto index = values.find(key); if(index == values.end()) { if(!this->handle->handle->editiable) throw command_value_missing_exception(this->bulk_index, key); auto result = make_shared(); values[key] = result; return command_entry(result); } return command_entry(index->second); } const command_entry command_bulk::value(const std::string &key) const { auto& values = this->handle->values; auto index = values.find(key); if(index == values.end()) throw command_value_missing_exception(this->bulk_index, key); return command_entry(index->second); } command_entry command_entry::empty{};