#pragma once #include #include #include #include #include #include #include #include #include "Error.h" #include "command_exception.h" #include "converters/converter.h" #ifdef WIN32 #define __attribute__used #else #define __attribute__used __attribute__((used)) #endif namespace ts { /* data impl stuff */ namespace impl { struct command_data; struct command_value { bool casted = false; /* true if value isn't a std::string */ std::any value; std::string(*to_string)(const std::any&); }; struct command_bulk { command_data* handle; std::map> values; }; struct command_data { std::string command; bool editable; std::deque> bulks; std::deque triggers; }; } /* Container stuff */ class command_bulk; class command_entry; class command { public: struct format { enum value { QUERY, BRACE_ESCAPED_QUERY, JSON }; }; static command parse(const std::string_view& /* command data */, bool /* expect type */ = true, bool /* drop non UTF-8 characters */ = false); explicit command(const std::string& /* command */ = "", bool /* editable */ = true); std::string identifier() const; void set_identifier(const std::string& /* command */); command_bulk bulk(size_t /* bulk index */); const command_bulk bulk(size_t /* bulk index */) const; size_t bulk_count() const; inline command_bulk operator[](size_t /* index */); inline const command_bulk operator[](size_t /* index */) const; command_entry value(const std::string& /* key */); const command_entry value(const std::string& /* key */) const; bool has_value(const std::string& /* key */) const; command_entry operator[](const std::string& /* key */); const command_entry operator[](const std::string& /* key */) const; bool has_trigger(const std::string& /* key */) const; void set_trigger(const std::string& /* key */, bool /* value */ = true); std::string build(format::value /* format */ = format::QUERY); /* TODO add a json object build method */ private: std::shared_ptr handle; }; class command_bulk { friend class command; public: bool has(const std::string& /* key */) const; command_entry value(const std::string& /* key */); command_entry const value(const std::string& /* key */) const; inline command_entry operator[](const std::string& /* key */); inline const command_entry operator[](const std::string& /* key */) const; private: command_bulk(size_t index, std::shared_ptr handle) : bulk_index(index), handle(std::move(handle)) {} size_t bulk_index; std::shared_ptr handle; }; class command_entry { public: static command_entry empty; command_entry() : handle(std::make_shared()) {} command_entry(const command_entry& ref) : handle(ref.handle) {} command_entry(command_entry&& ref) : handle(std::move(ref.handle)) {} command_entry&operator=(const command_entry& other) { this->handle = other.handle; return *this; } inline bool is_empty() const { return !this->handle->value.has_value(); } command_entry& reset() { this->handle->value.reset(); return *this; } const std::string string() const { if(this->is_empty()) return ""; if(!this->handle->casted || this->handle->value.type() == typeid(std::string)) //No cast needed return std::any_cast(this->handle->value); if(!this->handle->to_string) throw command_cannot_uncast_exception(); return this->handle->to_string(this->handle->value); } inline const std::string value() const { return (std::string) this->string(); } inline operator std::string() const { return this->string(); } template ::value, int> = 0> T as() { static_assert(converter::supported, "Target type isn't supported!"); static_assert(!converter::supported || converter::from_string, "Target type dosn't support parsing"); if(this->is_empty()) return T(); if(this->handle->casted) { if(this->handle->value.type() == typeid(T)) return std::any_cast(this->handle->value); else throw command_casted_exception(); } else { const auto& ref = std::any_cast(this->handle->value); this->handle->value = converter::from_string(ref); this->handle->to_string = converter::to_string; this->handle->casted = true; } return std::any_cast(this->handle->value); } template ::value, int> = 0> T as() { return this->string(); } template inline operator T() { return this->as(); } command_entry& melt() { if(this->handle->casted) { this->handle->value = this->handle->to_string(this->handle->value); this->handle->casted = false; this->handle->to_string = nullptr; } return *this; } template ::value && !std::is_same::value && !std::is_same::value, int>::type = 0> void set(const T& value) { static_assert(converter::supported, "Target type isn't supported!"); static_assert(!converter::supported || converter::to_string, "Target type dosn't support encode"); this->handle->casted = true; this->handle->value = std::move(value); this->handle->to_string = converter::to_string; } template ::value, int>::type = 0> void set(const T& value) { this->handle->value = value; this->handle->casted = false; this->handle->to_string = nullptr; } template void set(const char (&string)[N]) { this->set(std::string(string, N - 1)); } template command_entry&operator=(const T& value) { this->set(value); return *this; } explicit command_entry(std::shared_ptr handle) : handle(std::move(handle)) {} private: std::shared_ptr handle; }; namespace descriptor { namespace tliterals { template using tstring = std::integer_sequence; #ifndef WIN32 template constexpr tstring operator""_tstr() { return { }; } #endif template struct tliteral; template struct tliteral> { static constexpr char string[sizeof...(elements) + 1] = { elements..., '\0' }; }; } namespace impl { namespace templates { template struct _or_ { constexpr static bool value = false; }; template struct _or_ { constexpr static bool value = T || _or_::value; }; template struct index; template struct tuple_index; template struct index : std::integral_constant { }; template struct index : std::integral_constant::value> { }; template struct tuple_index> : std::integral_constant::value> { }; template struct remove_cr { typedef T type; }; template struct remove_cr { typedef T type; }; } struct base; template struct field; struct field_data; struct field_base; struct optional_extend; struct bulk_extend; template struct command_parser { constexpr static bool supported = false; }; inline void parse_field(const std::shared_ptr& description, field_base* field, command& cmd); struct option_data { bool bulked; bool optional; }; template struct options { using object_data_t = option_data; static constexpr auto is_bulked = bulked_t; static constexpr auto is_optional = optional_t; protected: inline static object_data_t options_object() { return { is_bulked, is_optional }; } }; using default_options = options; struct base { virtual ~base() = default; }; struct base_data { int type; /* 1 = field | 2 = switch | 3 = command handle */ option_data options; }; struct field_base : public base { virtual std::vector& ref_values() = 0; virtual const std::vector& ref_values() const = 0; }; struct field_data : public base_data { const char* key; const std::type_info& field_type; void* from_string; void* to_string; }; template struct field : public field_base, public options, public extends... { friend struct command_parser>; static_assert(converter::supported, "Target type isn't supported!"); static_assert(!converter::supported || converter::from_string, "Target type dosn't support parsing"); static_assert(impl::templates::_or_::value...>::value == false, "Extensions could not have data members"); protected: using object_t = field_data; using value_type = value_type_t; static constexpr auto key = key_t::string; static constexpr auto from_string = converter::from_string; public: template ::value...>::value, int> = 0 */> using as_optional = field, optional_extend, extends...>; template ::value...>::value, int> = 0 */> using as_bulked = field, bulk_extend, extends...>; using optional = as_optional; using bulked = as_bulked; inline static std::shared_ptr describe() { return std::make_shared( object_t { 1, options::options_object(), key, typeid(value_type_t), (void*) converter::from_string, (void*) converter::to_string } ); } inline value_type_t value() const { command_entry& value = this->get_command_entry(); return value.as(); } inline command_entry& get_command_entry() const { if(this->values.empty()) throw command_value_missing_exception{0, key}; const auto& front = this->values.front(); return *(command_entry*) &front; } template inline T as() const { command_entry& value = this->get_command_entry(); return value.as(); } template inline operator T() const { return this->as(); } std::vector& ref_values() final { return this->values; } const std::vector& ref_values() const final { return this->values; } protected: std::vector values; }; struct optional_extend { public: inline bool has_value() const { auto base = dynamic_cast((struct base*) this); assert(base); const auto& values = base->ref_values(); return !values.empty() && !values[0].is_empty(); } template inline T get_or(T&& value = T{}) const { auto base = dynamic_cast((struct base*) this); assert(base); auto& values = base->ref_values(); if(values.empty() || values[0].is_empty()) return value; return values.front().as(); } }; struct bulk_extend { public: inline bool has_index(size_t index) const { return !this->at(index).is_empty(); } inline size_t length() const { auto base = dynamic_cast((struct base*) this); assert(base); return base->ref_values().size(); } inline command_entry at(size_t index) const { auto base = dynamic_cast((struct base*) this); assert(base); auto& values = base->ref_values(); if(index > values.size()) throw command_bulk_exceed_index_exception(); return values[index]; } inline command_entry operator[](size_t index) const { return this->at(index); } }; struct trigger_base : public base { virtual bool& ref_flag() = 0; }; struct trigger_data : public base_data { const char* key; }; template struct trigger : public trigger_base, public options { protected: static constexpr auto key = key_t::string; public: using object_t = trigger_data; inline static std::shared_ptr describe() { return std::make_shared( object_t { 2, options::options_object(), key } ); } inline bool is_set() const { return this->flag_set; } operator bool() const { return this->flag_set; } bool& ref_flag() override { return this->flag_set; } private: bool flag_set = false; }; template struct command_parser> { constexpr static bool supported = true; typedef field field_t; using descriptor_t = std::shared_ptr; inline static descriptor_t describe() { return field_t::describe(); } inline static field_t apply(descriptor_t& descriptor, command& cmd) { assert(descriptor->type == 1); field_t result{}; //if(!description->options.optional && !cmd.has_value(description->key)) // throw command_value_missing_exception(); auto& values = result.ref_values(); values.clear(); if(descriptor->options.bulked) { values.resize(cmd.bulk_count()); for(size_t bulk_index = 0; bulk_index < cmd.bulk_count(); bulk_index++) { if(!cmd[bulk_index].has(descriptor->key)) { if(!descriptor->options.optional) throw command_value_missing_exception(bulk_index, descriptor->key); else values[bulk_index] = command_entry::empty; } else { values[bulk_index] = cmd[bulk_index][descriptor->key]; } } } else { if(!cmd.has_value(descriptor->key)) { if(!descriptor->options.optional) throw command_value_missing_exception(0, descriptor->key); else values.push_back(command_entry::empty); } else values.push_back(cmd[descriptor->key]); } return result; } }; template struct command_parser> { constexpr static bool supported = true; typedef trigger trigger_t; using descriptor_t = std::shared_ptr; inline static descriptor_t describe() { return trigger_t::describe(); } inline static trigger_t apply(descriptor_t& descriptor, command& cmd) { assert(descriptor->type == 2); trigger_t result{}; result.ref_flag() = cmd.has_trigger(descriptor->key); return result; } }; struct command_data : public base_data { }; template <> struct command_parser { constexpr static bool supported = true; using descriptor_t = std::shared_ptr; inline static descriptor_t describe() { return std::make_shared(command_data{{3, {false, false}}}); } inline static command& apply(descriptor_t& descriptor, command& cmd) { assert(descriptor->type == 3); return cmd; } }; template struct command_parser { constexpr static bool supported = false; using descriptor_t = std::shared_ptr; inline static descriptor_t describe() { return nullptr; } inline static command& apply(descriptor_t& descriptor, command& cmd) { return cmd; } }; } template using field = impl::field; template using trigger = impl::trigger; template inline std::array, sizeof...(args_t)> describe_function(void(*)(args_t...)) { static_assert(!impl::templates::_or_::type>::supported...>::value, "Not any function argument type is supported"); return {impl::command_parser::type>::describe()...}; } struct invocable_function { void operator()(command& command) { this->invoke(command); } virtual void invoke(command& command) = 0; }; template struct typed_invocable_function : public invocable_function { using args_tuple_t = std::tuple; template using command_parser_t = impl::command_parser::type>; using descriptors_t = std::tuple::descriptor_t...>; descriptors_t descriptors; void(*function)(args_t...); void invoke(command& command) override { this->function(command_parser_t::apply(std::get::value>(descriptors), command)...); } }; template > std::shared_ptr parse_function(void(*function)(args_t...)) { auto result = std::make_shared(); result->function = function; result->descriptors = {impl::command_parser::type>::describe()...}; return result; } template void invoke_function(void(*function)(args_t...), command& command) { auto descriptor = parse_function(function); (*descriptor)(command); } /* converts a literal into a template literal */ #define _tlit(literal) ::ts::descriptor::tliterals::tliteral #define tl(lit) _tlit(lit) } //using desc = descriptor::base; } #include "command_internal.h"