/src/valijson/include/valijson/validation_results.hpp
Line | Count | Source |
1 | | #pragma once |
2 | | |
3 | | #include <deque> |
4 | | #include <string> |
5 | | #include <utility> |
6 | | #include <vector> |
7 | | |
8 | | namespace valijson { |
9 | | |
10 | | /** |
11 | | * @brief Class that encapsulates the storage of validation errors. |
12 | | * |
13 | | * This class maintains an internal FIFO queue of errors that are reported |
14 | | * during validation. Errors are pushed on to the back of an internal |
15 | | * queue, and can retrieved by popping them from the front of the queue. |
16 | | */ |
17 | | class ValidationResults |
18 | | { |
19 | | public: |
20 | | |
21 | | enum Kind |
22 | | { |
23 | | kArray, |
24 | | kObject |
25 | | }; |
26 | | |
27 | | struct Segment |
28 | | { |
29 | | /// What kind of traversal is this? |
30 | | Kind kind; |
31 | | |
32 | | /// Index or name of property to traverse into |
33 | | std::string name; |
34 | | }; |
35 | | |
36 | | typedef std::vector<Segment> Path; |
37 | | |
38 | | /** |
39 | | * @brief Describes a validation error. |
40 | | * |
41 | | * This struct is used to pass around the path and description of a |
42 | | * validation error. The path is stored in two formats. First is 'context', |
43 | | * a legacy format, used only by Valijson. Second is JSON Pointer format, |
44 | | * which is defined in RFC 6901. |
45 | | */ |
46 | | struct Error |
47 | | { |
48 | | /** |
49 | | * Path to the node that failed validation (LEGACY). |
50 | | * |
51 | | * @deprecated use \c jsonPointer instead |
52 | | */ |
53 | | std::vector<std::string> context; |
54 | | |
55 | | /// A detailed description of the validation error. |
56 | | std::string description; |
57 | | |
58 | | /// JSON Pointer identifying the node that failed validation. |
59 | | std::string jsonPointer; |
60 | | }; |
61 | | |
62 | | /** |
63 | | * @brief Return begin iterator for results in the queue. |
64 | | */ |
65 | | std::deque<Error>::const_iterator begin() const |
66 | 0 | { |
67 | 0 | return m_errors.begin(); |
68 | 0 | } |
69 | | |
70 | | /** |
71 | | * @brief Return end iterator for results in the queue. |
72 | | */ |
73 | | std::deque<Error>::const_iterator end() const |
74 | 0 | { |
75 | 0 | return m_errors.end(); |
76 | 0 | } |
77 | | |
78 | | /** |
79 | | * @brief Return the number of errors in the queue. |
80 | | */ |
81 | | size_t numErrors() const |
82 | 0 | { |
83 | 0 | return m_errors.size(); |
84 | 0 | } |
85 | | |
86 | | /** |
87 | | * @brief Copy an Error and push it on to the back of the queue. |
88 | | * |
89 | | * @param error Reference to an Error object to be copied. |
90 | | */ |
91 | | void pushError(const Error &error) |
92 | 94.9k | { |
93 | 94.9k | m_errors.push_back(error); |
94 | 94.9k | } |
95 | | |
96 | | /** |
97 | | * @brief Push an error onto the back of the queue. |
98 | | * |
99 | | * @param path Path of the validation error. |
100 | | * @param description Description of the validation error. |
101 | | */ |
102 | | void |
103 | | pushError(const Path &path, const std::string &description) |
104 | 1.88M | { |
105 | | // construct legacy context |
106 | | // e.g. <root>["my_object"][1]["some_property"] |
107 | 1.88M | const std::vector<std::string> context = toContext(path); |
108 | | |
109 | | // construct JSON pointer |
110 | | // e.g. /my_object/1/some_property |
111 | 1.88M | const std::string jsonPointer = toJsonPointer(path); |
112 | | |
113 | 1.88M | m_errors.push_back({context, description, jsonPointer}); |
114 | 1.88M | } |
115 | | |
116 | | /** |
117 | | * @brief Pop an error from the front of the queue. |
118 | | * |
119 | | * @param error Reference to an Error object to populate. |
120 | | * |
121 | | * @returns true if an Error was popped, false otherwise. |
122 | | */ |
123 | | bool |
124 | | popError(Error &error) |
125 | 104k | { |
126 | 104k | if (m_errors.empty()) { |
127 | 9.20k | return false; |
128 | 9.20k | } |
129 | | |
130 | 94.9k | error = m_errors.front(); |
131 | 94.9k | m_errors.pop_front(); |
132 | 94.9k | return true; |
133 | 104k | } |
134 | | |
135 | | private: |
136 | | |
137 | | /// FIFO queue of validation errors that have been reported |
138 | | std::deque<Error> m_errors; |
139 | | |
140 | | static std::string escapeJsonPointerToken(const std::string &token) |
141 | 217k | { |
142 | 217k | std::string escaped; |
143 | 217k | escaped.reserve(token.size()); |
144 | | |
145 | 6.07M | for (const char ch : token) { |
146 | 6.07M | switch (ch) { |
147 | 17.6k | case '~': |
148 | 17.6k | escaped.append("~0"); |
149 | 17.6k | break; |
150 | 18.4k | case '/': |
151 | 18.4k | escaped.append("~1"); |
152 | 18.4k | break; |
153 | 6.03M | default: |
154 | 6.03M | escaped.push_back(ch); |
155 | 6.03M | break; |
156 | 6.07M | } |
157 | 6.07M | } |
158 | | |
159 | 217k | return escaped; |
160 | 217k | } |
161 | | |
162 | | /** |
163 | | * @brief Convert a path to a legacy v1.0 context string |
164 | | * |
165 | | * e.g. <root>["my_object"][1]["some_property"] |
166 | | */ |
167 | | static std::vector<std::string> toContext(const Path &path) |
168 | 1.88M | { |
169 | 1.88M | auto context = std::vector<std::string>(); |
170 | 1.88M | context.push_back("<root>"); |
171 | 1.88M | for (const auto &segment : path) { |
172 | 217k | if (segment.kind == kObject) { |
173 | 203k | std::string s("[\""); |
174 | 203k | s += segment.name; |
175 | 203k | s += "\"]"; |
176 | 203k | context.push_back(s); |
177 | 203k | } else { |
178 | 14.3k | std::string s("["); |
179 | 14.3k | s += segment.name; |
180 | 14.3k | s += "]"; |
181 | 14.3k | context.push_back(s); |
182 | 14.3k | } |
183 | 217k | } |
184 | | |
185 | 1.88M | return context; |
186 | 1.88M | } |
187 | | |
188 | | /** |
189 | | * Convert a path to a JSON Pointer |
190 | | * |
191 | | * e.g. /my_object/1/some_property |
192 | | */ |
193 | | static std::string toJsonPointer(const Path &path) |
194 | 1.88M | { |
195 | 1.88M | std::string pointer; |
196 | 1.88M | for (const auto &segment : path) { |
197 | 217k | pointer.push_back('/'); |
198 | 217k | pointer.append(escapeJsonPointerToken(segment.name)); |
199 | 217k | } |
200 | | |
201 | 1.88M | return pointer; |
202 | 1.88M | } |
203 | | }; |
204 | | |
205 | | } // namespace valijson |