SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
KV1Writer.h
Go to the documentation of this file.
1#pragma once
2
3#include "KV1.h"
4
5#include <sourcepp/FS.h>
6
7namespace kvpp {
8
9template<typename S = std::string>
10requires std::convertible_to<S, std::string>
12public:
14 [[nodiscard]] std::string_view getKey() const {
15 return this->key;
16 }
17
19 void setKey(std::string_view key_) {
20 this->key = key_;
21 }
22
24 [[nodiscard]] std::string_view getValue() const {
25 return this->value;
26 }
27
29 template<KV1ValueType V>
30 [[nodiscard]] V getValue() const {
31 if constexpr (std::convertible_to<V, std::string_view>) {
32 return this->value;
33 } else if constexpr (std::same_as<V, bool>) {
34 return static_cast<bool>(this->getValue<int32_t>());
35 } else if constexpr (std::same_as<V, int32_t> || std::same_as<V, int64_t>) {
36 V out = 0;
37 if (this->value.length() == 10 && this->value.starts_with("0x") && sourcepp::parser::text::isNumber(this->value.substr(2))) {
38 sourcepp::string::toInt(this->value.substr(2), out, 16);
39 } else {
41 }
42 return out;
43 } else if constexpr (std::same_as<V, float>) {
44 float out = 0.f;
46 return out;
47 }
48 return V{};
49 }
50
52 template<KV1ValueType V>
53 void setValue(V value_) {
54 if constexpr (std::convertible_to<V, std::string_view>) {
55 this->value = std::string_view{value_};
56 } else {
57 this->setValue(std::to_string(value_));
58 }
59 }
60
62 template<KV1ValueType V>
64 this->setValue(value_);
65 return *this;
66 }
67
69 [[nodiscard]] std::string_view getConditional() const {
70 return this->conditional;
71 }
72
74 void setConditional(std::string_view conditional_) {
75 this->conditional = conditional_;
76 }
77
79 [[nodiscard]] bool hasChild(std::string_view childKey) const {
80 return !this->operator[](childKey).isInvalid();
81 }
82
83 // Add a new child element with the given key, with an optional value and/or conditional
84 template<KV1ValueType V = std::string_view>
85 KV1ElementWritable& addChild(std::string_view key_, V value_ = {}, std::string_view conditional_ = "") {
87 elem.setKey(key_);
88 elem.setValue(value_);
89 elem.setConditional(conditional_);
90 this->children.push_back(elem);
91 return this->children.back();
92 }
93
95 [[nodiscard]] uint64_t getChildCount() const {
96 return this->children.size();
97 }
98
100 [[nodiscard]] uint64_t getChildCount(std::string_view childKey) const {
101 uint64_t count = 0;
102 for (const auto& element : this->children) {
103 if (sourcepp::string::iequals(element.key, childKey)) {
104 ++count;
105 }
106 }
107 return count;
108 }
109
111 [[nodiscard]] const std::vector<KV1ElementWritable>& getChildren() const {
112 return this->children;
113 }
114
116 [[nodiscard]] std::vector<KV1ElementWritable>& getChildren() {
117 return this->children;
118 }
119
120 using iterator = std::vector<KV1ElementWritable>::iterator;
121
122 [[nodiscard]] constexpr iterator begin() {
123 return this->children.begin();
124 }
125
126 [[nodiscard]] constexpr iterator end() {
127 return this->children.end();
128 }
129
130 using const_iterator = std::vector<KV1ElementWritable>::const_iterator;
131
132 [[nodiscard]] constexpr const_iterator begin() const {
133 return this->children.begin();
134 }
135
136 [[nodiscard]] constexpr const_iterator end() const {
137 return this->children.end();
138 }
139
140 [[nodiscard]] constexpr const_iterator cbegin() const {
141 return this->children.cbegin();
142 }
143
144 [[nodiscard]] constexpr const_iterator cend() const {
145 return this->children.cend();
146 }
147
149 [[nodiscard]] const KV1ElementWritable& operator[](unsigned int n) const {
150 return this->children.at(n);
151 }
152
154 [[nodiscard]] KV1ElementWritable& operator[](unsigned int n) {
155 return this->children.at(n);
156 }
157
159 [[nodiscard]] const KV1ElementWritable& operator[](std::string_view childKey) const {
160 return this->operator()(childKey);
161 }
162
164 [[nodiscard]] KV1ElementWritable& operator[](std::string_view childKey) {
165 return this->operator()(childKey);
166 }
167
169 [[nodiscard]] const KV1ElementWritable& operator()(std::string_view childKey) const {
170 for (const auto& element : this->children) {
171 if (sourcepp::string::iequals(element.getKey(), childKey)) {
172 return element;
173 }
174 }
175 return getInvalid();
176 }
177
179 [[nodiscard]] KV1ElementWritable& operator()(std::string_view childKey) {
180 for (auto& element : this->children) {
181 if (sourcepp::string::iequals(element.getKey(), childKey)) {
182 return element;
183 }
184 }
185 return this->addChild(childKey);
186 }
187
189 [[nodiscard]] const KV1ElementWritable& operator()(std::string_view childKey, unsigned int n) const {
190 unsigned int count = 0;
191 for (const auto& element : this->children) {
192 if (sourcepp::string::iequals(element.getKey(), childKey)) {
193 if (count == n) {
194 return element;
195 }
196 if (++count > n) {
197 break;
198 }
199 }
200 }
201 return getInvalid();
202 }
203
205 [[nodiscard]] KV1ElementWritable& operator()(std::string_view childKey, unsigned int n) {
206 unsigned int count = 0;
207 for (auto& element: this->children) {
208 if (sourcepp::string::iequals(element.getKey(), childKey)) {
209 if (count == n) {
210 return element;
211 }
212 if (++count > n) {
213 break;
214 }
215 }
216 }
217 return this->addChild(childKey);
218 }
219
221 void removeChild(unsigned int n) {
222 if (this->children.size() > n) {
223 this->children.erase(this->children.begin() + n);
224 }
225 }
226
228 void removeChild(std::string_view childKey, int n = -1) {
229 unsigned int count = 0;
230 for (auto element = this->children.begin(); element != this->children.end(); ++element) {
231 if (sourcepp::string::iequals(element->getKey(), childKey)) {
232 if (n < 0 || count == n) {
233 element = this->children.erase(element);
234 if (count == n) {
235 break;
236 }
237 }
238 ++count;
239 }
240 }
241 }
242
244 [[nodiscard]] bool isInvalid() const {
245 return this == &getInvalid();
246 }
247
249 static KV1ElementWritable element;
250 return element;
251 }
252
253 [[nodiscard]] explicit operator bool() const {
254 return !this->isInvalid();
255 }
256
257protected:
259
260 static void read(BufferStreamReadOnly& stream, std::vector<KV1ElementWritable>& elements, const sourcepp::parser::text::EscapeSequenceMap& escapeSequences) {
261 using namespace sourcepp;
262 while (true) {
263 // Check if the block is over
265 if (stream.peek<char>() == '}') {
266 stream.skip();
267 break;
268 }
269 // Read key
270 {
271 elements.push_back(KV1ElementWritable{});
274 }
275 // Read value
276 if (stream.peek<char>() != '{') {
279 }
280 // Read conditional
281 if (stream.peek<char>() == '[') {
282 elements.back().conditional = parser::text::readString(stream, "[", "]", escapeSequences);
284 }
285 // Read block
286 if (stream.peek<char>() == '{') {
287 stream.skip();
289 if (stream.peek<char>() != '}') {
290 KV1ElementWritable::read(stream, elements.back().children, escapeSequences);
291 } else {
292 stream.skip();
293 }
294 }
295 }
296 }
297
298 static void write(BufferStream& stream, const std::vector<KV1ElementWritable>& elements, unsigned short indentLevel, const sourcepp::parser::text::EscapeSequenceMap& escapeSequences) {
299 using namespace sourcepp;
300 constexpr auto writeIndentation = [](BufferStream& stream_, unsigned short indentLevel_) {
301 for (unsigned short i = 0; i < indentLevel_; i++) {
302 stream_.write('\t');
303 }
304 };
305 constexpr auto writeQuotedString = [](BufferStream& stream_, std::string_view str, const parser::text::EscapeSequenceMap& escapeSequences_, char quoteStart = '\"', char quoteEnd = '\"') {
306 stream_.write(quoteStart);
307 if (!str.empty()) {
308 stream_.write(parser::text::convertSpecialCharsToEscapes(str, escapeSequences_), false);
309 }
310 stream_.write(quoteEnd);
311 };
312
313 for (const auto& e : elements) {
314 writeIndentation(stream, indentLevel);
315 writeQuotedString(stream, e.key, escapeSequences);
316 if (!e.value.empty() || e.children.empty()) {
317 stream.write(' ');
318 writeQuotedString(stream, e.value, escapeSequences);
319 }
320 if (!e.conditional.empty()) {
321 stream.write(' ');
322 writeQuotedString(stream, e.conditional, escapeSequences, '[', ']');
323 }
324 stream.write('\n');
325 if (!e.children.empty()) {
326 writeIndentation(stream, indentLevel);
327 stream << '{' << '\n';
328 write(stream, e.children, indentLevel + 1, escapeSequences);
329 writeIndentation(stream, indentLevel);
330 stream << '}' << '\n';
331 }
332 }
333 }
334
335 S key = ""; // NOLINT(*-redundant-string-init)
336 S value = ""; // NOLINT(*-redundant-string-init)
337 S conditional = ""; // NOLINT(*-redundant-string-init)
338 std::vector<KV1ElementWritable> children;
339};
340
341template<typename S = std::string>
342requires std::convertible_to<S, std::string_view>
343class KV1Writer : public KV1ElementWritable<S> {
344public:
345 explicit KV1Writer(std::string_view kv1Data = "", bool useEscapeSequences_ = false)
346 : KV1ElementWritable<S>()
347 , useEscapeSequences(useEscapeSequences_) {
348 if (kv1Data.empty()) {
349 return;
350 }
351 BufferStreamReadOnly stream{kv1Data};
352 try {
353 KV1ElementWritable<S>::read(stream, this->children, sourcepp::parser::text::getDefaultEscapeSequencesOrNone(this->useEscapeSequences));
354 } catch (const std::overflow_error&) {}
355 }
356
357 [[nodiscard]] std::string bake() const {
358 std::string buffer;
359 BufferStream stream{buffer};
361 buffer.resize(stream.size());
362 return buffer;
363 }
364
365 void bake(const std::filesystem::path& kv1Path) const {
366 sourcepp::fs::writeFileText(kv1Path, this->bake());
367 }
368
369protected:
374 using KV1ElementWritable<S>::operator=;
377
379};
380
381namespace literals {
382
383inline KV1Writer<> operator""_kv1w(const char* str, const std::size_t len) {
384 return KV1Writer{std::string_view{str, len}};
385}
386
387} // namespace literals
388
389} // namespace kvpp
std::vector< KV1ElementWritable > & getChildren()
Get the child elements of the element.
Definition KV1Writer.h:116
constexpr const_iterator end() const
Definition KV1Writer.h:136
void setKey(std::string_view key_)
Set the key associated with the element.
Definition KV1Writer.h:19
std::string_view getConditional() const
Get the conditional associated with the element.
Definition KV1Writer.h:69
bool isInvalid() const
Check if the given element is invalid.
Definition KV1Writer.h:244
constexpr const_iterator begin() const
Definition KV1Writer.h:132
const std::vector< KV1ElementWritable > & getChildren() const
Get the child elements of the element.
Definition KV1Writer.h:111
void removeChild(std::string_view childKey, int n=-1)
Remove a child element from the element with the given key. -1 means all children with the given key.
Definition KV1Writer.h:228
const KV1ElementWritable & operator()(std::string_view childKey) const
Get the first child element of the element with the given key.
Definition KV1Writer.h:169
void removeChild(unsigned int n)
Remove a child element from the element.
Definition KV1Writer.h:221
static void write(BufferStream &stream, const std::vector< KV1ElementWritable > &elements, unsigned short indentLevel, const sourcepp::parser::text::EscapeSequenceMap &escapeSequences)
Definition KV1Writer.h:298
constexpr iterator begin()
Definition KV1Writer.h:122
KV1ElementWritable & operator=(V value_)
Set the value associated with the element.
Definition KV1Writer.h:63
void setConditional(std::string_view conditional_)
Set the conditional associated with the element.
Definition KV1Writer.h:74
KV1ElementWritable & operator()(std::string_view childKey, unsigned int n)
Get the nth child element of the element with the given key, or create a new element if it doesn't ex...
Definition KV1Writer.h:205
std::vector< KV1ElementWritable > children
Definition KV1Writer.h:338
KV1ElementWritable & addChild(std::string_view key_, V value_={}, std::string_view conditional_="")
Definition KV1Writer.h:85
void setValue(V value_)
Set the value associated with the element.
Definition KV1Writer.h:53
constexpr const_iterator cend() const
Definition KV1Writer.h:144
std::vector< KV1ElementWritable >::iterator iterator
Definition KV1Writer.h:120
KV1ElementWritable & operator[](unsigned int n)
Get the child element of the element at the given index.
Definition KV1Writer.h:154
KV1ElementWritable & operator()(std::string_view childKey)
Get the first child element of the element with the given key, or create a new element if it doesn't ...
Definition KV1Writer.h:179
std::vector< KV1ElementWritable >::const_iterator const_iterator
Definition KV1Writer.h:130
const KV1ElementWritable & operator[](unsigned int n) const
Get the child element of the element at the given index.
Definition KV1Writer.h:149
uint64_t getChildCount(std::string_view childKey) const
Get the number of child elements with the given key.
Definition KV1Writer.h:100
static void read(BufferStreamReadOnly &stream, std::vector< KV1ElementWritable > &elements, const sourcepp::parser::text::EscapeSequenceMap &escapeSequences)
Definition KV1Writer.h:260
std::string_view getValue() const
Get the value associated with the element.
Definition KV1Writer.h:24
bool hasChild(std::string_view childKey) const
Check if the element has one or more children with the given key.
Definition KV1Writer.h:79
KV1ElementWritable & operator[](std::string_view childKey)
Get the first child element of the element with the given key, or create a new element if it doesn't ...
Definition KV1Writer.h:164
constexpr iterator end()
Definition KV1Writer.h:126
const KV1ElementWritable & operator()(std::string_view childKey, unsigned int n) const
Get the nth child element of the element with the given key.
Definition KV1Writer.h:189
constexpr const_iterator cbegin() const
Definition KV1Writer.h:140
static const KV1ElementWritable & getInvalid()
Definition KV1Writer.h:248
uint64_t getChildCount() const
Get the number of child elements.
Definition KV1Writer.h:95
std::string_view getKey() const
Get the key associated with the element.
Definition KV1Writer.h:14
const KV1ElementWritable & operator[](std::string_view childKey) const
Get the first child element of the element with the given key.
Definition KV1Writer.h:159
V getValue() const
Get the value associated with the element as the given type.
Definition KV1Writer.h:30
std::string bake() const
Definition KV1Writer.h:357
void bake(const std::filesystem::path &kv1Path) const
Definition KV1Writer.h:365
KV1Writer(std::string_view kv1Data="", bool useEscapeSequences_=false)
Definition KV1Writer.h:345
bool useEscapeSequences
Definition KV1Writer.h:378
Definition DMX.h:13
bool writeFileText(const std::filesystem::path &filepath, std::string_view text)
Definition FS.cpp:34
std::string convertSpecialCharsToEscapes(std::string_view str, const EscapeSequenceMap &escapeSequences)
Convert special characters like \n to escaped special characters like \\n.
Definition Text.cpp:56
std::unordered_map< char, char > EscapeSequenceMap
Definition Text.h:17
void eatWhitespaceAndSingleLineComments(BufferStream &stream, std::string_view singleLineCommentStart=DEFAULT_SINGLE_LINE_COMMENT_START)
Eat all whitespace and single line comments after the current stream position.
Definition Text.cpp:108
std::string readString(BufferStream &stream, std::string_view start=DEFAULT_STRING_START, std::string_view end=DEFAULT_STRING_END, const EscapeSequenceMap &escapeSequences=getDefaultEscapeSequences())
Read a string starting at the current stream position.
Definition Text.cpp:147
constexpr std::string_view DEFAULT_STRING_END
Definition Text.h:15
constexpr std::string_view DEFAULT_STRING_START
Definition Text.h:14
const EscapeSequenceMap & getDefaultEscapeSequencesOrNone(bool useEscapes)
Definition Text.cpp:27
bool isNumber(char c)
If a char is a numerical character (0-9).
Definition Text.cpp:48
std::from_chars_result toFloat(std::string_view number, std::floating_point auto &out)
Definition String.h:85
std::from_chars_result toInt(std::string_view number, std::integral auto &out, int base=10)
Definition String.h:81
bool iequals(std::string_view s1, std::string_view s2)
Definition String.cpp:61