2019-06-26 22:11:22 +02:00
# pragma once
# include <any>
# include <memory>
# include <vector>
# include <map>
# include <cassert>
# include <string_view>
# include <deque>
# include <tuple>
# 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 < std : : string , std : : shared_ptr < command_value > > values ;
} ;
struct command_data {
std : : string command ;
2019-10-13 17:12:50 +02:00
bool editable ;
2019-06-26 22:11:22 +02:00
std : : deque < std : : shared_ptr < command_bulk > > bulks ;
std : : deque < std : : string > 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 < impl : : command_data > 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 < impl : : command_bulk > handle ) : bulk_index ( index ) , handle ( std : : move ( handle ) ) { }
size_t bulk_index ;
std : : shared_ptr < impl : : command_bulk > handle ;
} ;
class command_entry {
public :
static command_entry empty ;
command_entry ( ) : handle ( std : : make_shared < impl : : command_value > ( ) ) { }
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 < std : : string > ( 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 ( ) ;
}
2019-10-13 17:12:50 +02:00
template < typename T , std : : enable_if_t < ! std : : is_same < T , std : : string > : : value , int > = 0 >
2019-06-26 22:11:22 +02:00
T as ( ) {
static_assert ( converter < T > : : supported , " Target type isn't supported! " ) ;
static_assert ( ! converter < T > : : supported | | converter < T > : : 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 < T > ( this - > handle - > value ) ;
else
throw command_casted_exception ( ) ;
} else {
const auto & ref = std : : any_cast < const std : : string & > ( this - > handle - > value ) ;
this - > handle - > value = converter < T > : : from_string ( ref ) ;
this - > handle - > to_string = converter < T > : : to_string ;
this - > handle - > casted = true ;
}
return std : : any_cast < T > ( this - > handle - > value ) ;
}
template < typename T , std : : enable_if_t < std : : is_same < T , std : : string > : : value , int > = 0 >
T as ( ) {
return this - > string ( ) ;
}
template < typename T >
inline operator T ( ) {
return this - > as < T > ( ) ;
}
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 < typename T , typename std : : enable_if < ! std : : is_same < T , std : : string > : : value & & ! std : : is_same < T , const char * > : : value & & ! std : : is_same < T , const char ( & ) [ ] > : : value , int > : : type = 0 >
void set ( const T & value ) {
static_assert ( converter < T > : : supported , " Target type isn't supported! " ) ;
static_assert ( ! converter < T > : : supported | | converter < T > : : to_string , " Target type dosn't support encode " ) ;
this - > handle - > casted = true ;
this - > handle - > value = std : : move ( value ) ;
this - > handle - > to_string = converter < T > : : to_string ;
}
template < typename T , typename std : : enable_if < std : : is_same < T , std : : string > : : value , int > : : type = 0 >
void set ( const T & value ) {
this - > handle - > value = value ;
this - > handle - > casted = false ;
this - > handle - > to_string = nullptr ;
}
template < int N >
void set ( const char ( & string ) [ N ] ) {
this - > set ( std : : string ( string , N - 1 ) ) ;
}
template < typename T >
command_entry & operator = ( const T & value ) {
this - > set ( value ) ;
return * this ;
}
explicit command_entry ( std : : shared_ptr < impl : : command_value > handle ) : handle ( std : : move ( handle ) ) { }
private :
std : : shared_ptr < impl : : command_value > handle ;
} ;
namespace descriptor {
namespace tliterals {
template < char . . . chars >
using tstring = std : : integer_sequence < char , chars . . . > ;
# ifndef WIN32
template < typename T , T . . . chars >
constexpr tstring < chars . . . > operator " " _tstr ( ) { return { } ; }
# endif
template < typename >
struct tliteral ;
template < char . . . elements >
struct tliteral < tstring < elements . . . > > {
static constexpr char string [ sizeof . . . ( elements ) + 1 ] = { elements . . . , ' \0 ' } ;
} ;
}
namespace impl {
namespace templates {
template < bool . . . >
struct _or_ {
constexpr static bool value = false ;
} ;
template < bool T , bool . . . Args >
struct _or_ < T , Args . . . > {
constexpr static bool value = T | | _or_ < Args . . . > : : value ;
} ;
template < typename . . . >
struct index ;
template < typename . . . >
struct tuple_index ;
template < typename T , typename . . . R >
struct index < T , T , R . . . > : std : : integral_constant < size_t , 0 >
{ } ;
template < typename T , typename F , typename . . . R >
struct index < T , F , R . . . > : std : : integral_constant < size_t , 1 + index < T , R . . . > : : value >
{ } ;
template < typename T , typename . . . R >
struct tuple_index < T , std : : tuple < R . . . > > : std : : integral_constant < size_t , index < T , R . . . > : : value >
{ } ;
template < typename T >
struct remove_cr {
typedef T type ;
} ;
template < typename T >
struct remove_cr < const T & > {
typedef T type ;
} ;
}
struct base ;
template < class key_t , typename value_type_t , class options , class . . . extends >
struct field ;
struct field_data ;
struct field_base ;
struct optional_extend ;
struct bulk_extend ;
template < typename . . . >
struct command_parser {
constexpr static bool supported = false ;
} ;
inline void parse_field ( const std : : shared_ptr < field_data > & description , field_base * field , command & cmd ) ;
struct option_data {
bool bulked ;
bool optional ;
} ;
template < bool bulked_t , bool optional_t >
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 < false , false > ;
2019-10-13 17:12:50 +02:00
struct base {
virtual ~ base ( ) = default ;
} ;
2019-06-26 22:11:22 +02:00
struct base_data {
int type ; /* 1 = field | 2 = switch | 3 = command handle */
option_data options ;
} ;
2019-10-13 17:12:50 +02:00
struct field_base : public base {
virtual std : : vector < command_entry > & ref_values ( ) = 0 ;
virtual const std : : vector < command_entry > & ref_values ( ) const = 0 ;
} ;
2019-06-26 22:11:22 +02:00
struct field_data : public base_data {
const char * key ;
const std : : type_info & field_type ;
void * from_string ;
void * to_string ;
} ;
template < class key_t , typename value_type_t , class options , class . . . extends >
2019-10-13 17:12:50 +02:00
struct field : public field_base , public options , public extends . . . {
2019-06-26 22:11:22 +02:00
friend struct command_parser < field < key_t , value_type_t , options , extends . . . > > ;
static_assert ( converter < value_type_t > : : supported , " Target type isn't supported! " ) ;
static_assert ( ! converter < value_type_t > : : supported | | converter < value_type_t > : : from_string , " Target type dosn't support parsing " ) ;
2019-10-13 17:12:50 +02:00
static_assert ( impl : : templates : : _or_ < ! std : : is_empty < extends > : : value . . . > : : value = = false , " Extensions could not have data members " ) ;
2019-06-26 22:11:22 +02:00
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 < value_type_t > : : from_string ;
public :
template < bool flag = true /*, std::enable_if_t<!templates::_or_<std::is_same<extends, optional_extend>::value...>::value, int> = 0 */ >
using as_optional = field < key_t , value_type_t , impl : : options < options : : is_bulked , flag > , optional_extend , extends . . . > ;
template < bool flag = true /*, std::enable_if_t<!templates::_or_<std::is_same<extends, bulk_extend>::value...>::value, int> = 0 */ >
using as_bulked = field < key_t , value_type_t , impl : : options < flag , options : : is_optional > , bulk_extend , extends . . . > ;
using optional = as_optional < true > ;
using bulked = as_bulked < true > ;
inline static std : : shared_ptr < object_t > describe ( ) {
return std : : make_shared < object_t > (
object_t {
1 ,
options : : options_object ( ) ,
key ,
typeid ( value_type_t ) ,
( void * ) converter < value_type_t > : : from_string ,
( void * ) converter < value_type_t > : : to_string
}
) ;
}
inline value_type_t value ( ) const {
command_entry & value = this - > get_command_entry ( ) ;
return value . as < value_type_t > ( ) ;
}
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 < typename T >
inline T as ( ) const {
command_entry & value = this - > get_command_entry ( ) ;
return value . as < T > ( ) ;
}
template < typename T >
inline operator T ( ) const {
return this - > as < T > ( ) ;
}
2019-10-13 17:12:50 +02:00
std : : vector < command_entry > & ref_values ( ) final { return this - > values ; }
const std : : vector < command_entry > & ref_values ( ) const final { return this - > values ; }
2019-06-26 22:11:22 +02:00
protected :
std : : vector < command_entry > values ;
} ;
struct optional_extend {
public :
inline bool has_value ( ) const {
2019-10-13 17:12:50 +02:00
auto base = dynamic_cast < field_base * > ( ( struct base * ) this ) ;
assert ( base ) ;
const auto & values = base - > ref_values ( ) ;
2019-06-26 22:11:22 +02:00
return ! values . empty ( ) & & ! values [ 0 ] . is_empty ( ) ;
}
template < typename T >
inline T get_or ( T & & value = T { } ) const {
2019-10-13 17:12:50 +02:00
auto base = dynamic_cast < field_base * > ( ( struct base * ) this ) ;
assert ( base ) ;
auto & values = base - > ref_values ( ) ;
if ( values . empty ( ) | | values [ 0 ] . is_empty ( ) )
return value ;
return values . front ( ) . as < T > ( ) ;
2019-06-26 22:11:22 +02:00
}
} ;
struct bulk_extend {
public :
inline bool has_index ( size_t index ) const {
return ! this - > at ( index ) . is_empty ( ) ;
}
2019-10-13 17:12:50 +02:00
inline size_t length ( ) const {
auto base = dynamic_cast < field_base * > ( ( struct base * ) this ) ;
assert ( base ) ;
return base - > ref_values ( ) . size ( ) ;
}
2019-06-26 22:11:22 +02:00
inline command_entry at ( size_t index ) const {
2019-10-13 17:12:50 +02:00
auto base = dynamic_cast < field_base * > ( ( struct base * ) this ) ;
assert ( base ) ;
auto & values = base - > ref_values ( ) ;
2019-06-26 22:11:22 +02:00
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 ) ;
}
} ;
2019-10-13 17:12:50 +02:00
struct trigger_base : public base {
virtual bool & ref_flag ( ) = 0 ;
2019-06-26 22:11:22 +02:00
} ;
2019-10-13 17:12:50 +02:00
struct trigger_data : public base_data {
const char * key ;
2019-06-26 22:11:22 +02:00
} ;
template < class key_t , class options >
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 < object_t > describe ( ) {
return std : : make_shared < object_t > (
object_t {
2 ,
options : : options_object ( ) ,
key
}
) ;
}
inline bool is_set ( ) const { return this - > flag_set ; }
operator bool ( ) const { return this - > flag_set ; }
2019-10-13 17:12:50 +02:00
bool & ref_flag ( ) override {
2019-06-26 22:11:22 +02:00
return this - > flag_set ;
}
private :
2019-10-13 17:12:50 +02:00
bool flag_set = false ;
2019-06-26 22:11:22 +02:00
} ;
template < class key_t , class options , class . . . extends >
struct command_parser < field < key_t , options , extends . . . > > {
constexpr static bool supported = true ;
typedef field < key_t , options , extends . . . > field_t ;
using descriptor_t = std : : shared_ptr < typename field_t : : object_t > ;
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 { } ;
2019-10-13 17:12:50 +02:00
//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 ] ) ;
}
2019-06-26 22:11:22 +02:00
return result ;
}
} ;
template < typename key_t , class options >
struct command_parser < trigger < key_t , options > > {
constexpr static bool supported = true ;
typedef trigger < key_t , options > trigger_t ;
using descriptor_t = std : : shared_ptr < typename trigger_t : : object_t > ;
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 { } ;
2019-10-13 17:12:50 +02:00
result . ref_flag ( ) = cmd . has_trigger ( descriptor - > key ) ;
2019-06-26 22:11:22 +02:00
return result ;
}
} ;
2019-10-13 17:12:50 +02:00
struct command_data : public base_data { } ;
template < >
struct command_parser < command & > {
2019-06-26 22:11:22 +02:00
constexpr static bool supported = true ;
2019-10-13 17:12:50 +02:00
using descriptor_t = std : : shared_ptr < command_data > ;
2019-06-26 22:11:22 +02:00
2019-10-13 17:12:50 +02:00
inline static descriptor_t describe ( ) { return std : : make_shared < command_data > ( command_data { { 3 , { false , false } } } ) ; }
2019-06-26 22:11:22 +02:00
2019-10-13 17:12:50 +02:00
inline static command & apply ( descriptor_t & descriptor , command & cmd ) {
2019-06-26 22:11:22 +02:00
assert ( descriptor - > type = = 3 ) ;
2019-10-13 17:12:50 +02:00
return cmd ;
2019-06-26 22:11:22 +02:00
}
} ;
template < typename C >
struct command_parser < C > {
2019-10-13 17:12:50 +02:00
constexpr static bool supported = false ;
2019-06-26 22:11:22 +02:00
2019-10-13 17:12:50 +02:00
using descriptor_t = std : : shared_ptr < nullptr_t > ;
2019-06-26 22:11:22 +02:00
inline static descriptor_t describe ( ) {
return nullptr ;
}
inline static command & apply ( descriptor_t & descriptor , command & cmd ) {
return cmd ;
}
} ;
}
template < class key_t , typename value_type_t >
using field = impl : : field < key_t , value_type_t , impl : : default_options > ;
template < class key_t >
using trigger = impl : : trigger < key_t , impl : : default_options > ;
template < typename . . . args_t >
2019-10-13 17:12:50 +02:00
inline std : : array < std : : shared_ptr < struct impl : : base_data > , sizeof . . . ( args_t ) > describe_function ( void ( * ) ( args_t . . . ) ) {
static_assert ( ! impl : : templates : : _or_ < ! impl : : command_parser < typename impl : : templates : : remove_cr < args_t > : : type > : : supported . . . > : : value , " Not any function argument type is supported " ) ;
return { impl : : command_parser < typename impl : : templates : : remove_cr < args_t > : : type > : : describe ( ) . . . } ;
2019-06-26 22:11:22 +02:00
}
struct invocable_function {
2019-10-13 17:12:50 +02:00
void operator ( ) ( command & command ) { this - > invoke ( command ) ; }
2019-06-26 22:11:22 +02:00
virtual void invoke ( command & command ) = 0 ;
} ;
template < typename . . . args_t >
struct typed_invocable_function : public invocable_function {
using args_tuple_t = std : : tuple < args_t . . . > ;
template < typename arg_t >
using command_parser_t = impl : : command_parser < typename impl : : templates : : remove_cr < arg_t > : : type > ;
using descriptors_t = std : : tuple < typename command_parser_t < args_t > : : descriptor_t . . . > ;
descriptors_t descriptors ;
void ( * function ) ( args_t . . . ) ;
void invoke ( command & command ) override {
2019-10-13 17:12:50 +02:00
this - > function ( command_parser_t < args_t > : : apply ( std : : get < impl : : templates : : tuple_index < args_t , args_tuple_t > : : value > ( descriptors ) , command ) . . . ) ;
2019-06-26 22:11:22 +02:00
}
} ;
template < typename . . . args_t , typename typed_function = typed_invocable_function < args_t . . . > >
std : : shared_ptr < invocable_function > parse_function ( void ( * function ) ( args_t . . . ) ) {
auto result = std : : make_shared < typed_function > ( ) ;
result - > function = function ;
result - > descriptors = { impl : : command_parser < typename impl : : templates : : remove_cr < args_t > : : type > : : describe ( ) . . . } ;
return result ;
}
template < typename . . . args_t >
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<decltype(literal ##_tstr)>
# define tl(lit) _tlit(lit)
}
//using desc = descriptor::base<descriptor::impl::default_options>;
}
# include "command_internal.h"