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 <format>
4
5#include "KV1.h"
6
7#include <sourcepp/FS.h>
8
9namespace kvpp {
10
11template<typename S = std::string>
12requires std::convertible_to<S, std::string>
14public:
16 [[nodiscard]] std::string_view getKey() const {
17 return this->key;
18 }
19
21 void setKey(std::string_view key_) {
22 this->key = key_;
23 }
24
26 [[nodiscard]] std::string_view getValue() const {
27 return this->value;
28 }
29
31 template<KV1ValueType V>
32 [[nodiscard]] V getValue() const {
33 if constexpr (std::convertible_to<V, std::string_view>) {
34 return this->value;
35 } else if constexpr (std::same_as<V, bool>) {
36 return static_cast<bool>(this->getValue<int32_t>());
37 } else if constexpr (std::same_as<V, int32_t> || std::same_as<V, int64_t>) {
38 V out = 0;
39 if (this->value.length() == 10 && this->value.starts_with("0x") && sourcepp::parser::text::isNumber(this->value.substr(2))) {
40 sourcepp::string::toInt(this->value.substr(2), out, 16);
41 } else {
43 }
44 return out;
45 } else if constexpr (std::same_as<V, float>) {
46 float out = 0.f;
48 return out;
49 }
50 return V{};
51 }
52
54 template<KV1ValueType V>
55 void setValue(V value_) {
56 if constexpr (std::convertible_to<V, std::string_view>) {
57 this->value = std::string_view{value_};
58 } else if constexpr (std::same_as<V, bool>) {
59 this->setValue(std::format("{:b}", value_));
60 } else {
61 this->setValue(std::format("{}", value_));
62 }
63 }
64
66 template<KV1ValueType V>
68 this->setValue(value_);
69 return *this;
70 }
71
73 [[nodiscard]] std::string_view getConditional() const {
74 return this->conditional;
75 }
76
78 void setConditional(std::string_view conditional_) {
79 this->conditional = conditional_;
80 }
81
83 [[nodiscard]] bool hasChild(std::string_view childKey) const {
84 return !this->operator[](childKey).isInvalid();
85 }
86
87 // Add a new child element with the given key, with an optional value and/or conditional
88 template<KV1ValueType V = std::string_view>
89 KV1ElementWritable& addChild(std::string_view key_, V value_ = {}, std::string_view conditional_ = "") {
91 elem.setKey(key_);
92 elem.setValue(value_);
93 elem.setConditional(conditional_);
94 this->children.push_back(elem);
95 return this->children.back();
96 }
97
99 [[nodiscard]] uint64_t getChildCount() const {
100 return this->children.size();
101 }
102
104 [[nodiscard]] uint64_t getChildCount(std::string_view childKey) const {
105 uint64_t count = 0;
106 for (const auto& element : this->children) {
107 if (sourcepp::string::iequals(element.key, childKey)) {
108 ++count;
109 }
110 }
111 return count;
112 }
113
115 [[nodiscard]] const std::vector<KV1ElementWritable>& getChildren() const {
116 return this->children;
117 }
118
120 [[nodiscard]] std::vector<KV1ElementWritable>& getChildren() {
121 return this->children;
122 }
123
124 using iterator = std::vector<KV1ElementWritable>::iterator;
125
126 [[nodiscard]] constexpr iterator begin() {
127 return this->children.begin();
128 }
129
130 [[nodiscard]] constexpr iterator end() {
131 return this->children.end();
132 }
133
134 using const_iterator = std::vector<KV1ElementWritable>::const_iterator;
135
136 [[nodiscard]] constexpr const_iterator begin() const {
137 return this->children.begin();
138 }
139
140 [[nodiscard]] constexpr const_iterator end() const {
141 return this->children.end();
142 }
143
144 [[nodiscard]] constexpr const_iterator cbegin() const {
145 return this->children.cbegin();
146 }
147
148 [[nodiscard]] constexpr const_iterator cend() const {
149 return this->children.cend();
150 }
151
153 [[nodiscard]] const KV1ElementWritable& operator[](unsigned int n) const {
154 return this->children.at(n);
155 }
156
158 [[nodiscard]] KV1ElementWritable& operator[](unsigned int n) {
159 return this->children.at(n);
160 }
161
163 [[nodiscard]] const KV1ElementWritable& operator[](std::string_view childKey) const {
164 return this->operator()(childKey);
165 }
166
168 [[nodiscard]] KV1ElementWritable& operator[](std::string_view childKey) {
169 return this->operator()(childKey);
170 }
171
173 [[nodiscard]] const KV1ElementWritable& operator()(std::string_view childKey) const {
174 for (const auto& element : this->children) {
175 if (sourcepp::string::iequals(element.getKey(), childKey)) {
176 return element;
177 }
178 }
179 return getInvalid();
180 }
181
183 [[nodiscard]] KV1ElementWritable& operator()(std::string_view childKey) {
184 for (auto& element : this->children) {
185 if (sourcepp::string::iequals(element.getKey(), childKey)) {
186 return element;
187 }
188 }
189 return this->addChild(childKey);
190 }
191
193 [[nodiscard]] const KV1ElementWritable& operator()(std::string_view childKey, unsigned int n) const {
194 unsigned int count = 0;
195 for (const auto& element : this->children) {
196 if (sourcepp::string::iequals(element.getKey(), childKey)) {
197 if (count == n) {
198 return element;
199 }
200 if (++count > n) {
201 break;
202 }
203 }
204 }
205 return getInvalid();
206 }
207
209 [[nodiscard]] KV1ElementWritable& operator()(std::string_view childKey, unsigned int n) {
210 unsigned int count = 0;
211 for (auto& element: this->children) {
212 if (sourcepp::string::iequals(element.getKey(), childKey)) {
213 if (count == n) {
214 return element;
215 }
216 if (++count > n) {
217 break;
218 }
219 }
220 }
221 return this->addChild(childKey);
222 }
223
225 void removeChild(unsigned int n) {
226 if (this->children.size() > n) {
227 this->children.erase(this->children.begin() + n);
228 }
229 }
230
232 void removeChild(std::string_view childKey, int n = -1) {
233 unsigned int count = 0;
234 for (auto element = this->children.begin(); element != this->children.end(); ++element) {
235 if (sourcepp::string::iequals(element->getKey(), childKey)) {
236 if (n < 0 || count == n) {
237 element = this->children.erase(element);
238 if (count == n) {
239 break;
240 }
241 }
242 ++count;
243 }
244 }
245 }
246
248 [[nodiscard]] bool isInvalid() const {
249 return this == &getInvalid();
250 }
251
253 static KV1ElementWritable element;
254 return element;
255 }
256
257 [[nodiscard]] explicit operator bool() const {
258 return !this->isInvalid();
259 }
260
261protected:
263
264 static void read(BufferStreamReadOnly& stream, std::vector<KV1ElementWritable>& elements, const sourcepp::parser::text::EscapeSequenceMap& escapeSequences) {
265 using namespace sourcepp;
266 while (true) {
267 // Check if the block is over
269 if (stream.peek<char>() == '}') {
270 stream.skip();
271 break;
272 }
273 // Read key
274 {
275 elements.push_back(KV1ElementWritable{});
278 }
279 // Read value
280 if (stream.peek<char>() != '{') {
283 }
284 // Read conditional
285 if (stream.peek<char>() == '[') {
286 elements.back().conditional = parser::text::readString(stream, "[", "]", escapeSequences);
288 }
289 // Read block
290 if (stream.peek<char>() == '{') {
291 stream.skip();
293 if (stream.peek<char>() != '}') {
294 KV1ElementWritable::read(stream, elements.back().children, escapeSequences);
295 } else {
296 stream.skip();
297 }
298 }
299 }
300 }
301
302 static void write(BufferStream& stream, const std::vector<KV1ElementWritable>& elements, unsigned short indentLevel, const sourcepp::parser::text::EscapeSequenceMap& escapeSequences) {
303 using namespace sourcepp;
304 constexpr auto writeIndentation = [](BufferStream& stream_, unsigned short indentLevel_) {
305 for (unsigned short i = 0; i < indentLevel_; i++) {
306 stream_.write('\t');
307 }
308 };
309 constexpr auto writeQuotedString = [](BufferStream& stream_, std::string_view str, const parser::text::EscapeSequenceMap& escapeSequences_, char quoteStart = '\"', char quoteEnd = '\"') {
310 stream_.write(quoteStart);
311 if (!str.empty()) {
312 stream_.write(parser::text::convertSpecialCharsToEscapes(str, escapeSequences_), false);
313 }
314 stream_.write(quoteEnd);
315 };
316
317 for (const auto& e : elements) {
318 writeIndentation(stream, indentLevel);
319 writeQuotedString(stream, e.key, escapeSequences);
320 if (!e.value.empty() || e.children.empty()) {
321 stream.write(' ');
322 writeQuotedString(stream, e.value, escapeSequences);
323 }
324 if (!e.conditional.empty()) {
325 stream.write(' ');
326 writeQuotedString(stream, e.conditional, escapeSequences, '[', ']');
327 }
328 stream.write('\n');
329 if (!e.children.empty()) {
330 writeIndentation(stream, indentLevel);
331 stream << '{' << '\n';
332 write(stream, e.children, indentLevel + 1, escapeSequences);
333 writeIndentation(stream, indentLevel);
334 stream << '}' << '\n';
335 }
336 }
337 }
338
339 S key = ""; // NOLINT(*-redundant-string-init)
340 S value = ""; // NOLINT(*-redundant-string-init)
341 S conditional = ""; // NOLINT(*-redundant-string-init)
342 std::vector<KV1ElementWritable> children;
343};
344
345template<typename S = std::string>
346requires std::convertible_to<S, std::string_view>
347class KV1Writer : public KV1ElementWritable<S> {
348public:
349 explicit KV1Writer(std::string_view kv1Data = "", bool useEscapeSequences_ = false)
350 : KV1ElementWritable<S>()
351 , useEscapeSequences(useEscapeSequences_) {
352 if (kv1Data.empty()) {
353 return;
354 }
355 BufferStreamReadOnly stream{kv1Data};
356 try {
357 KV1ElementWritable<S>::read(stream, this->children, sourcepp::parser::text::getDefaultEscapeSequencesOrNone(this->useEscapeSequences));
358 } catch (const std::overflow_error&) {}
359 }
360
361 [[nodiscard]] std::string bake() const {
362 std::string buffer;
363 BufferStream stream{buffer};
365 buffer.resize(stream.size());
366 return buffer;
367 }
368
369 void bake(const std::filesystem::path& kv1Path) const {
370 sourcepp::fs::writeFileText(kv1Path, this->bake());
371 }
372
373protected:
378 using KV1ElementWritable<S>::operator=;
381
383};
384
385namespace literals {
386
387inline KV1Writer<> operator""_kv1w(const char* str, const std::size_t len) {
388 return KV1Writer{std::string_view{str, len}};
389}
390
391} // namespace literals
392
393} // namespace kvpp
std::vector< KV1ElementWritable > & getChildren()
Get the child elements of the element.
Definition KV1Writer.h:120
constexpr const_iterator end() const
Definition KV1Writer.h:140
void setKey(std::string_view key_)
Set the key associated with the element.
Definition KV1Writer.h:21
std::string_view getConditional() const
Get the conditional associated with the element.
Definition KV1Writer.h:73
bool isInvalid() const
Check if the given element is invalid.
Definition KV1Writer.h:248
constexpr const_iterator begin() const
Definition KV1Writer.h:136
const std::vector< KV1ElementWritable > & getChildren() const
Get the child elements of the element.
Definition KV1Writer.h:115
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:232
const KV1ElementWritable & operator()(std::string_view childKey) const
Get the first child element of the element with the given key.
Definition KV1Writer.h:173
void removeChild(unsigned int n)
Remove a child element from the element.
Definition KV1Writer.h:225
static void write(BufferStream &stream, const std::vector< KV1ElementWritable > &elements, unsigned short indentLevel, const sourcepp::parser::text::EscapeSequenceMap &escapeSequences)
Definition KV1Writer.h:302
constexpr iterator begin()
Definition KV1Writer.h:126
KV1ElementWritable & operator=(V value_)
Set the value associated with the element.
Definition KV1Writer.h:67
void setConditional(std::string_view conditional_)
Set the conditional associated with the element.
Definition KV1Writer.h:78
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:209
std::vector< KV1ElementWritable > children
Definition KV1Writer.h:342
KV1ElementWritable & addChild(std::string_view key_, V value_={}, std::string_view conditional_="")
Definition KV1Writer.h:89
void setValue(V value_)
Set the value associated with the element.
Definition KV1Writer.h:55
constexpr const_iterator cend() const
Definition KV1Writer.h:148
std::vector< KV1ElementWritable >::iterator iterator
Definition KV1Writer.h:124
KV1ElementWritable & operator[](unsigned int n)
Get the child element of the element at the given index.
Definition KV1Writer.h:158
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:183
std::vector< KV1ElementWritable >::const_iterator const_iterator
Definition KV1Writer.h:134
const KV1ElementWritable & operator[](unsigned int n) const
Get the child element of the element at the given index.
Definition KV1Writer.h:153
uint64_t getChildCount(std::string_view childKey) const
Get the number of child elements with the given key.
Definition KV1Writer.h:104
static void read(BufferStreamReadOnly &stream, std::vector< KV1ElementWritable > &elements, const sourcepp::parser::text::EscapeSequenceMap &escapeSequences)
Definition KV1Writer.h:264
std::string_view getValue() const
Get the value associated with the element.
Definition KV1Writer.h:26
bool hasChild(std::string_view childKey) const
Check if the element has one or more children with the given key.
Definition KV1Writer.h:83
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:168
constexpr iterator end()
Definition KV1Writer.h:130
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:193
constexpr const_iterator cbegin() const
Definition KV1Writer.h:144
static const KV1ElementWritable & getInvalid()
Definition KV1Writer.h:252
uint64_t getChildCount() const
Get the number of child elements.
Definition KV1Writer.h:99
std::string_view getKey() const
Get the key associated with the element.
Definition KV1Writer.h:16
const KV1ElementWritable & operator[](std::string_view childKey) const
Get the first child element of the element with the given key.
Definition KV1Writer.h:163
V getValue() const
Get the value associated with the element as the given type.
Definition KV1Writer.h:32
std::string bake() const
Definition KV1Writer.h:361
void bake(const std::filesystem::path &kv1Path) const
Definition KV1Writer.h:369
KV1Writer(std::string_view kv1Data="", bool useEscapeSequences_=false)
Definition KV1Writer.h:349
bool useEscapeSequences
Definition KV1Writer.h:382
Definition DMX.h:13
bool writeFileText(const std::filesystem::path &filepath, std::string_view text)
Definition FS.cpp:46
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:62