Euphoria
json.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <optional>
4 #include <set>
5 
6 #include "jsonh/jsonh.h"
7 
8 #include "log/log.h"
9 
10 #include "assert/assert.h"
11 
12 #include "base/result.h"
13 
14 
15 namespace eu::io
16 {
17  struct FileSystem;
18  struct FilePath;
19 
20  struct Json { jsonh::Document doc; jsonh::Value root; };
22  struct JsonError { JsonErrorType error; std::string display; };
23 
25 
26  std::string get_string_from_path_for_debugging(FileSystem* fs, const FilePath& p);
27 
30  (
31  FileSystem* fs,
32  const FilePath& file_name
33  );
34 
35  std::string could_be(const std::string& invalid_value, const std::vector<std::string>& possible_values);
36 
37  struct ObjectQuery
38  {
39  explicit ObjectQuery(const jsonh::Object* o);
40 
41  const jsonh::Object* object;
42 
43  std::set<std::string> found; // all found props
44  std::set<std::string> missing; // all missed props
45  std::vector<std::string> required; // all missed and required props
46 
47  std::optional<jsonh::Value> get(const std::string& name);
48 
49  std::optional<std::string> get_missing_errors_message();
50  };
51 }
52 
53 #define JSON_PARSE_FUNC(TYPE) \
54  bool parse(::eu::log::Logger* log, TYPE* owner, jsonh::Value root_val, const jsonh::Document* doc)
55 
56 #define JSON_EXPECT(x, msg) \
57  do {\
58  if (!(x))\
59  {\
60  log->error(msg);\
61  return false;\
62  }\
63  } while(false)
64 
65 #define JSON_BEGIN_ENUM(TYPE) \
66  const auto* dnu_val = root_val.AsString(doc);\
67  JSON_EXPECT(dnu_val, "Expected string value for " #TYPE);\
68  const std::string dnu_enum_name = #TYPE; \
69  using dnu_enum_type = TYPE; \
70  std::vector<std::string> dnu_values
71 
72 #define JSON_ENUM_VAL(VAL) \
73  if(dnu_val->value == #VAL) {\
74  *owner = dnu_enum_type::VAL;\
75  return true; \
76  } \
77  else { \
78  dnu_values.emplace_back(#VAL);\
79  }
80 #define JSON_END_ENUM() \
81  log->error(fmt::format("{} was invalid for {}, could be {}",\
82  dnu_val->value, dnu_enum_name,\
83  eu::io::could_be(dnu_val->value, dnu_values)));\
84  return false
85 
86 #define JSON_BEGIN_OBJECT() \
87  const auto* root = root_val.AsObject(doc);\
88  JSON_EXPECT(root, "root is not a object");\
89  auto object = ::eu::io::ObjectQuery{ root }
90 
91 #define JSON_VAL_X(TYPE, PROP, REQUIRED) \
92  do {\
93  auto dnu_json = object.get(#PROP);\
94  if (dnu_json)\
95  {\
96  auto* dnu_prop = dnu_json->As ## TYPE(doc);\
97  JSON_EXPECT(dnu_prop, #PROP " is not a " #TYPE);\
98  owner->PROP = dnu_prop->value;\
99  }\
100  else if constexpr(REQUIRED)\
101  {\
102  object.required.emplace_back(#PROP);\
103  }\
104  } while(false)
105 
106 #define JSON_VAL_OBJ_X(TYPE, PROP, REQUIRED, CON) \
107  do {\
108  auto dnu_json = object.get(#PROP);\
109  if (dnu_json)\
110  {\
111  TYPE dnu_prop = {};\
112  if (false == parse(log, &dnu_prop, *dnu_json, doc))\
113  {\
114  return false;\
115  }\
116  owner->PROP = CON(std::move(dnu_prop));\
117  }\
118  else if constexpr(REQUIRED)\
119  {\
120  object.required.emplace_back(#PROP);\
121  }\
122  } while(false)
123 
124 #define JSON_VAL_OBJ(TYPE, PROP) JSON_VAL_OBJ_X(TYPE, PROP, true, )
125 #define JSON_VAL_OPT_OBJ(TYPE, PROP) JSON_VAL_OBJ_X(TYPE, PROP, false, )
126 #define JSON_VAL_OPT_OBJ_CON(TYPE, PROP, CON) JSON_VAL_OBJ_X(TYPE, PROP, false, CON)
127 
128 #define JSON_VAL(TYPE, PROP) JSON_VAL_X(TYPE, PROP, true)
129 #define JSON_OPT_VAL(TYPE, PROP) JSON_VAL_X(TYPE, PROP, false)
130 
131 // todo(Gustv): remove expect in favor of if statements, logging and setting a failure state so this node is loaded completly before aborting
132 #define JSON_ARRAY(TYPE, PROP) \
133  do{\
134  auto dnu_val = object.get(#PROP);\
135  JSON_EXPECT(dnu_val, "missing property " #PROP);\
136  auto* dnu_array = dnu_val->AsArray(doc);\
137  JSON_EXPECT(dnu_array, #PROP " is not a array");\
138  for (const auto dnu_json : dnu_array->array)\
139  {\
140  TYPE dnu_prop;\
141  if (false == parse(log, &dnu_prop, dnu_json, doc))\
142  {\
143  return false;\
144  }\
145  owner->PROP.emplace_back(std::move(dnu_prop));\
146  }\
147  } while(false)
148 
149 #define JSON_ARRAY_PROP_CON(TYPE, PROP, CON) \
150  do{\
151  auto dnu_val = object.get(#PROP);\
152  JSON_EXPECT(dnu_val, "missing property " #PROP);\
153  auto* dnu_array = dnu_val->AsArray(doc);\
154  JSON_EXPECT(dnu_array, #PROP " is not a array");\
155  for (const auto dnu_json : dnu_array->array)\
156  {\
157  const auto* dnu_prop = dnu_json.As ## TYPE(doc);\
158  JSON_EXPECT(dnu_prop, #PROP " is not a " #TYPE);\
159  owner->PROP.emplace_back(CON(dnu_prop->value));\
160  }\
161  } while(false)
162 
163 #define JSON_ARRAY_PROP(TYPE, PROP) \
164  JSON_ARRAY_PROP_CON(TYPE, PROP, )
165 
166 #define JSON_END_OBJECT() \
167  const auto missing = object.get_missing_errors_message();\
168  if (missing)\
169  {\
170  log->error(*missing);\
171  return false;\
172  }\
173  return true
174 
Definition: enum.h:8
JsonResult read_json_file(FileSystem *fs, const FilePath &file_name)
Definition: json.cc:58
std::string could_be(const std::string &invalid_value, const std::vector< std::string > &possible_values)
Definition: json.cc:89
std::string get_string_from_path_for_debugging(FileSystem *fs, const FilePath &p)
Definition: json.cc:21
JsonErrorType
Definition: json.h:21
JsonErrorType error
Definition: json.h:22
std::string display
Definition: json.h:22
jsonh::Document doc
Definition: json.h:20
jsonh::Value root
Definition: json.h:20
std::optional< std::string > get_missing_errors_message()
Definition: json.cc:134
ObjectQuery(const jsonh::Object *o)
Definition: json.cc:128
const jsonh::Object * object
Definition: json.h:41
std::vector< std::string > required
Definition: json.h:45
std::set< std::string > found
Definition: json.h:43
std::optional< jsonh::Value > get(const std::string &name)
Definition: json.cc:112
std::set< std::string > missing
Definition: json.h:44