aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/yson/node/node.h
blob: 68c50deadd56d9d3d853045dcce364c59f9b7869 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
#pragma once

#include <util/generic/bt_exception.h>
#include <util/generic/cast.h>
#include <util/generic/hash.h>
#include <util/generic/vector.h>
#include <util/generic/yexception.h>
#include <util/generic/ylimits.h>
#include <util/string/cast.h>

#include <cmath>
#include <variant>

class IInputStream;
class IOutputStream;

namespace NYT {

////////////////////////////////////////////////////////////////////////////////

class TNode
{
public:
    class TLookupError
        : public TWithBackTrace<yexception>
    { };

    class TTypeError
        : public TWithBackTrace<yexception>
    { };

    enum EType {
        Undefined = 0   /*"undefined"*/,

        // NOTE: string representation of all node types
        // are compatible with server node type (except `Undefined' which is missing on server).
        String = 1  /*"string_node"*/,
        Int64 = 2   /*"int64_node"*/,
        Uint64 = 3  /*"uint64_node"*/,
        Double = 4  /*"double_node"*/,
        Bool = 5    /*"boolean_node"*/,
        List = 6    /*"list_node"*/,
        Map = 7     /*"map_node"*/,
        Null = 8    /*"null"*/,
    };

    using TListType = TVector<TNode>;
    using TMapType = THashMap<TString, TNode>;

private:
    struct TNull {
        bool operator==(const TNull&) const;
    };

    struct TUndefined {
        bool operator==(const TUndefined&) const;
    };

    using TValue = std::variant<
        bool,
        i64,
        ui64,
        double,
        TString,
        TListType,
        TMapType,
        TNull,
        TUndefined
        >;

public:

    TNode();
    TNode(const char* s);
    TNode(TStringBuf s);
    explicit TNode(std::string_view s);
    explicit TNode(const std::string& s);
    TNode(TString s);
    TNode(int i);

    //this case made speccially for prevent mess cast of EType into TNode through TNode(int) constructor
    //usual case of error SomeNode == TNode::Undefined <-- SomeNode indeed will be compared with TNode(0) without this method
    //correct way is SomeNode.GetType() == TNode::Undefined
    template<class T = EType>
    Y_FORCE_INLINE TNode(EType)
    {
        static_assert(!std::is_same<T, EType>::value, "looks like a mistake, may be you forget .GetType()");
    }

    //this case made speccially for prevent mess cast of T* into TNode through implicit bool ctr
    template<class T = int>
    Y_FORCE_INLINE TNode(const T*) : TNode() {
        static_assert(!std::is_same<T,T>::value, "looks like a mistake, and pointer have converted to bool");
    }

    TNode(unsigned int ui);
    TNode(long i);
    TNode(unsigned long ui);
    TNode(long long i);
    TNode(unsigned long long ui);
    TNode(double d);
    TNode(bool b);
    TNode(TMapType map);

    TNode(const TNode& rhs);
    TNode& operator=(const TNode& rhs);

    TNode(TNode&& rhs) noexcept;
    TNode& operator=(TNode&& rhs) noexcept;

    ~TNode();

    void Clear();

    bool IsString() const;
    bool IsInt64() const;
    bool IsUint64() const;
    bool IsDouble() const;
    bool IsBool() const;
    bool IsList() const;
    bool IsMap() const;

    // `IsEntity' is deprecated use `IsNull' instead.
    bool IsEntity() const;
    bool IsNull() const;
    bool IsUndefined() const;
    // Returns true if TNode is neither Null, nor Undefined
    bool HasValue() const;

    template<typename T>
    bool IsOfType() const noexcept;

    // Int64, Uint64, Double, or Bool
    bool IsArithmetic() const;

    bool Empty() const;
    size_t Size() const;

    EType GetType() const;

    const TString& AsString() const;
    i64 AsInt64() const;
    ui64 AsUint64() const;
    double AsDouble() const;
    bool AsBool() const;
    const TListType& AsList() const;
    const TMapType& AsMap() const;
    TListType& AsList();
    TMapType& AsMap();

    const TString& UncheckedAsString() const noexcept;
    i64 UncheckedAsInt64() const noexcept;
    ui64 UncheckedAsUint64() const noexcept;
    double UncheckedAsDouble() const noexcept;
    bool UncheckedAsBool() const noexcept;
    const TListType& UncheckedAsList() const noexcept;
    const TMapType& UncheckedAsMap() const noexcept;
    TListType& UncheckedAsList() noexcept;
    TMapType& UncheckedAsMap() noexcept;

    // integer types cast
    // makes overflow checks
    template<typename T>
    T IntCast() const;

    // integers <-> double <-> string
    // makes overflow checks
    template<typename T>
    T ConvertTo() const;

    template<typename T>
    T& As();

    template<typename T>
    const T& As() const;

    static TNode CreateList();
    static TNode CreateList(TListType list);
    static TNode CreateMap();
    static TNode CreateMap(TMapType map);
    static TNode CreateEntity();

    const TNode& operator[](size_t index) const;
    TNode& operator[](size_t index);
    const TNode& At(size_t index) const;
    TNode& At(size_t index);

    TNode& Add() &;
    TNode Add() &&;
    TNode& Add(const TNode& node) &;
    TNode Add(const TNode& node) &&;
    TNode& Add(TNode&& node) &;
    TNode Add(TNode&& node) &&;

    bool HasKey(const TStringBuf key) const;

    TNode& operator()(const TString& key, const TNode& value) &;
    TNode operator()(const TString& key, const TNode& value) &&;
    TNode& operator()(const TString& key, TNode&& value) &;
    TNode operator()(const TString& key, TNode&& value) &&;

    const TNode& operator[](const TStringBuf key) const;
    TNode& operator[](const TStringBuf key);
    const TNode& At(const TStringBuf key) const;
    TNode& At(const TStringBuf key);

    // map getters
    // works the same way like simple getters
    const TString& ChildAsString(const TStringBuf key) const;
    i64 ChildAsInt64(const TStringBuf key) const;
    ui64 ChildAsUint64(const TStringBuf key) const;
    double ChildAsDouble(const TStringBuf key) const;
    bool ChildAsBool(const TStringBuf key) const;
    const TListType& ChildAsList(const TStringBuf key) const;
    const TMapType& ChildAsMap(const TStringBuf key) const;
    TListType& ChildAsList(const TStringBuf key);
    TMapType& ChildAsMap(const TStringBuf key);

    template<typename T>
    T ChildIntCast(const TStringBuf key) const;

    template<typename T>
    T ChildConvertTo(const TStringBuf key) const;

    template<typename T>
    const T& ChildAs(const TStringBuf key) const;

    template<typename T>
    T& ChildAs(const TStringBuf key);

    // list getters
    // works the same way like simple getters
    const TString& ChildAsString(size_t index) const;
    i64 ChildAsInt64(size_t index) const;
    ui64 ChildAsUint64(size_t index) const;
    double ChildAsDouble(size_t index) const;
    bool ChildAsBool(size_t index) const;
    const TListType& ChildAsList(size_t index) const;
    const TMapType& ChildAsMap(size_t index) const;
    TListType& ChildAsList(size_t index);
    TMapType& ChildAsMap(size_t index);

    template<typename T>
    T ChildIntCast(size_t index) const;

    template<typename T>
    T ChildConvertTo(size_t index) const;

    template<typename T>
    const T& ChildAs(size_t index) const;

    template<typename T>
    T& ChildAs(size_t index);


    // attributes
    bool HasAttributes() const;
    void ClearAttributes();
    const TNode& GetAttributes() const;
    TNode& Attributes();

    void MoveWithoutAttributes(TNode&& rhs);

    // Serialize TNode using binary yson format.
    // Methods for ysaveload.
    void Save(IOutputStream* output) const;
    void Load(IInputStream* input);

private:
    void Move(TNode&& rhs);

    void CheckType(EType type) const;

    void AssureMap();
    void AssureList();

    void CreateAttributes();

private:
    TValue Value_;
    THolder<TNode> Attributes_;

    friend bool operator==(const TNode& lhs, const TNode& rhs);
    friend bool operator!=(const TNode& lhs, const TNode& rhs);
};

bool operator==(const TNode& lhs, const TNode& rhs);
bool operator!=(const TNode& lhs, const TNode& rhs);

bool GetBool(const TNode& node);

/// Debug printer for gtest
void PrintTo(const TNode& node, std::ostream* out);

inline bool TNode::IsArithmetic() const {
    return IsInt64() || IsUint64() || IsDouble() || IsBool();
}

template<typename T>
inline T TNode::IntCast() const {
    if constexpr (std::is_integral<T>::value) {
        try {
            switch (GetType()) {
                case TNode::Uint64:
                    return SafeIntegerCast<T>(AsUint64());
                case TNode::Int64:
                    return SafeIntegerCast<T>(AsInt64());
                default:
                    ythrow TTypeError() << "IntCast() called for type " << GetType();
            }
        } catch(TBadCastException& exc) {
            ythrow TTypeError() << "TBadCastException during IntCast(): " << exc.what();
        }
    } else {
        static_assert(sizeof(T) != sizeof(T), "implemented only for std::is_integral types");
    }
}

template<typename T>
inline T TNode::ConvertTo() const {
    if constexpr (std::is_integral<T>::value) {
        switch (GetType()) {
            case NYT::TNode::String:
                return ::FromString(AsString());
            case NYT::TNode::Int64:
            case NYT::TNode::Uint64:
                return IntCast<T>();
            case NYT::TNode::Double:
                if (AsDouble() < Min<T>() || AsDouble() > MaxFloor<T>() || !std::isfinite(AsDouble())) {
                    ythrow TTypeError() << AsDouble() << " can't be converted to " << TypeName<T>();
                }
                return AsDouble();
            case NYT::TNode::Bool:
                return AsBool();
            case NYT::TNode::List:
            case NYT::TNode::Map:
            case NYT::TNode::Null:
            case NYT::TNode::Undefined:
                ythrow TTypeError() << "ConvertTo<" << TypeName<T>() << ">() called for type " << GetType();
        };
    } else {
        static_assert(sizeof(T) != sizeof(T), "should have template specialization");
    }
}

template<>
inline TString TNode::ConvertTo<TString>() const {
    switch (GetType()) {
        case NYT::TNode::String:
            return AsString();
        case NYT::TNode::Int64:
            return ::ToString(AsInt64());
        case NYT::TNode::Uint64:
            return ::ToString(AsUint64());
        case NYT::TNode::Double:
            return ::ToString(AsDouble());
        case NYT::TNode::Bool:
            return ::ToString(AsBool());
        case NYT::TNode::List:
        case NYT::TNode::Map:
        case NYT::TNode::Null:
        case NYT::TNode::Undefined:
            ythrow TTypeError() << "ConvertTo<TString>() called for type " << GetType();
    }
    Y_UNREACHABLE();
}

template<>
inline double TNode::ConvertTo<double>() const {
    switch (GetType()) {
        case NYT::TNode::String:
            return ::FromString(AsString());
        case NYT::TNode::Int64:
            return AsInt64();
        case NYT::TNode::Uint64:
            return AsUint64();
        case NYT::TNode::Double:
            return AsDouble();
        case NYT::TNode::Bool:
            return AsBool();
        case NYT::TNode::List:
        case NYT::TNode::Map:
        case NYT::TNode::Null:
        case NYT::TNode::Undefined:
            ythrow TTypeError() << "ConvertTo<double>() called for type " << GetType();
    }
}

template<>
inline bool TNode::ConvertTo<bool>() const {
    switch (GetType()) {
        case NYT::TNode::String:
            return ::FromString(AsString());
        case NYT::TNode::Int64:
            return AsInt64();
        case NYT::TNode::Uint64:
            return AsUint64();
        case NYT::TNode::Double:
            return AsDouble();
        case NYT::TNode::Bool:
            return AsBool();
        case NYT::TNode::List:
        case NYT::TNode::Map:
        case NYT::TNode::Null:
        case NYT::TNode::Undefined:
            ythrow TTypeError() << "ConvertTo<bool>() called for type " << GetType();
    }
}

template<typename T>
inline T TNode::ChildIntCast(const TStringBuf key) const {
    const auto& node = At(key);
    try {
        return node.IntCast<T>();
    } catch (TTypeError& e) {
        e << ", during getting key=" << key;
        throw e;
    } catch (...) {
        ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key;
    }
}

template<typename T>
inline T TNode::ChildIntCast(size_t index) const {
    const auto& node = At(index);
    try {
        return node.IntCast<T>();
    } catch (TTypeError& e) {
        e << ", during getting index=" << index;
        throw e;
    } catch (...) {
        ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index;
    }
}

template<typename T>
inline T TNode::ChildConvertTo(const TStringBuf key) const {
    const auto& node = At(key);
    try {
        return node.ConvertTo<T>();
    } catch (TTypeError& e) {
        e << ", during getting key=" << key;
        throw e;
    } catch (...) {
        ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key;
    }
}

template<typename T>
inline T TNode::ChildConvertTo(size_t index) const {
    const auto& node = At(index);
    try {
        return node.ConvertTo<T>();
    } catch (TTypeError& e) {
        e << ", during getting index=" << index;
        throw e;
    } catch (...) {
        ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index;
    }
}

template<typename T>
inline const T& TNode::ChildAs(const TStringBuf key) const {
    const auto& node = At(key);
    try {
        return node.As<T>();
    } catch (TTypeError& e) {
        e << ", during getting key=" << key;
        throw e;
    } catch (...) {
        ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key;
    }
}

template<typename T>
inline const T& TNode::ChildAs(size_t index) const {
    const auto& node = At(index);
    try {
        return node.As<T>();
    } catch (TTypeError& e) {
        e << ", during getting index=" << index;
        throw e;
    } catch (...) {
        ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index;
    }
}

template<typename T>
inline T& TNode::ChildAs(const TStringBuf key) {
    return const_cast<T&>(static_cast<const TNode*>(this)->ChildAs<T>(key));
}

template<typename T>
inline T& TNode::ChildAs(size_t index) {
    return const_cast<T&>(static_cast<const TNode*>(this)->ChildAs<T>(index));
}

template<typename T>
inline bool TNode::IsOfType() const noexcept {
    return std::holds_alternative<T>(Value_);
}

template<typename T>
inline T& TNode::As() {
    return std::get<T>(Value_);
}

template<typename T>
inline const T& TNode::As() const {
    return std::get<T>(Value_);
}

////////////////////////////////////////////////////////////////////////////////

namespace NNodeCmp {
    bool operator<(const TNode& lhs, const TNode& rhs);
    bool operator<=(const TNode& lhs, const TNode& rhs);
    bool operator>(const TNode& lhs, const TNode& rhs);
    bool operator>=(const TNode& lhs, const TNode& rhs);
    bool IsComparableType(const TNode::EType type);
}

////////////////////////////////////////////////////////////////////////////////

} // namespace NYT