NeBuild dev
Loading...
Searching...
No Matches
at_path.inl
Go to the documentation of this file.
1// # This file is a part of toml++ and is subject to the the terms of the MIT license.
2// # Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
3// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
4// SPDX-License-Identifier: MIT
5#pragma once
6
7// # {{
8#include "preprocessor.hpp"
9#if !TOML_IMPLEMENTATION
10#error This is an implementation-only header.
11#endif
12// # }}
13
14#include "array.hpp"
15#include "at_path.hpp"
16#include "table.hpp"
18#if TOML_INT_CHARCONV
19#include <charconv>
20#else
21#include <sstream>
22#endif
24#include "header_start.hpp"
25
28 bool TOML_CALLCONV parse_path(const std::string_view path, void* const data,
29 const parse_path_callback<std::string_view> on_key,
30 const parse_path_callback<size_t> on_index) {
31 // a blank string is a valid path; it's just one component representing the "" key
32 if (path.empty()) return on_key(data, ""sv);
33
34 size_t pos = 0;
35 const auto end = path.length();
36 bool prev_was_array_indexer = false;
37 bool prev_was_dot = true; // invisible root 'dot'
38
39 while (pos < end) {
40 // start of an array indexer
41 if (path[pos] == '[') {
42 // find first digit in index
43 size_t index_start = pos + 1u;
44 while (true) {
45 if TOML_UNLIKELY (index_start >= path.length()) return false;
46
47 const auto c = path[index_start];
48 if TOML_LIKELY (c >= '0' && c <= '9')
49 break;
50 else if (c == ' ' || c == '\t')
51 index_start++;
52 else
53 return false;
54 }
55 TOML_ASSERT(path[index_start] >= '0');
56 TOML_ASSERT(path[index_start] <= '9');
57
58 // find end of index (first non-digit character)
59 size_t index_end = index_start + 1u;
60 while (true) {
61 // if an array indexer is missing the trailing ']' at the end of the string, permissively
62 // accept it
63 if TOML_UNLIKELY (index_end >= path.length()) break;
64
65 const auto c = path[index_end];
66 if (c >= '0' && c <= '9')
67 index_end++;
68 else if (c == ']' || c == ' ' || c == '\t' || c == '.' || c == '[')
69 break;
70 else
71 return false;
72 }
73 TOML_ASSERT(path[index_end - 1u] >= '0');
74 TOML_ASSERT(path[index_end - 1u] <= '9');
75
76 // move pos to after indexer (char after closing ']' or permissively EOL/subkey '.'/next
77 // opening '[')
78 pos = index_end;
79 while (true) {
80 if TOML_UNLIKELY (pos >= path.length()) break;
81
82 const auto c = path[pos];
83 if (c == ']') {
84 pos++;
85 break;
86 } else if TOML_UNLIKELY (c == '.' || c == '[')
87 break;
88 else if (c == '\t' || c == ' ')
89 pos++;
90 else
91 return false;
92 }
93
94 // get array index substring
95 auto index_str = path.substr(index_start, index_end - index_start);
96
97 // parse the actual array index to an integer type
98 size_t index;
99 if (index_str.length() == 1u)
100 index = static_cast<size_t>(index_str[0] - '0');
101 else {
102#if TOML_INT_CHARCONV
103
104 auto fc_result =
105 std::from_chars(index_str.data(), index_str.data() + index_str.length(), index);
106 if (fc_result.ec != std::errc{}) return false;
107
108#else
109
110 std::stringstream ss;
111 ss.imbue(std::locale::classic());
112 ss.write(index_str.data(), static_cast<std::streamsize>(index_str.length()));
113 if (!(ss >> index)) return false;
114
115#endif
116 }
117
118 prev_was_dot = false;
119 prev_was_array_indexer = true;
120
121 if (!on_index(data, index)) return false;
122 }
123
124 // start of a new table child
125 else if (path[pos] == '.') {
126 // a dot immediately following another dot (or at the beginning of the string) is as if we'd
127 // asked for an empty child in between, e.g.
128 //
129 // foo..bar
130 //
131 // is equivalent to
132 //
133 // "foo".""."bar"
134 //
135 if (prev_was_dot && !on_key(data, ""sv)) return false;
136
137 pos++;
138 prev_was_dot = true;
139 prev_was_array_indexer = false;
140 }
141
142 // an errant closing ']'
143 else if TOML_UNLIKELY (path[pos] == ']')
144 return false;
145
146 // some regular subkey
147 else {
148 const auto subkey_start = pos;
149 const auto subkey_len =
150 impl::min(path.find_first_of(".[]"sv, subkey_start + 1u), path.length()) - subkey_start;
151 const auto subkey = path.substr(subkey_start, subkey_len);
152
153 // a regular subkey segment immediately after an array indexer is OK if it was all
154 // whitespace, e.g.:
155 //
156 // "foo[0] .bar"
157 // ^^ skip this
158 //
159 // otherwise its an error (since it would have to be preceeded by a dot)
160 if (prev_was_array_indexer) {
161 auto non_ws = subkey.find_first_not_of(" \t");
162 if (non_ws == std::string_view::npos) {
163 pos += subkey_len;
164 prev_was_dot = false;
165 prev_was_array_indexer = false;
166 continue;
167 } else
168 return false;
169 }
170
171 pos += subkey_len;
172 prev_was_dot = false;
173 prev_was_array_indexer = false;
174
175 if (!on_key(data, subkey)) return false;
176 }
177 }
178
179 // Last character was a '.', which implies an empty string key at the end of the path
180 if (prev_was_dot && !on_key(data, ""sv)) return false;
181
182 return true;
183 }
184}
186
189 node_view<node> TOML_CALLCONV at_path(node & root, std::string_view path) noexcept {
190 // early-exit sanity-checks
191 if (root.is_value()) return {};
192 if (auto tbl = root.as_table(); tbl && tbl->empty()) return {};
193 if (auto arr = root.as_array(); arr && arr->empty()) return {};
194
195 node* current = &root;
196
197 static constexpr auto on_key = [](void* data, std::string_view key) noexcept -> bool {
198 auto& curr = *static_cast<node**>(data);
199 TOML_ASSERT_ASSUME(curr);
200
201 const auto current_table = curr->as<table>();
202 if (!current_table) return false;
203
204 curr = current_table->get(key);
205 return curr != nullptr;
206 };
207
208 static constexpr auto on_index = [](void* data, size_t index) noexcept -> bool {
209 auto& curr = *static_cast<node**>(data);
210 TOML_ASSERT_ASSUME(curr);
211
212 const auto current_array = curr->as<array>();
213 if (!current_array) return false;
214
215 curr = current_array->get(index);
216 return curr != nullptr;
217 };
218
219 if (!impl::parse_path(path, &current, on_key, on_index)) current = nullptr;
220
221 return node_view{current};
222 }
223
225 node_view<const node> TOML_CALLCONV at_path(const node& root, std::string_view path) noexcept {
226 return node_view<const node>{at_path(const_cast<node&>(root), path).node()};
227 }
228
229#if TOML_ENABLE_WINDOWS_COMPAT
230
232 node_view<node> TOML_CALLCONV at_path(node & root, std::wstring_view path) {
233 // these are the same top-level checks from the narrow-string version;
234 // they're hoisted up here to avoid doing the wide -> narrow conversion where it would not be
235 // necessary (avoids an allocation)
236 if (root.is_value()) return {};
237 if (auto tbl = root.as_table(); tbl && tbl->empty()) return {};
238 if (auto arr = root.as_array(); arr && arr->empty()) return {};
239
240 return at_path(root, impl::narrow(path));
241 }
242
244 node_view<const node> TOML_CALLCONV at_path(const node& root, std::wstring_view path) {
245 return node_view<const node>{at_path(const_cast<node&>(root), path).node()};
246 }
247
248#endif // TOML_ENABLE_WINDOWS_COMPAT
249}
251
252#include "header_end.hpp"
TOML_NODISCARD TOML_EXPORTED_FREE_FUNCTION node_view< const node > TOML_CALLCONV at_path(const node &root, std::string_view path) noexcept
Returns a const view of the node matching a fully-qualified "TOML path".
TOML_NAMESPACE_START
Definition at_path.inl:187
TOML_ENABLE_WARNINGS
Definition at_path.inl:23
TOML_DISABLE_WARNINGS
Definition at_path.inl:17
TOML_IMPL_NAMESPACE_START
Definition at_path.inl:26
A TOML array.
Definition array.hpp:285
TOML_PURE_INLINE_GETTER node * get(size_t index) noexcept
Gets a pointer to the element at a specific index.
Definition array.hpp:685
A TOML path.
Definition path.hpp:239
TOML_PURE_INLINE_GETTER bool empty() const noexcept
Whether (true) or not (false) the path is empty.
Definition path.hpp:302
A TOML table.
Definition table.hpp:220
TOML_PURE_GETTER TOML_EXPORTED_MEMBER_FUNCTION node * get(std::string_view key) noexcept
Gets the node at a specific key.
#define TOML_CALLCONV
Calling convention to apply to exported free/static functions. \detail Not defined by default (let th...
Definition preprocessor.hpp:1134
#define TOML_ASSERT(expr)
Sets the assert function used by the library. \detail Defaults to the standard C assert().
Definition preprocessor.hpp:1185
#define TOML_ASSERT_ASSUME(expr)
Definition preprocessor.hpp:1190
#define TOML_UNLIKELY(...)
Definition preprocessor.hpp:538
#define TOML_LIKELY(...)
Definition preprocessor.hpp:525
#define TOML_EXTERNAL_LINKAGE
Definition preprocessor.hpp:1339
#define TOML_NAMESPACE_END
Definition preprocessor.hpp:1320
#define TOML_IMPL_NAMESPACE_END
Definition preprocessor.hpp:1334