• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OpenLightingProject / ola / 21336882381

25 Jan 2026 05:47PM UTC coverage: 45.06% (-0.7%) from 45.72%
21336882381

Pull #1984

github

web-flow
Merge b8c8613eb into 704337b09
Pull Request #1984: Add conditionals for Protobuf 22+ API changes

8556 of 19814 branches covered (43.18%)

5 of 6 new or added lines in 3 files covered. (83.33%)

324 existing lines in 56 files now uncovered.

22100 of 49046 relevant lines covered (45.06%)

48.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.17
/common/web/JsonPatchParser.cpp
1
/*
2
 * This library is free software; you can redistribute it and/or
3
 * modify it under the terms of the GNU Lesser General Public
4
 * License as published by the Free Software Foundation; either
5
 * version 2.1 of the License, or (at your option) any later version.
6
 *
7
 * This library is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10
 * Lesser General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU Lesser General Public
13
 * License along with this library; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
 *
16
 * JsonPatchParser.cpp
17
 * Create a JsonPatchSet from a string.
18
 * Copyright (C) 2014 Simon Newton
19
 */
20
#include "ola/web/JsonPatchParser.h"
21

22
#define __STDC_LIMIT_MACROS  // for UINT8_MAX & friends
23

24
#include <memory>
25
#include <string>
26
#include "ola/Logging.h"
27
#include "ola/stl/STLUtils.h"
28
#include "ola/web/Json.h"
29
#include "ola/web/JsonLexer.h"
30
#include "ola/base/Macro.h"
31

32
namespace ola {
33
namespace web {
34

35
using std::string;
36

37
const char JsonPatchParser::kPatchListError[] =
38
  "A JSON Patch document must be an array";
39
const char JsonPatchParser::kPatchElementError[] =
40
  "Elements within a JSON Patch array must be objects";
41
const char JsonPatchParser::kMissingPath[] =
42
  "Missing path specifier";
43
const char JsonPatchParser::kMissingValue[] =
44
  "Missing or invalid value";
45
const char JsonPatchParser::kMissingFrom[] =
46
  "Missing from specifier";
47
const char JsonPatchParser::kAddOp[] = "add";
48
const char JsonPatchParser::kCopyOp[] = "copy";
49
const char JsonPatchParser::kFromKey[] = "from";
50
const char JsonPatchParser::kMoveOp[] = "move";
51
const char JsonPatchParser::kOpKey[] = "op";
52
const char JsonPatchParser::kPathKey[] = "path";
53
const char JsonPatchParser::kRemoveOp[] = "remove";
54
const char JsonPatchParser::kReplaceOp[] = "replace";
55
const char JsonPatchParser::kTestOp[] = "test";
56
const char JsonPatchParser::kValueKey[] = "value";
57

58
void JsonPatchParser::Begin() {
93✔
59
  m_parser_depth = 0;
93✔
60
  m_error = "";
93✔
61
  m_key = "";
93✔
62
  m_state = TOP;
93✔
63
  m_parser.Begin();
93✔
64
}
93✔
65

66
void JsonPatchParser::End() {
93✔
67
  if (m_state != TOP) {
93✔
68
    SetError("Invalid JSON data");
×
69
  }
70
}
93✔
71

72
void JsonPatchParser::String(const string &value) {
138✔
73
  switch (m_state) {
138✔
74
    case TOP:
1✔
75
      SetError(kPatchListError);
1✔
76
      break;
1✔
77
    case PATCH_LIST:
1✔
78
      SetError(kPatchElementError);
1✔
79
      break;
1✔
80
    case PATCH:
136✔
81
      HandlePatchString(value);
136✔
82
      break;
136✔
83
    case VALUE:
×
84
      m_parser.String(value);
×
85
      break;
×
86
  }
87
}
138✔
88

89
void JsonPatchParser::Number(uint32_t value) {
15✔
90
  HandleNumber(value);
15✔
91
}
15✔
92

93
void JsonPatchParser::Number(int32_t value) {
×
94
  HandleNumber(value);
×
95
}
×
96

97
void JsonPatchParser::Number(uint64_t value) {
×
98
  HandleNumber(value);
×
99
}
×
100

101
void JsonPatchParser::Number(int64_t value) {
×
102
  HandleNumber(value);
×
103
}
×
104

105
void JsonPatchParser::Number(const JsonDouble::DoubleRepresentation &rep) {
5✔
106
  HandleNumber(rep);
5✔
107
}
5✔
108

109
void JsonPatchParser::Number(double value) {
×
110
  HandleNumber(value);
×
111
}
×
112

113
void JsonPatchParser::Bool(bool value) {
11✔
114
  switch (m_state) {
11✔
115
    case TOP:
1✔
116
      SetError(kPatchListError);
1✔
117
      break;
1✔
118
    case PATCH_LIST:
×
119
      SetError(kPatchElementError);
×
120
      break;
×
121
    case PATCH:
10✔
122
      if (m_key == kValueKey) {
10✔
123
        m_value.reset(new JsonBool(value));
2✔
124
      }
125
      break;
126
    case VALUE:
×
127
      m_parser.Bool(value);
×
128
      break;
×
129
  }
130
}
11✔
131

132
void JsonPatchParser::Null() {
10✔
133
  switch (m_state) {
10✔
134
    case TOP:
1✔
135
      SetError(kPatchListError);
1✔
136
      break;
1✔
137
    case PATCH_LIST:
1✔
138
      SetError(kPatchElementError);
1✔
139
      break;
1✔
140
    case PATCH:
8✔
141
      if (m_key == kValueKey) {
8✔
142
        m_value.reset(new JsonNull());
×
143
      }
144
      break;
145
    case VALUE:
×
146
      m_parser.Null();
×
147
      break;
×
148
  }
149
}
10✔
150

151
void JsonPatchParser::OpenArray() {
102✔
152
  switch (m_state) {
102✔
153
    case TOP:
88✔
154
      m_state = PATCH_LIST;
88✔
155
      break;
88✔
156
    case PATCH_LIST:
1✔
157
      SetError(kPatchElementError);
1✔
158
      break;
1✔
159
    case PATCH:
11✔
160
      m_parser_depth = 0;
11✔
161
      m_state = VALUE;
11✔
162
      // fall through
163
      OLA_FALLTHROUGH
13✔
164
    case VALUE:
13✔
165
      m_parser_depth++;
13✔
166
      m_parser.OpenArray();
13✔
167
      break;
13✔
168
  }
169
}
102✔
170

171
void JsonPatchParser::CloseArray() {
102✔
172
  switch (m_state) {
102✔
173
    case TOP:
174
      break;
175
    case PATCH_LIST:
88✔
176
      m_state = TOP;
88✔
177
      break;
88✔
178
    case PATCH:
179
      break;
180
    case VALUE:
13✔
181
      m_parser.CloseArray();
13✔
182
      m_parser_depth--;
13✔
183
      if (m_parser_depth == 0) {
13✔
184
        if (m_key == kValueKey) {
11✔
185
          m_value.reset(m_parser.ClaimRoot());
3✔
186
        }
187
        m_state = PATCH;
11✔
188
      }
189
  }
190
}
102✔
191

192
void JsonPatchParser::OpenObject() {
119✔
193
  switch (m_state) {
119✔
194
    case TOP:
1✔
195
      SetError(kPatchListError);
1✔
196
      break;
1✔
197
    case PATCH_LIST:
84✔
198
      m_state = PATCH;
84✔
199
      m_value.reset();
84✔
200
      m_path.Reset();
84✔
201
      m_op = "";
84✔
202
      m_from.Reset();
84✔
203
      break;
84✔
204
    case PATCH:
33✔
205
      m_parser_depth = 0;
33✔
206
      m_state = VALUE;
33✔
207
      // fall through
208
      OLA_FALLTHROUGH
34✔
209
    case VALUE:
34✔
210
      m_parser_depth++;
34✔
211
      m_parser.OpenObject();
34✔
212
      break;
34✔
213
  }
214
}
119✔
215

216
void JsonPatchParser::ObjectKey(const std::string &key) {
213✔
217
  if (m_state == VALUE) {
213✔
218
    m_parser.ObjectKey(key);
1✔
219
  } else {
220
    m_key = key;
212✔
221
  }
222
}
213✔
223

224
void JsonPatchParser::CloseObject() {
119✔
225
  switch (m_state) {
119✔
226
    case TOP:
227
      break;
228
    case PATCH_LIST:
229
      break;
230
    case PATCH:
84✔
231
      m_state = PATCH_LIST;
84✔
232
      HandlePatch();
84✔
233
      break;
84✔
234
    case VALUE:
34✔
235
      m_parser.CloseObject();
34✔
236
      m_parser_depth--;
34✔
237
      if (m_parser_depth == 0) {
34✔
238
        if (m_key == kValueKey) {
33✔
239
          m_value.reset(m_parser.ClaimRoot());
23✔
240
        }
241
        m_state = PATCH;
33✔
242
      }
243
      break;
244
  }
245
}
119✔
246

247
void JsonPatchParser::SetError(const string &error) {
75✔
248
  if (m_error.empty()) {
75✔
249
    m_error = error;
75✔
250
  }
251
}
75✔
252

253
string JsonPatchParser::GetError() const {
75✔
254
  return m_error;
75✔
255
}
256

257
bool JsonPatchParser::IsValid() const {
93✔
258
  return m_error.empty();
93✔
259
}
260

261
template <typename T>
UNCOV
262
void JsonPatchParser::HandleNumber(const T &value) {
×
UNCOV
263
  switch (m_state) {
×
UNCOV
264
    case TOP:
×
UNCOV
265
      SetError(kPatchListError);
×
UNCOV
266
      break;
×
UNCOV
267
    case PATCH_LIST:
×
UNCOV
268
      SetError(kPatchElementError);
×
UNCOV
269
      break;
×
UNCOV
270
    case PATCH:
×
UNCOV
271
      if (m_key == kValueKey) {
×
UNCOV
272
        m_value.reset(JsonValue::NewValue(value));
×
273
      }
274
      break;
UNCOV
275
    case VALUE:
×
UNCOV
276
      m_parser.Number(value);
×
UNCOV
277
      break;
×
278
  }
UNCOV
279
}
×
280

281
void JsonPatchParser::HandlePatchString(const std::string &value) {
136✔
282
  if (m_key == kOpKey) {
136✔
283
    m_op = value;
83✔
284
  } else if (m_key == kFromKey) {
53✔
285
    m_from.Set(value);
16✔
286
  } else if (m_key == kPathKey) {
37✔
287
    m_path.Set(value);
35✔
288
  } else if (m_key == kValueKey) {
2✔
289
    m_value.reset(new JsonString(value));
2✔
290
  }
291
}
136✔
292

293
void JsonPatchParser::HandlePatch() {
84✔
294
  if (!m_path.IsSet()) {
84✔
295
    SetError(kMissingPath);
49✔
296
    return;
49✔
297
  }
298

299
  if (m_op == kAddOp) {
35✔
300
    if (!m_value.get()) {
3✔
301
      SetError(kMissingValue);
1✔
302
      return;
1✔
303
    }
304
    m_patch_set->AddOp(
4✔
305
        new JsonPatchAddOp(JsonPointer(m_path.Value()), m_value.release()));
6✔
306
  } else if (m_op == kRemoveOp) {
32✔
307
    m_patch_set->AddOp(new JsonPatchRemoveOp(JsonPointer(m_path.Value())));
9✔
308
  } else if (m_op == kReplaceOp) {
29✔
309
    if (!m_value.get()) {
6✔
310
      SetError(kMissingValue);
1✔
311
      return;
1✔
312
    }
313
    m_patch_set->AddOp(
10✔
314
        new JsonPatchReplaceOp(JsonPointer(m_path.Value()), m_value.release()));
15✔
315
  } else if (m_op == kMoveOp) {
23✔
316
    if (!m_from.IsSet()) {
9✔
317
      SetError(kMissingFrom);
6✔
318
      return;
6✔
319
    }
320
    m_patch_set->AddOp(
3✔
321
        new JsonPatchMoveOp(JsonPointer(m_from.Value()),
6✔
322
                            JsonPointer(m_path.Value())));
9✔
323
  } else if (m_op == kCopyOp) {
14✔
324
    if (!m_from.IsSet()) {
9✔
325
      SetError(kMissingFrom);
6✔
326
      return;
6✔
327
    }
328
    m_patch_set->AddOp(
3✔
329
        new JsonPatchCopyOp(JsonPointer(m_from.Value()),
6✔
330
                            JsonPointer(m_path.Value())));
9✔
331
  } else if (m_op == kTestOp) {
5✔
332
    if (!m_value.get()) {
5✔
333
      SetError(kMissingValue);
1✔
334
      return;
1✔
335
    }
336
    m_patch_set->AddOp(
8✔
337
        new JsonPatchTestOp(JsonPointer(m_path.Value()), m_value.release()));
12✔
338
  } else {
339
    SetError("Invalid or missing 'op'");
×
340
  }
341
}
342

343
bool JsonPatchParser::Parse(const std::string &input,
94✔
344
                            JsonPatchSet *patch_set,
345
                            std::string *error) {
346
  JsonPatchParser parser(patch_set);
94✔
347
  bool ok = JsonLexer::Parse(input, &parser) && parser.IsValid();
94✔
348
  if (!ok) {
75✔
349
    *error = parser.GetError();
75✔
350
  }
351
  return ok;
94✔
352
}
94✔
353
}  // namespace web
354
}  // namespace ola
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc