-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathgon.h
160 lines (134 loc) · 8.57 KB
/
gon.h
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
//Glaiel Object Notation
//its json, minus the crap!
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include <functional>
class GonObject {
public:
static const GonObject null_gon;
static GonObject non_const_null_gon;
static std::string last_accessed_named_field;//used for error reporting when a field is missing,
//this assumes you don't cache a field then try to access it later
//as the error report for fields uses this value for its message (to avoid creating and destroying a ton of dummy-objects)
//this isn't a great or super accurate solution for errors, but it's better than nothing
//default just throws the string, can be set if you want to avoid exceptions
static std::function<void(const std::string&)> ErrorCallback;
enum class FieldType {
NULLGON,
STRING,
NUMBER,
OBJECT,
ARRAY,
BOOL
};
enum class MergeMode {
DEFAULT,
APPEND,
MERGE,
OVERWRITE,
//for numbers:
ADD,
MULTIPLY
};
std::unordered_map<std::string, int> children_map;
std::vector<GonObject> children_array;
int int_data;
double float_data;
bool bool_data;
std::string string_data;
std::string name;
FieldType type;
static MergeMode MergePolicyAppend(const GonObject& field_a, const GonObject& field_b);
static MergeMode MergePolicyMerge(const GonObject& field_a, const GonObject& field_b);
static MergeMode MergePolicyOverwrite(const GonObject& field_a, const GonObject& field_b);
static GonObject Load(const std::string& filename);
static GonObject LoadFromBuffer(const std::string& buffer);
typedef std::function<MergeMode(const GonObject& field_a, const GonObject& field_b)> MergePolicyCallback;
GonObject();
//throw error if accessing wrong type, otherwise return correct type
std::string String() const;
const char* CString() const;
int Int() const;
double Number() const;
double Percent() const;
bool Bool() const;
//returns a default value if the field doesn't exist or is the wrong type
std::string String(const std::string& _default) const;
const char* CString(const char* _default) const;
int Int(int _default) const;
double Number(double _default) const;
double Percent(double _default) const; //if field is a number-parsable string that ends with a %, return that number divided by 100, otherwise just return the number
bool Bool(bool _default) const;
bool Contains(const std::string& child) const;
bool ContainsNthChildWithName(const std::string& child, int index) const; //similar to just operator[], however if index is more than 0 then it skips the first N children with that name (ex, index=1 searches for the *second* field named "child" in the object)
bool Contains(int child) const;
bool Exists() const; //true if non-null
bool IsPercent() const; //while regular numbers can be read as percents, "is percent" only returns true if the string ends with a %
//returns null_gon if the field does not exist.
const GonObject& operator[](const std::string& child) const;
GonObject& operator[](const std::string& child);
//returns self if child does not exist (useful for stuff that can either be a child or the default property of a thing)
const GonObject& ChildOrSelf(const std::string& child) const;
GonObject& ChildOrSelf(const std::string& child);
//similar to just operator[], however if index is more than 0 then it skips the first N children with that name (ex, index=1 searches for the *second* field named "child" in the object)
const GonObject& NthChildWithName(const std::string& child, int index) const;
GonObject& NthChildWithName(const std::string& child, int index);
//checks for [child][field], if that doesnt exists returns [field] instead. look I was using this pattern a ton with things that could have optional variants
const GonObject& FieldInChildOrSelf(const std::string& child, const std::string& field) const;
GonObject& FieldInChildOrSelf(const std::string& child, const std::string& field);
//returns self if index is not an array,
//all objects can be considered an array of size 1 with themselves as the member, if they are not an ARRAY or an OBJECT
const GonObject& operator[](int childindex) const;
GonObject& operator[](int childindex);
int Size() const;
//compatability with std
//all objects can be considered an array of size 1 with themselves as the member, if they are not an ARRAY or an OBJECT
int size() const;
bool empty() const;
const GonObject* begin() const;
const GonObject* end() const;
GonObject* begin();
GonObject* end();
//mostly used for debugging, as GON is not meant for saving files usually
void DebugOut() const;
void Save(const std::string& outfilename) const;
std::string SaveToStr(bool compact = false) const;
std::string GetOutStr(const std::string& tab = " ", const std::string& line_break = "\n", const std::string& current_tab = "") const;
//if nullgon -> promotes to object
//if object or array -> adds as child
//otherwise, error
void InsertChild(const GonObject& other);
void InsertChild(std::string cname, const GonObject& other);
//merging/combining functions
//if self and other are an OBJECT: other will be appended to self
//if self and other are an ARRAY: other will be appended to self
//if self and other are STRINGS: the strings are appended
//if self is a null gon: overwrite self with other
//otherwise: error
//(note if a field with the same name is used multiple times, the most recently added one is mapped to the associative array lookup table, however duplicate fields will still exist)
void Append(const GonObject& other);
//if self and other are an OBJECT: fields with matching names will be overwritten, new fields appended
//if self and other are an ARRAY: other will be appended to self
//if self is a null gon: overwrite self with other
//otherwise: error
//(OnOverwrite can be specified if you want a warning or error if gons contain overlapping members)
//ShallowMerge is not recursive into children, DeepMerge is
void ShallowMerge(const GonObject& other, std::function<void(const GonObject& a, const GonObject& b)> OnOverwrite = NULL);
//if self and other are an OBJECT: fields with matching names will be DeepMerged, new fields appended
//if self and other are an ARRAY: fields with matching indexes will be DeepMerged, additional fields appended
//if self and other mismatch: other will overwrite self
//ObjectMergePolicy and ArrayMergePolicy can be specified if you want to change how fields merge on a per-field basis
void DeepMerge(const GonObject& other, MergePolicyCallback ObjectMergePolicy = MergePolicyMerge, MergePolicyCallback ArrayMergePolicy = MergePolicyMerge);
//similar to deepmerge, however the merge policy for the patch is specified in the patch itself (ex, naming a field "myfield.append" will append it's contents to the end of "myfield" in self
//possible suffixes: .overwrite, .append, .merge, .add, .multiply
//with "overwrite", no merge modes specified in sub-fields will have any effect
//with "append", specifying a merge mode in a sub field will use that mode for that field only
//"merge" is the default
//if the patch has node named ".append", ".overwrite", or ".merge", instead of that node getting patched to self, the contents of that node are patched with self instead
//if both fields are strings: .append will append the strings
//if both fields are numbers: .add, .multiply can be used to add/subtract numbers
//.add is treated as .append for non-numerical types, .multiply is treated as .merge for non-numerical types
void PatchMerge(const GonObject& patch);
};