forked from Alepha/Alepha
UA
This commit is contained in:
@ -44,6 +44,7 @@ add_subdirectory( assertion.test )
|
|||||||
add_subdirectory( Constness.test )
|
add_subdirectory( Constness.test )
|
||||||
add_subdirectory( Capabilities.test )
|
add_subdirectory( Capabilities.test )
|
||||||
add_subdirectory( delimited_list.test )
|
add_subdirectory( delimited_list.test )
|
||||||
|
add_subdirectory( UniversalAggregate.test )
|
||||||
|
|
||||||
# Sample applications
|
# Sample applications
|
||||||
add_executable( example example.cc )
|
add_executable( example example.cc )
|
||||||
|
282
UA_Key.h
Normal file
282
UA_Key.h
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <variant>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @file
|
||||||
|
* @todo validate levels for externals sources like vector constructor, operator /
|
||||||
|
* @todo better exceptions
|
||||||
|
* @todo switch both isValid to use regex if appropriate
|
||||||
|
* @todo factory method for Level from a string, then possibly use it in UA_Key constructor
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen ::detail:: UA_Key_m
|
||||||
|
{
|
||||||
|
inline namespace exports
|
||||||
|
{
|
||||||
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
|
/*! @brief Used as a compound key to address an element in a (possibly) multi level Universal Aggregate)
|
||||||
|
* @detail Format is that it must start with a name (string) and then is followed by a series of names or vector indices.
|
||||||
|
* Names are separated with a period if consecutive and indices are contained in brackets.
|
||||||
|
* Names are alphanumeric starting with an alpha. Indices are non negative ints.
|
||||||
|
* A Name is used a key for a map in the UniversalAggregate
|
||||||
|
* Correct examples: key
|
||||||
|
* key[1]
|
||||||
|
* key.key2
|
||||||
|
* key[3][4]key2[2]
|
||||||
|
* key.key.key
|
||||||
|
* Bad examples: 3key starts with a period
|
||||||
|
* key.[2] period not supported between key and bracket
|
||||||
|
* key[2].key period not supported between key and bracket
|
||||||
|
*/
|
||||||
|
class UA_Key
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/*! @brief Determines if a stringview represents a valid name according to our definition
|
||||||
|
* @details Currently required to start with an alpha and contain only alphanumerics.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
isValidAsName( const std::string_view sv ) const
|
||||||
|
{
|
||||||
|
return true
|
||||||
|
and ( not sv.empty() )
|
||||||
|
and ( std::isalpha( sv.at( 0 ) ) )
|
||||||
|
and ( std::find_if_not( sv.cbegin() + 1, sv.cend(), []( const unsigned char c ){ return std::isalnum( c ); } ) == sv.end() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief Determines if a stringview represents a valid index (i.e., valid size_t representation, no check is done against an actual vector)
|
||||||
|
* @details Currently required to represent a non negative integer.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
isValidAsIndex( const std::string_view sv ) const
|
||||||
|
{
|
||||||
|
return true
|
||||||
|
and ( not sv.empty() )
|
||||||
|
and ( std::find_if_not( sv.cbegin(), sv.cend(), []( const unsigned char c ){ return std::isdigit( c ); } ) == sv.end() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @brief Type for a UA key for a map */
|
||||||
|
using NameType= std::string;
|
||||||
|
/*! @brief Type for a UA index for a vector */
|
||||||
|
using IndexType= size_t;
|
||||||
|
|
||||||
|
/*! @brief A representation of either a name for a UA map lookup or an index for a vector */
|
||||||
|
class Level: public std::variant< size_t, std::string >
|
||||||
|
{
|
||||||
|
friend std::ostream &
|
||||||
|
operator << ( std::ostream &os, const Level &lv )
|
||||||
|
{
|
||||||
|
std::visit( [&]( const auto &val ) { os << val; }, lv );
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! @brief Stores the parsed levels */
|
||||||
|
std::vector< Level > levels;
|
||||||
|
|
||||||
|
/*! @brief The default empty constructor is fine */
|
||||||
|
UA_Key()= default;
|
||||||
|
|
||||||
|
/*!@brief construct from a vector of Level */
|
||||||
|
explicit
|
||||||
|
UA_Key( const std::vector< Level > &lev )
|
||||||
|
: levels( lev )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief construct from another UA_Key with option to skip some of the levels at the front source UA_Key */
|
||||||
|
explicit
|
||||||
|
UA_Key( const UA_Key &ua, const IndexType skip )
|
||||||
|
: levels( ua.levels.cbegin() + std::min( skip, ua.levels.size() ), ua.levels.cend() )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!@brief construct from a stringview
|
||||||
|
* @details see class documentation for format explanation\n
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
UA_Key( const std::string_view sv )
|
||||||
|
{
|
||||||
|
size_t levelStart= 0; // index to the first char of the level being parsed
|
||||||
|
bool inIndex= false; // set to true when parsing of this level is after an open bracket
|
||||||
|
bool postIndex= false; // set to true when parsing of this level is after a closed bracket - next char must be a period or open bracket
|
||||||
|
while( levelStart < sv.size() )
|
||||||
|
{
|
||||||
|
char term; // which terminating char is encountered
|
||||||
|
auto pos= sv.find_first_of( "[].", levelStart );
|
||||||
|
if( pos == std::string::npos )
|
||||||
|
{
|
||||||
|
if( inIndex )
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "Unable to parse UA_Key(1) from " + std::string( sv ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
term= '.';
|
||||||
|
pos= sv.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
term= sv.at( pos );
|
||||||
|
}
|
||||||
|
const std::string_view candidate= sv.substr( levelStart, pos - levelStart );
|
||||||
|
levelStart= pos + 1;
|
||||||
|
if( postIndex )
|
||||||
|
{
|
||||||
|
if( candidate.size() != 0 or term == ']' )
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "Unable to parse UA_Key(2) from " + std::string( sv ) );
|
||||||
|
}
|
||||||
|
postIndex= false;
|
||||||
|
inIndex= ( term == '[' );
|
||||||
|
}
|
||||||
|
else if( ( term == '.' or term == '[' ) and not inIndex and isValidAsName( candidate ) )
|
||||||
|
{
|
||||||
|
|
||||||
|
levels.push_back( Level( std::string( candidate ) ) );
|
||||||
|
inIndex= ( term == '[' );
|
||||||
|
}
|
||||||
|
else if( term == ']' and inIndex and isValidAsIndex( candidate ) )
|
||||||
|
{
|
||||||
|
levels.push_back( Level( ::boost::lexical_cast< size_t >( candidate ) ) );
|
||||||
|
inIndex= false;
|
||||||
|
postIndex= true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "Unable to parse UA_Key(3) from " + std::string( sv ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( inIndex )
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "Unable to parse UA_Key(4) from " + std::string( sv ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief default == and != are fine. Other comparisons are not useful for now.
|
||||||
|
* @details Perhaps an imposed ordering could be used if sorting were to be needed.
|
||||||
|
*/
|
||||||
|
bool operator == ( const UA_Key & ) const= default;
|
||||||
|
|
||||||
|
/*! @brief returns true if the lowest level is a vector index.
|
||||||
|
* @note, false will be returned if there are no levels
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
isVector() const
|
||||||
|
{
|
||||||
|
return true
|
||||||
|
and ( not levels.empty() )
|
||||||
|
and ( std::holds_alternative< size_t >( levels.back() ) )
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief returns a UA_Key formed by removing the last level from a copy of this UA_Key
|
||||||
|
* @note, current behavior mimics that of vector.pop_back() in that if the this UA_Key has no levels, the behavior is undefined
|
||||||
|
*/
|
||||||
|
UA_Key
|
||||||
|
parent() const
|
||||||
|
{
|
||||||
|
UA_Key ret= *this;
|
||||||
|
ret.levels.pop_back();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief returns a reference to the last level
|
||||||
|
* @note, current behavior mimics that of vector.back() in that if the this UA_Key has no levels, the behavior is undefined
|
||||||
|
*/
|
||||||
|
Level &
|
||||||
|
back()
|
||||||
|
{
|
||||||
|
if( levels.empty() )
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "back attempted on UA_Key with 0 Levels " );
|
||||||
|
}
|
||||||
|
return levels.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief returns a const reference to the last level
|
||||||
|
* @note, current behavior mimics that of vector.back() in that if the this UA_Key has no levels, the behavior is undefined
|
||||||
|
*/
|
||||||
|
const Level &
|
||||||
|
back() const
|
||||||
|
{
|
||||||
|
return levels.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief appends a level to a copy of this UA_Key
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UA_Key
|
||||||
|
operator / ( const Level &lv )
|
||||||
|
{
|
||||||
|
UA_Key ret= *this;
|
||||||
|
ret.levels.push_back( lv );
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief outputs a character representation of the levels
|
||||||
|
* @note, as long as the current restriction against whitespace on a string parses into a UA_Key remains, the operator << will produce an output identical to the input
|
||||||
|
*/
|
||||||
|
friend std::ostream &
|
||||||
|
operator << ( std::ostream &os, const UA_Key &uak )
|
||||||
|
{
|
||||||
|
bool first= true;
|
||||||
|
for( const auto lv: uak.levels )
|
||||||
|
{
|
||||||
|
std::visit
|
||||||
|
(
|
||||||
|
[&]< typename T >( const T &t )
|
||||||
|
{
|
||||||
|
if constexpr( std::is_same_v< std::string, T > )
|
||||||
|
{
|
||||||
|
if( not first )
|
||||||
|
{
|
||||||
|
os << '.';
|
||||||
|
}
|
||||||
|
os << std::get< std::string >( lv );
|
||||||
|
}
|
||||||
|
else if constexpr( std::is_same_v< size_t, T > )
|
||||||
|
{
|
||||||
|
os << '[' << std::get< size_t >( lv ) << ']';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lv
|
||||||
|
);
|
||||||
|
first= false;
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief istream operator. Reads a string
|
||||||
|
* @details result is the same as if the istream is read into a string and the string were used to construct a UA_Key and then copied ot uak uak.
|
||||||
|
*/
|
||||||
|
friend std::istream &
|
||||||
|
operator >> ( std::istream &is, UA_Key &uak )
|
||||||
|
{
|
||||||
|
std::string input_string;
|
||||||
|
is >> input_string;
|
||||||
|
uak= UA_Key( input_string );
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::inline exports::inline UA_Key_m
|
||||||
|
{
|
||||||
|
using namespace detail::UA_Key_m::exports;
|
||||||
|
}
|
||||||
|
|
207
UniversalAggregate.h
Normal file
207
UniversalAggregate.h
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
#include <Alepha/UA_Key.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <any>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @file
|
||||||
|
* @todo consider dropping string arg versions in favor of just using UA_Key versions (after making conversion constructor not explicit)
|
||||||
|
* @todo consider how operator >> and << should function. JSON? If so, using what?
|
||||||
|
* @todo consider [] versions of at
|
||||||
|
* @todo decide exceptions to throw (especially for not found)
|
||||||
|
* @todo comment at()
|
||||||
|
* @todo auto creating option for get (and for general use)
|
||||||
|
* @todo need to constrain type stored in UniversalAggregate and coerce to those when possible
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen ::detail:: UniversalAggregate_m
|
||||||
|
{
|
||||||
|
inline namespace exports
|
||||||
|
{
|
||||||
|
class UniversalAggregate;
|
||||||
|
|
||||||
|
using MemberType= std::variant< UniversalAggregate, std::string, int, std::vector< UniversalAggregate > >;
|
||||||
|
|
||||||
|
class UniversalAggregate
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// The value could be a UA, a vector of UAs or anything else.
|
||||||
|
using CoreMap= std::map< std::string, MemberType >;
|
||||||
|
CoreMap data;
|
||||||
|
|
||||||
|
UniversalAggregate &
|
||||||
|
get( UA_Key key, bool create= false )
|
||||||
|
{
|
||||||
|
UniversalAggregate *ret= this;
|
||||||
|
for( const auto &lv : key.levels )
|
||||||
|
{
|
||||||
|
if( std::holds_alternative< std::string >(lv) )
|
||||||
|
{
|
||||||
|
if( not ret->data.count( std::get< std::string >( lv ) ) )
|
||||||
|
{
|
||||||
|
if( not create ) // throw an error
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "desired level not found: " + std::get< std::string >( lv ) );
|
||||||
|
}
|
||||||
|
else // create the missing level
|
||||||
|
{
|
||||||
|
ret->data[ std::get< std::string >( lv ) ]= UniversalAggregate{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret= &(std::get<UniversalAggregate>(ret->data.at( std::get< std::string >( lv ) ) ) );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UniversalAggregate &
|
||||||
|
get( UA_Key key ) const
|
||||||
|
{
|
||||||
|
const UniversalAggregate *ret= this;
|
||||||
|
for( const auto &lv : key.levels )
|
||||||
|
{
|
||||||
|
if( std::holds_alternative< std::string >(lv) )
|
||||||
|
{
|
||||||
|
if( not ret->data.count( std::get< std::string >( lv ) ) )
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "desired level not found: " + std::get< std::string >( lv ) );
|
||||||
|
}
|
||||||
|
ret= &(std::get<UniversalAggregate>(ret->data.at( std::get< std::string >( lv ) ) ) );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
UniversalAggregate()= default;
|
||||||
|
|
||||||
|
/*! @brief
|
||||||
|
* @note direct insert on a vector is not supported. Use at() to obtain a reference to the vector
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
insert( const UA_Key &key, const MemberType &value )
|
||||||
|
{
|
||||||
|
if( key.isVector() )
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "Operation not supported on vector" );
|
||||||
|
}
|
||||||
|
auto& target= get( key.parent(), true );
|
||||||
|
target.data[ std::get< std::string >( key.levels.back() ) ]= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
insert( const std::string &key, const MemberType &value )
|
||||||
|
{
|
||||||
|
insert( UA_Key( key ), value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief
|
||||||
|
* @note direct erase on a vector is not supported. Use at() to obtain a reference to the vector
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
erase( const UA_Key &key )
|
||||||
|
{
|
||||||
|
if( key.isVector() )
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "Operation not supported on vector" );
|
||||||
|
}
|
||||||
|
auto& target= get( key.parent() );
|
||||||
|
target.data.erase( std::get< std::string >( key.levels.back() ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
erase( const std::string &key )
|
||||||
|
{
|
||||||
|
erase( UA_Key( key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const MemberType &
|
||||||
|
at( const std::string &key ) const
|
||||||
|
{
|
||||||
|
return at( UA_Key( key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const MemberType &
|
||||||
|
at( const UA_Key &key ) const
|
||||||
|
{
|
||||||
|
if( key.isVector() )
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "Operation not supported on vector" );
|
||||||
|
}
|
||||||
|
auto& target= get( key.parent() );
|
||||||
|
return target.data.at( std::get< std::string >( key.levels.back() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
MemberType &
|
||||||
|
at( const std::string &key )
|
||||||
|
{
|
||||||
|
return at( UA_Key( key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
MemberType &
|
||||||
|
at( const UA_Key &key )
|
||||||
|
{
|
||||||
|
if( key.isVector() )
|
||||||
|
{
|
||||||
|
throw std::runtime_error( "Operation not supported on vector" );
|
||||||
|
}
|
||||||
|
auto& target= get( key.parent() );
|
||||||
|
return target.data.at( std::get< std::string >( key.levels.back() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
count( const std::string &key )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief
|
||||||
|
* @note direct count on a vector is not supported. Use at() to obtain a reference to the vector
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
count( const UA_Key &key )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::inline exports::inline UniversalAggregate_m
|
||||||
|
{
|
||||||
|
using namespace detail::UniversalAggregate_m::exports;
|
||||||
|
}
|
||||||
|
|
205
UniversalAggregate.test/0.cc
Normal file
205
UniversalAggregate.test/0.cc
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <Alepha/UniversalAggregate.h>
|
||||||
|
#include <Alepha/Exception.h>
|
||||||
|
#include <Alepha/IOStreams/OStreamable.h>
|
||||||
|
|
||||||
|
#include <Alepha/Testing/test.h>
|
||||||
|
#include <Alepha/Testing/TableTest.h>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @file
|
||||||
|
* @todo test isVector
|
||||||
|
* @todo test operator/
|
||||||
|
* @todo test from vector constructor
|
||||||
|
* @todo test skipping constructor
|
||||||
|
* @todo test make skipping constructor default to 0 or add copy constructor
|
||||||
|
* @todo more UA tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace Alepha::Testing::exports;
|
||||||
|
|
||||||
|
Alepha::UniversalAggregate ua;
|
||||||
|
|
||||||
|
std::string
|
||||||
|
validUA_Key( std::string s )
|
||||||
|
{
|
||||||
|
Alepha::UA_Key uak( s );
|
||||||
|
return Alepha::IOStreams::String{} << uak;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool
|
||||||
|
doesUA_KeyThrow( const std::string &s )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Alepha::UA_Key uak( s );
|
||||||
|
}
|
||||||
|
catch( ... )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
parentOfUA_Key( std::string s )
|
||||||
|
{
|
||||||
|
Alepha::UA_Key uak( s );
|
||||||
|
return Alepha::IOStreams::String{} << uak.parent();
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string
|
||||||
|
backOfUA_Key( std::string s )
|
||||||
|
{
|
||||||
|
Alepha::UA_Key uak( s );
|
||||||
|
return Alepha::IOStreams::String{} << uak.back();
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string
|
||||||
|
istreamUA_Key( std::string s )
|
||||||
|
{
|
||||||
|
std::istringstream is(s);
|
||||||
|
Alepha::UA_Key uak;
|
||||||
|
is >> uak;
|
||||||
|
return Alepha::IOStreams::String{} << uak;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto UA_Key_tests= Alepha::Utility::enroll <=[]
|
||||||
|
{
|
||||||
|
"UA_Key.Valid"_test <=TableTest< validUA_Key >::Cases
|
||||||
|
{
|
||||||
|
{ "1k", { "tony" }, "tony" },
|
||||||
|
{ "2k", { "tony.bonnie" }, "tony.bonnie" },
|
||||||
|
{ "1k1i", { "tony[5]" }, "tony[5]" },
|
||||||
|
{ "1k2i1k1i", { "tony[5][6].bonnie[7]" }, "tony[5][6].bonnie[7]" },
|
||||||
|
};
|
||||||
|
|
||||||
|
"UA_Key.Exceptions"_test <=TableTest< doesUA_KeyThrow >::Cases
|
||||||
|
{
|
||||||
|
{ "good", { "tony" }, false },
|
||||||
|
{ "bad-brackets-1", { "tony[[" }, true },
|
||||||
|
{ "bad-brackets-2", { "tony[" }, true },
|
||||||
|
{ "bad-brackets-3", { "tony]" }, true },
|
||||||
|
{ "bad-leading-dot", { ".tony" }, true },
|
||||||
|
{ "bad-leading-open", { "[tony" }, true },
|
||||||
|
{ "bad-leading-close", { "]tony" }, true },
|
||||||
|
{ "bad-index-empty", { "tony[]" }, true },
|
||||||
|
{ "bad-index-alpha", { "tony[a]" }, true },
|
||||||
|
{ "bad-index-containsAlpha", { "tony[1a3]" }, true },
|
||||||
|
{ "bad-index-containsSpace", { "tony[1 3]" }, true },
|
||||||
|
{ "bad-name-empty", { "tony..Bonnie" }, true },
|
||||||
|
{ "bad-name-leadingNumber1", { "2tony.Bonnie" }, true },
|
||||||
|
{ "bad-name-leadingNumber2", { "tony.2Bonnie" }, true },
|
||||||
|
{ "bad-name-illegalChar", { "to%ny" }, true },
|
||||||
|
{ "bad-name-containsSpace", { "to ny" }, true },
|
||||||
|
};
|
||||||
|
|
||||||
|
"UA_Key.Parent"_test <=TableTest< parentOfUA_Key >::Cases
|
||||||
|
{
|
||||||
|
{ "2lev", { std::string("tony.bonnie") }, "tony" },
|
||||||
|
{ "2lev[]", { std::string("tony[5]") }, "tony" },
|
||||||
|
{ "3lev", { std::string("tony[3].bonnie") }, "tony[3]" },
|
||||||
|
{ "1lev", { std::string("tony") }, "" },
|
||||||
|
};
|
||||||
|
|
||||||
|
"UA_Key.istream"_test <=TableTest< istreamUA_Key >::Cases
|
||||||
|
{
|
||||||
|
{ "2lev", { std::string("tony.bonnie") }, "tony.bonnie" },
|
||||||
|
{ "2lev[]", { std::string("tony[5]") }, "tony[5]" },
|
||||||
|
{ "3lev", { std::string("tony[3].bonnie") }, "tony[3].bonnie" },
|
||||||
|
{ "1lev", { std::string("tony") }, "tony" },
|
||||||
|
};
|
||||||
|
|
||||||
|
"UA_Key.Back"_test <=TableTest< backOfUA_Key >::Cases
|
||||||
|
{
|
||||||
|
{ "2lev", { std::string("tony.bonnie") }, "bonnie" },
|
||||||
|
{ "2lev[]", { std::string("tony[5]") }, "5" },
|
||||||
|
{ "3lev", { std::string("tony[3].bonnie") }, "bonnie" },
|
||||||
|
{ "1lev", { std::string("tony") }, "tony" },
|
||||||
|
};
|
||||||
|
|
||||||
|
"UA_KeyDefaultConstructor"_test <=[] () -> bool
|
||||||
|
{
|
||||||
|
Alepha::UA_Key uak;
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << uak;
|
||||||
|
return ( oss.str() == "" );
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto UA_test= Alepha::Utility::enroll <=[]
|
||||||
|
{
|
||||||
|
"UA_Simple1"_test <=[] () -> bool
|
||||||
|
{
|
||||||
|
Alepha::UniversalAggregate ua;
|
||||||
|
ua.insert( "tony", std::string( "bonnie" ) );
|
||||||
|
return std::get<std::string>( ua.at( "tony" ) ) == "bonnie";
|
||||||
|
};
|
||||||
|
|
||||||
|
"UA_Simple1Const"_test <=[] () -> bool
|
||||||
|
{
|
||||||
|
Alepha::UniversalAggregate ua;
|
||||||
|
ua.insert( "tony", std::string( "bonnie" ) );
|
||||||
|
const Alepha::UniversalAggregate ub= ua;
|
||||||
|
return std::get<std::string>( ub.at( "tony" ) ) == "bonnie";
|
||||||
|
};
|
||||||
|
|
||||||
|
"UA_Simple2"_test <=[] () -> bool
|
||||||
|
{
|
||||||
|
Alepha::UniversalAggregate ua, ub;
|
||||||
|
ua.insert( "tony", std::string( "bonnie" ) );
|
||||||
|
ua.insert( "gina", ub );
|
||||||
|
ua.insert( "gina.bobby", std::string("sam") );
|
||||||
|
return std::get<std::string>( ua.at( "gina.bobby" ) ) == "sam";
|
||||||
|
};
|
||||||
|
|
||||||
|
"UA_GetCreate"_test <=[] () -> bool // creates missing level
|
||||||
|
{
|
||||||
|
Alepha::UniversalAggregate ua, ub;
|
||||||
|
ua.insert( "tony", std::string( "bonnie" ) );
|
||||||
|
ua.insert( "gina", ub );
|
||||||
|
ua.insert( "gina.bobby.bill", std::string("sam") );
|
||||||
|
return std::get<std::string>( ua.at( "gina.bobby.bill" ) ) == "sam";
|
||||||
|
};
|
||||||
|
|
||||||
|
"UA_Erase1"_test <=[] () -> bool
|
||||||
|
{
|
||||||
|
Alepha::UniversalAggregate ua, ub;
|
||||||
|
ua.insert( "tony", std::string( "bonnie" ) );
|
||||||
|
ua.insert( "gina", ub );
|
||||||
|
ua.erase( "tony" );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ua.at( "tony" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch( ... )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
"UA_Erase2"_test <=[] () -> bool
|
||||||
|
{
|
||||||
|
Alepha::UniversalAggregate ua, ub;
|
||||||
|
ua.insert( "tony", std::string( "bonnie" ) );
|
||||||
|
ua.insert( "gina", ub );
|
||||||
|
ua.erase( "gina" );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return std::get<std::string>( ua.at( "tony" ) ) == "bonnie";
|
||||||
|
}
|
||||||
|
catch( ... )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
1
UniversalAggregate.test/CMakeLists.txt
Normal file
1
UniversalAggregate.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
unit_test( 0 )
|
Reference in New Issue
Block a user