Mercurial > irccd
annotate common/ini.h @ 60:223487a685b1 release-2.0
Added tag 2.0.0 for changeset 92b0be5ce4b0
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 01 Mar 2016 08:53:05 +0100 |
parents | 03068f5ed79d |
children |
rev | line source |
---|---|
0 | 1 /* |
2 * ini.h -- .ini file parsing | |
3 * | |
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #ifndef _INI_H_ | |
20 #define _INI_H_ | |
21 | |
22 /** | |
23 * @file Ini.h | |
24 * @brief Configuration file parser. | |
25 */ | |
26 | |
27 #include <algorithm> | |
28 #include <exception> | |
29 #include <stdexcept> | |
30 #include <string> | |
31 #include <vector> | |
32 | |
33 namespace irccd { | |
34 | |
35 /** | |
36 * Namespace for ini related classes. | |
37 */ | |
38 namespace ini { | |
39 | |
40 class Document; | |
41 | |
42 /** | |
43 * @class Error | |
44 * @brief Error in a file | |
45 */ | |
46 class Error : public std::exception { | |
47 private: | |
48 int m_line; //!< line number | |
49 int m_column; //!< line column | |
50 std::string m_message; //!< error message | |
51 | |
52 public: | |
53 /** | |
54 * Constructor. | |
55 * | |
56 * @param l the line | |
57 * @param c the column | |
58 * @param m the message | |
59 */ | |
60 inline Error(int l, int c, std::string m) noexcept | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
61 : m_line(l) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
62 , m_column(c) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
63 , m_message(std::move(m)) |
0 | 64 { |
65 } | |
66 | |
67 /** | |
68 * Get the line number. | |
69 * | |
70 * @return the line | |
71 */ | |
72 inline int line() const noexcept | |
73 { | |
74 return m_line; | |
75 } | |
76 | |
77 /** | |
78 * Get the column number. | |
79 * | |
80 * @return the column | |
81 */ | |
82 inline int column() const noexcept | |
83 { | |
84 return m_column; | |
85 } | |
86 | |
87 /** | |
88 * Return the raw error message (no line and column shown). | |
89 * | |
90 * @return the error message | |
91 */ | |
92 const char *what() const noexcept override | |
93 { | |
94 return m_message.c_str(); | |
95 } | |
96 }; | |
97 | |
98 /** | |
99 * @class Token | |
100 * @brief Describe a token read in the .ini source | |
101 * | |
102 * This class can be used when you want to parse a .ini file yourself. | |
103 * | |
104 * @see Document::analyze | |
105 */ | |
106 class Token { | |
107 public: | |
108 /** | |
109 * @brief Token type | |
110 */ | |
111 enum Type { | |
112 Include, //!< include statement | |
113 Section, //!< [section] | |
114 Word, //!< word without quotes | |
115 QuotedWord, //!< word with quotes | |
116 Assign, //!< = assignment | |
117 ListBegin, //!< begin of list ( | |
118 ListEnd, //!< end of list ) | |
119 Comma //!< list separation | |
120 }; | |
121 | |
122 private: | |
123 Type m_type; | |
124 int m_line; | |
125 int m_column; | |
126 std::string m_value; | |
127 | |
128 public: | |
129 /** | |
130 * Construct a token. | |
131 * | |
132 * @param type the type | |
133 * @param line the line | |
134 * @param column the column | |
135 * @param value the value | |
136 */ | |
137 Token(Type type, int line, int column, std::string value = "") noexcept | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
138 : m_type(type) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
139 , m_line(line) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
140 , m_column(column) |
0 | 141 { |
142 switch (type) { | |
143 case Include: | |
144 m_value = "@include"; | |
145 break; | |
146 case Section: | |
147 case Word: | |
148 case QuotedWord: | |
149 m_value = value; | |
150 break; | |
151 case Assign: | |
152 m_value = "="; | |
153 break; | |
154 case ListBegin: | |
155 m_value = "("; | |
156 break; | |
157 case ListEnd: | |
158 m_value = ")"; | |
159 break; | |
160 case Comma: | |
161 m_value = ","; | |
162 break; | |
163 default: | |
164 break; | |
165 } | |
166 } | |
167 | |
168 /** | |
169 * Get the type. | |
170 * | |
171 * @return the type | |
172 */ | |
173 inline Type type() const noexcept | |
174 { | |
175 return m_type; | |
176 } | |
177 | |
178 /** | |
179 * Get the line. | |
180 * | |
181 * @return the line | |
182 */ | |
183 inline int line() const noexcept | |
184 { | |
185 return m_line; | |
186 } | |
187 | |
188 /** | |
189 * Get the column. | |
190 * | |
191 * @return the column | |
192 */ | |
193 inline int column() const noexcept | |
194 { | |
195 return m_column; | |
196 } | |
197 | |
198 /** | |
199 * Get the value. For words, quoted words and section, the value is the content. Otherwise it's the | |
200 * characters parsed. | |
201 * | |
202 * @return the value | |
203 */ | |
204 inline const std::string &value() const noexcept | |
205 { | |
206 return m_value; | |
207 } | |
208 }; | |
209 | |
210 /** | |
211 * List of tokens in order they are analyzed. | |
212 */ | |
213 using Tokens = std::vector<Token>; | |
214 | |
215 /** | |
216 * @class Option | |
217 * @brief Option definition. | |
218 */ | |
219 class Option : public std::vector<std::string> { | |
220 private: | |
221 std::string m_key; | |
222 | |
223 public: | |
224 /** | |
225 * Construct an empty option. | |
226 * | |
227 * @param key the key | |
228 * @param value the value | |
229 */ | |
230 inline Option(std::string key) noexcept | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
231 : std::vector<std::string>() |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
232 , m_key(std::move(key)) |
0 | 233 { |
234 } | |
235 | |
236 /** | |
237 * Construct a single option. | |
238 * | |
239 * @param key the key | |
240 * @param value the value | |
241 */ | |
242 inline Option(std::string key, std::string value) noexcept | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
243 : m_key(std::move(key)) |
0 | 244 { |
245 push_back(std::move(value)); | |
246 } | |
247 | |
248 /** | |
249 * Construct a list option. | |
250 * | |
251 * @param key the key | |
252 * @param values the values | |
253 */ | |
254 inline Option(std::string key, std::vector<std::string> values) noexcept | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
255 : std::vector<std::string>(std::move(values)) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
256 , m_key(std::move(key)) |
0 | 257 { |
258 } | |
259 | |
260 /** | |
261 * Get the option key. | |
262 * | |
263 * @return the key | |
264 */ | |
265 inline const std::string &key() const noexcept | |
266 { | |
267 return m_key; | |
268 } | |
269 | |
270 /** | |
271 * Get the option value. | |
272 * | |
273 * @return the value | |
274 */ | |
275 inline const std::string &value() const noexcept | |
276 { | |
277 static std::string dummy; | |
278 | |
279 return empty() ? dummy : (*this)[0]; | |
280 } | |
281 }; | |
282 | |
283 /** | |
284 * @class Section | |
285 * @brief Section that contains one or more options. | |
286 */ | |
287 class Section : public std::vector<Option> { | |
288 private: | |
289 std::string m_key; | |
290 | |
291 public: | |
292 /** | |
293 * Construct a section with its name. | |
294 * | |
295 * @param key the key | |
296 */ | |
297 inline Section(std::string key) noexcept | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
298 : m_key(std::move(key)) |
0 | 299 { |
300 } | |
301 | |
302 /** | |
303 * Get the section key. | |
304 * | |
305 * @return the key | |
306 */ | |
307 inline const std::string &key() const noexcept | |
308 { | |
309 return m_key; | |
310 } | |
311 | |
312 /** | |
313 * Check if the section contains a specific option. | |
314 * | |
315 * @param key the option key | |
316 * @return true if the option exists | |
317 */ | |
318 inline bool contains(const std::string &key) const noexcept | |
319 { | |
320 return find(key) != end(); | |
321 } | |
322 | |
323 /** | |
324 * Access an option at the specified key. | |
325 * | |
326 * @param key the key | |
327 * @return the option | |
328 * @throw std::out_of_range if the key does not exist | |
329 */ | |
330 inline Option &operator[](const std::string &key) | |
331 { | |
332 return *find(key); | |
333 } | |
334 | |
335 /** | |
336 * Access an option at the specified key. | |
337 * | |
338 * @param key the key | |
339 * @return the option | |
340 * @throw std::out_of_range if the key does not exist | |
341 */ | |
342 inline const Option &operator[](const std::string &key) const | |
343 { | |
344 return *find(key); | |
345 } | |
346 | |
347 /** | |
348 * Find an option by key and return an iterator. | |
349 * | |
350 * @param key the key | |
351 * @return the iterator or end() if not found | |
352 */ | |
353 inline iterator find(const std::string &key) noexcept | |
354 { | |
355 return std::find_if(begin(), end(), [&] (const auto &o) { | |
356 return o.key() == key; | |
357 }); | |
358 } | |
359 | |
360 /** | |
361 * Find an option by key and return an iterator. | |
362 * | |
363 * @param key the key | |
364 * @return the iterator or end() if not found | |
365 */ | |
366 inline const_iterator find(const std::string &key) const noexcept | |
367 { | |
368 return std::find_if(cbegin(), cend(), [&] (const auto &o) { | |
369 return o.key() == key; | |
370 }); | |
371 } | |
372 | |
373 /** | |
374 * Inherited operators. | |
375 */ | |
376 using std::vector<Option>::operator[]; | |
377 }; | |
378 | |
379 /** | |
380 * @class File | |
381 * @brief Source for reading .ini files. | |
382 */ | |
383 class File { | |
384 public: | |
385 /** | |
386 * Path to the file. | |
387 */ | |
388 std::string path; | |
389 }; | |
390 | |
391 /** | |
392 * @class Buffer | |
393 * @brief Source for reading ini from text. | |
394 * @note the include statement is not supported with buffers. | |
395 */ | |
396 class Buffer { | |
397 public: | |
398 /** | |
399 * The ini content. | |
400 */ | |
401 std::string text; | |
402 }; | |
403 | |
404 /** | |
405 * @class Document | |
406 * @brief Ini config file loader | |
407 */ | |
408 class Document : public std::vector<Section> { | |
409 private: | |
410 std::string m_path; | |
411 | |
412 public: | |
413 /** | |
414 * Analyze a file and extract tokens. If the function succeeds, that does not mean the content is valid, | |
415 * it just means that there are no syntax error. | |
416 * | |
417 * For example, this class does not allow adding options under no sections and this function will not | |
418 * detect that issue. | |
419 * | |
420 * @param file the file to read | |
421 * @return the list of tokens | |
422 * @throws Error on errors | |
423 */ | |
424 static Tokens analyze(const File &file); | |
425 | |
426 /** | |
427 * Overloaded function for buffers. | |
428 * | |
429 * @param buffer the buffer to read | |
430 * @return the list of tokens | |
431 * @throws Error on errors | |
432 */ | |
433 static Tokens analyze(const Buffer &buffer); | |
434 | |
435 /** | |
436 * Show all tokens and their description. | |
437 * | |
438 * @param tokens the tokens | |
439 */ | |
440 static void dump(const Tokens &tokens); | |
441 | |
442 /** | |
443 * Construct a document from a file. | |
444 * | |
445 * @param file the file to read | |
446 * @throws Error on errors | |
447 */ | |
448 Document(const File &file); | |
449 | |
450 /** | |
451 * Overloaded constructor for buffers. | |
452 * | |
453 * @param buffer the buffer to read | |
454 * @throws Error on errors | |
455 */ | |
456 Document(const Buffer &buffer); | |
457 | |
458 /** | |
459 * Get the current document path, only useful when constructed from File source. | |
460 * | |
461 * @return the path | |
462 */ | |
463 inline const std::string &path() const noexcept | |
464 { | |
465 return m_path; | |
466 } | |
467 | |
468 /** | |
469 * Check if a document has a specific section. | |
470 * | |
471 * @param key the key | |
472 */ | |
473 inline bool contains(const std::string &key) const noexcept | |
474 { | |
475 return std::find_if(begin(), end(), [&] (const auto &sc) { return sc.key() == key; }) != end(); | |
476 } | |
477 | |
478 /** | |
479 * Access a section at the specified key. | |
480 * | |
481 * @param key the key | |
482 * @return the section | |
483 * @throw std::out_of_range if the key does not exist | |
484 */ | |
485 inline Section &operator[](const std::string &key) | |
486 { | |
487 return *find(key); | |
488 } | |
489 | |
490 /** | |
491 * Access a section at the specified key. | |
492 * | |
493 * @param key the key | |
494 * @return the section | |
495 * @throw std::out_of_range if the key does not exist | |
496 */ | |
497 inline const Section &operator[](const std::string &key) const | |
498 { | |
499 return *find(key); | |
500 } | |
501 | |
502 /** | |
503 * Find a section by key and return an iterator. | |
504 * | |
505 * @param key the key | |
506 * @return the iterator or end() if not found | |
507 */ | |
508 inline iterator find(const std::string &key) noexcept | |
509 { | |
510 return std::find_if(begin(), end(), [&] (const auto &o) { | |
511 return o.key() == key; | |
512 }); | |
513 } | |
514 | |
515 /** | |
516 * Find a section by key and return an iterator. | |
517 * | |
518 * @param key the key | |
519 * @return the iterator or end() if not found | |
520 */ | |
521 inline const_iterator find(const std::string &key) const noexcept | |
522 { | |
523 return std::find_if(cbegin(), cend(), [&] (const auto &o) { | |
524 return o.key() == key; | |
525 }); | |
526 } | |
527 | |
528 /** | |
529 * Inherited operators. | |
530 */ | |
531 using std::vector<Section>::operator[]; | |
532 }; | |
533 | |
534 } // !ini | |
535 | |
536 } // !irccd | |
537 | |
538 #endif // !_INI_H_ |