4#include "Stroika/Foundation/StroikaPreComp.h"
6#include "Stroika/Foundation/Characters/FloatConversion.h"
8#include "Stroika/Foundation/Characters/String2Int.h"
10#include "Stroika/Foundation/Containers/Concrete/Mapping_HashTable.h"
12#include "Stroika/Foundation/DataExchange/BadFormatException.h"
14#include "Stroika/Foundation/Math/Common.h"
17#include "VariantValue.h"
20using namespace Characters::Literals;
23using Memory::MakeSharedPtr;
29static_assert (regular<VariantValue>);
33 using IntegerType_ =
long long int;
34 using UnsignedIntegerType_ =
unsigned long long int;
35 using FloatType_ =
long double;
45 struct TN_<Memory::
BLOB> {
49 struct TN_<IntegerType_> {
53 struct TN_<UnsignedIntegerType_> {
57 struct TN_<FloatType_> {
65 struct TN_<DateTime> {
84 template <
typename FWD>
85 inline TIRep_ (
FWD&& v)
89 virtual Type GetType ()
const override
91 return TN_<T>::kTYPEENUM;
101const shared_ptr<VariantValue::IRep_> VariantValue::kFalseRep_ = MakeSharedPtr<TIRep_<bool>> (
false);
102const shared_ptr<VariantValue::IRep_> VariantValue::kTrueRep_ = MakeSharedPtr<TIRep_<bool>> (
true);
217#if qStroika_HasComponent_boost
219 inline auto mk_ (
const boost::json::value& val) ->
VariantValue
221 using namespace boost;
222 switch (val.kind ()) {
223 case json::kind::null:
226 case json::kind::bool_:
227 return val.as_bool ();
229 case json::kind::double_:
230 return val.as_double ();
232 case json::kind::int64:
233 return val.as_int64 ();
235 case json::kind::uint64:
236 return val.as_uint64 ();
238 case json::kind::string: {
239 const json::string& bs = val.as_string ();
240 return String::FromUTF8 (span{bs});
242 case json::kind::array: {
243 const auto& a = val.as_array ();
244 std::vector<VariantValue> r;
245 r.reserve (a.size ());
246 for (
const boost::json::value& i : a) {
247 r.emplace_back (mk_ (i));
251 case json::kind::object: {
252 const auto& o = val.as_object ();
254 for (
const auto& i : o) {
255 r.
insert ({String::FromUTF8 (span{i.key ()}), mk_ (i.value ())});
273 if (fVal_ ==
nullptr) {
276 switch (fVal_->GetType ()) {
279 case Type::eUnsignedInteger: {
283 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
284 return v->fVal.
empty ();
287 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
289 return isnan (v->fVal);
294 case Type::eDateTime: {
297 case Type::eString: {
298 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
300 return v->fVal.empty ();
303 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
305 return v->fVal.empty ();
308 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
310 return v->fVal.empty ();
323bool VariantValue::AsBool_ ()
const
325 if (fVal_ ==
nullptr) {
328 switch (fVal_->GetType ()) {
329 case Type::eBoolean: {
330 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
334 case Type::eString: {
338 case Type::eInteger: {
341 case Type::eUnsignedInteger: {
345#if USE_NOISY_TRACE_IN_THIS_MODULE_
346 DbgTrace (
"failed coerce-to-bool: type={}, value={}"_f, fVal_->GetType (), *
this);
384 case Type::eUnsignedInteger:
390 case Type::eDateTime:
406 switch (GetType ()) {
415 case Type::eUnsignedInteger:
421 if (std::isnan (f)
or std::isinf (f)) {
428 case Type::eDateTime:
448 if (fVal_ ==
nullptr) {
451 switch (fVal_->GetType ()) {
453 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
458 return Cryptography::Encoding::Algorithm::Base64::Decode (
As<String> ());
463VariantValue::IntegerType_ VariantValue::AsInteger_ ()
const
465 if (fVal_ ==
nullptr) {
468 switch (fVal_->GetType ()) {
469 case Type::eBoolean: {
470 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
475 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
477 return Math::Round<VariantValue::IntegerType_> (v->fVal);
479 case Type::eInteger: {
480 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
484 case Type::eUnsignedInteger: {
485 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
489 case Type::eString: {
490 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
492 return Characters::String2Int<IntegerType_> (v->fVal);
495#if USE_NOISY_TRACE_IN_THIS_MODULE_
496 DbgTrace (
"failed coerce-to-int: type={}, value={}"_f, fVal_->GetType (), *
this);
503VariantValue::UnsignedIntegerType_ VariantValue::AsUnsignedInteger_ ()
const
505 if (fVal_ ==
nullptr) {
508 switch (fVal_->GetType ()) {
510 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
512 return Math::Round<VariantValue::UnsignedIntegerType_> (v->fVal);
514 case Type::eInteger: {
515 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
519 case Type::eUnsignedInteger: {
520 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
524 case Type::eString: {
525 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
529 return Characters::String2Int<UnsignedIntegerType_> (v->fVal);
532#if USE_NOISY_TRACE_IN_THIS_MODULE_
533 DbgTrace (
"failed coerce-to-uint: type={}, value={}"_f, fVal_->GetType (), *
this);
540VariantValue::FloatType_ VariantValue::AsFloatType_ ()
const
542 if (fVal_ ==
nullptr) {
543 return Math::nan<FloatType_> ();
545 switch (fVal_->GetType ()) {
546 case Type::eInteger: {
547 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
549 return static_cast<FloatType_
> (v->fVal);
551 case Type::eUnsignedInteger: {
552 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
554 return static_cast<FloatType_
> (v->fVal);
557 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
561 case Type::eString: {
562 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
565 return Characters::FloatConversion::ToFloat<FloatType_> (v->fVal);
568#if USE_NOISY_TRACE_IN_THIS_MODULE_
569 DbgTrace (
"failed coerce-to-float: type={}, value={}"_f, fVal_->GetType (), *
this);
576Date VariantValue::AsDate_ ()
const
578 if (fVal_ ==
nullptr) {
581 switch (fVal_->GetType ()) {
583 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
587 case Type::eDateTime: {
588 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
590 return v->fVal.GetDate ();
592 case Type::eString: {
593 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
598#if USE_NOISY_TRACE_IN_THIS_MODULE_
599 DbgTrace (
"failed coerce-to-date: type={}, value={}"_f, fVal_->GetType (), *
this);
606DateTime VariantValue::AsDateTime_ ()
const
608 if (fVal_ ==
nullptr) {
611 switch (fVal_->GetType ()) {
613 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
615 return DateTime{v->fVal};
617 case Type::eDateTime: {
618 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
622 case Type::eString: {
623 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
625 return DateTime::Parse (v->fVal, DateTime::kISO8601Format);
628#if USE_NOISY_TRACE_IN_THIS_MODULE_
629 DbgTrace (
"failed coerce-to-datetime: type={}, value={}"_f, fVal_->GetType (), *
this);
636#if qStroika_HasComponent_boost
639 using namespace boost;
640 if (fVal_ ==
nullptr) {
641 return json::value{
nullptr};
643 switch (fVal_->GetType ()) {
645 return json::value{
nullptr};
647 case Type::eBoolean: {
650 case Type::eString: {
652 return As<String> ().AsUTF8<
string> ().c_str ();
654 case Type::eInteger: {
657 case Type::eUnsignedInteger: {
665 case Type::eDateTime: {
683 for (
const auto& i :
srcMap) {
690 return json::value{
nullptr};
695String VariantValue::AsString_ ()
const
697 if (fVal_ ==
nullptr) [[
unlikely]] {
700 switch (fVal_->GetType ()) {
705 case Type::eBoolean: {
706 auto v = Debug::UncheckedDynamicCast<const TIRep_<bool>*> (fVal_.get ());
708 return v->fVal ?
"true"sv :
"false"sv;
711 auto v = Debug::UncheckedDynamicCast<const TIRep_<Memory::BLOB>*> (fVal_.get ());
714 using namespace Cryptography::Encoding::Algorithm;
715 return String{Base64::Encode (v->fVal, Base64::Options{.fLineBreak = Base64::LineBreak::eNone_LB})};
717 case Type::eInteger: {
718 auto v = Debug::UncheckedDynamicCast<const TIRep_<IntegerType_>*> (fVal_.get ());
720 return "{}"_f(v->fVal);
722 case Type::eUnsignedInteger: {
724 auto v = Debug::UncheckedDynamicCast<const TIRep_<UnsignedIntegerType_>*> (fVal_.get ());
726 return "{}"_f(v->fVal);
729 auto v = Debug::UncheckedDynamicCast<const TIRep_<FloatType_>*> (fVal_.get ());
731 using namespace Characters;
732 return FloatConversion::ToString (v->fVal, FloatConversion::SignificantFigures::kFullPrecision);
735 auto v = Debug::UncheckedDynamicCast<const TIRep_<Date>*> (fVal_.get ());
739 case Type::eDateTime: {
740 auto v = Debug::UncheckedDynamicCast<const TIRep_<DateTime>*> (fVal_.get ());
742 return v->fVal.Format (DateTime::kISO8601Format);
744 case Type::eString: {
745 auto v = Debug::UncheckedDynamicCast<const TIRep_<String>*> (fVal_.get ());
750 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
754 for (
auto i = v->fVal.begin (); i != v->fVal.end (); ++i) {
755 if (i != v->fVal.begin ()) {
764 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
768 for (
auto i = v->fVal.begin (); i != v->fVal.end (); ++i) {
769 if (i != v->fVal.begin ()) {
786 if (fVal_ ==
nullptr) [[
unlikely]] {
789 switch (fVal_->GetType ()) {
791 auto v = Debug::UncheckedDynamicCast<const TIRep_<Mapping<String, VariantValue>>*> (fVal_.get ());
803 if (fVal_ ==
nullptr) [[
unlikely]] {
806 switch (fVal_->GetType ()) {
808 auto v = Debug::UncheckedDynamicCast<const TIRep_<Sequence<VariantValue>>*> (fVal_.get ());
823bool Stroika::Foundation::DataExchange::VariantValue::EqualsComparer::operator() (
const VariantValue& lhs,
const VariantValue& rhs)
const
833 case VariantValue::eNull:
835 case VariantValue::eBoolean:
836 return ln.
As<
bool> () == rn.
As<
bool> ();
837 case VariantValue::eFloat:
838 return Math::NearlyEquals (ln.
As<FloatType_> (), rn.
As<FloatType_> ());
839 case VariantValue::eString:
841 case VariantValue::eArray: {
845 case VariantValue::eMap: {
849 case VariantValue::eBLOB:
850 case VariantValue::eInteger:
851 case VariantValue::eUnsignedInteger:
852 case VariantValue::eDate:
853 case VariantValue::eDateTime:
876 case VariantValue::eNull:
877 return strong_ordering::equal;
878 case VariantValue::eBoolean:
879 return ln.As<
bool> () <=>
rn.As<
bool> ();
880 case VariantValue::eFloat: {
882 FloatType_
l =
ln.As<FloatType_> ();
883 FloatType_
r =
rn.As<FloatType_> ();
884 if (Math::NearlyEquals (
l,
r)) {
885 return strong_ordering::equal;
888 return strong_ordering::less;
891 return strong_ordering::greater;
894 case VariantValue::eString:
896 case VariantValue::eArray: {
900 case VariantValue::eMap: {
917 case VariantValue::eBLOB:
918 case VariantValue::eInteger:
919 case VariantValue::eUnsignedInteger:
920 case VariantValue::eDate:
921 case VariantValue::eDateTime:
925 return strong_ordering::equal;
#define AssertNotReached()
auto MakeSharedPtr(ARGS_TYPE &&... args) -> shared_ptr< T >
same as make_shared, but if type T has block allocation, then use block allocation for the 'shared pa...
conditional_t< qStroika_Foundation_Memory_PreferBlockAllocation and andTrueCheck, BlockAllocationUseHelper< T >, Common::Empty > UseBlockAllocationIfAppropriate
Use this to enable block allocation for a particular class. Beware of subclassing.
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
nonvirtual RESULT_T As() const
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
Mapping_HashTable<KEY_TYPE, MAPPED_VALUE_TYPE, TRAITS> is a HashTable based concrete implementation o...
Sequence_stdvector<T> is an std::vector-based concrete implementation of the Sequence<T> container pa...
implement hash table support in a lightweight standard template library style. Use traits to describe...
nonvirtual void insert(const pair< KEY_TYPE, MAPPED_TYPE > &p)
somewhat stdlib-like names - that will do what is expected of someone from stdc++,...
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
nonvirtual CONTAINER_OF_Key_T As() const
nonvirtual void insert(ArgByValueType< value_type > kvp)
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void As(CONTAINER_OF_ADDABLE *into) const
write this Sequence into an existing container
nonvirtual void push_back(ArgByValueType< value_type > item)
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
nonvirtual VariantValue ConvertTo(Type to) const
Return this VariantValue converted to the given type (as if by As<T> for the T appropriate to 'Type t...
VariantValue()=default
construct a VariantValue from most any 'basic type' you would expect to find in a weakly typed langua...
nonvirtual bool empty() const
nonvirtual String ToString() const
Type
Enumeration of variant types.
nonvirtual bool IsConvertibleTo(Type to) const
nonvirtual RETURNTYPE As() const
nonvirtual VariantValue Normalize() const
static constexpr string_view kISO8601Format
Y-M-D format - locale independent, and ISO-8601 date format standard.
static Date Parse(const String &rep, const locale &l=locale{})
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
STRING_TYPE ToString(FLOAT_TYPE f, const ToStringOptions &options={})
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...