Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Read key sets using yaml-cpp
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #include "read.hpp"
10 : #include "yaml-cpp/yaml.h"
11 :
12 : #include <kdb.hpp>
13 : #include <kdblogger.h>
14 : #include <kdbplugin.h>
15 :
16 : #include <sstream>
17 :
18 : using namespace std;
19 : using namespace kdb;
20 :
21 : namespace
22 : {
23 : /**
24 : * @brief This function converts a given number to an array base name.
25 : *
26 : * @param index This number specifies the index of the array entry.
27 : *
28 : * @return A string representing the given indices as Elektra array name.
29 : */
30 273 : string indexToArrayBaseName (uintmax_t const index)
31 : {
32 273 : size_t digits = 1;
33 :
34 284 : for (uintmax_t value = index; value > 9; digits++)
35 : {
36 11 : value /= 10;
37 : }
38 :
39 1911 : return "#" + string (digits - 1, '_') + to_string (index);
40 : }
41 :
42 : /**
43 : * @brief This function creates a new key from the given parameters.
44 : *
45 : * @param name This string specifies the postfix of the name of the key produced by this function.
46 : * @param parent This key specifies the prefix of the name of the key produced by this function.
47 : *
48 : * @returns The function returns a new key that combines the name of the parent key and `name`.
49 : */
50 396 : Key newKey (string const & name, Key const & parent)
51 : {
52 : ELEKTRA_LOG_DEBUG ("Add new key with base name “%s”", name.c_str ());
53 :
54 792 : Key key{ parent.getFullName (), KEY_BINARY, KEY_END };
55 396 : key.addBaseName (name);
56 :
57 396 : return key;
58 : }
59 :
60 : /**
61 : * @brief This function creates a new array key from the given parameters.
62 : *
63 : * @param arrayKey This argument specifies the key that represents the root of the array.
64 : * @param index This parameter specifies the index of the array key this function creates.
65 : *
66 : * @returns The function returns a new key that is part of the array represented by `arrayKey`.
67 : */
68 273 : Key newArrayKey (Key & arrayKey, uintmax_t const index)
69 : {
70 : ELEKTRA_LOG_DEBUG ("Add new array element to array parent “%s”", arrayKey.getName ().c_str ());
71 :
72 546 : Key newKey{ arrayKey.getName (), KEY_BINARY, KEY_END };
73 546 : newKey.addBaseName (indexToArrayBaseName (index));
74 1365 : arrayKey.setMeta ("array", newKey.getBaseName ());
75 :
76 273 : return newKey;
77 : }
78 :
79 : /**
80 : * @brief Add metadata saved in a YAML map to the specified key
81 : *
82 : * @param key This parameter saves the key to which this function should add the metadata stored in `node`.
83 : * @param node This YAML node stores a map containing metadata.
84 : */
85 36 : void addMetadata (Key & key, YAML::Node const & node)
86 : {
87 222 : for (auto & element : node)
88 : {
89 78 : auto metakey = element.first.as<string> ();
90 117 : auto metavalue = element.second.IsNull () ? "" : element.second.as<string> ();
91 : ELEKTRA_LOG_DEBUG ("Add metakey “%s: %s”", metakey.c_str (), metavalue.c_str ());
92 78 : key.setMeta (metakey, metavalue);
93 : }
94 36 : }
95 :
96 : /**
97 : * @brief Create a key containing a (possibly empty) value.
98 : *
99 : * @param node This YAML node stores the data that should be converted to a new `Key`.
100 : * @param name This text specifies the name of the key this function creates.
101 : *
102 : * @return A new key containing the data specified in `node`
103 : */
104 435 : Key createLeafKey (YAML::Node const & node, string const & name)
105 : {
106 870 : Key key{ name, KEY_BINARY, KEY_END };
107 435 : if (!node.IsNull ())
108 : {
109 692 : auto value = node.as<string> ();
110 686 : if (value == "true" || value == "false")
111 : {
112 : try
113 : {
114 8 : key.set<bool> (node.as<bool> ());
115 : }
116 0 : catch (YAML::BadConversion const &)
117 : {
118 0 : key.set<string> (value); // Save value as string, if `node` is a quoted scalar
119 : }
120 : }
121 : else
122 : {
123 676 : key.set<string> (value);
124 : }
125 : }
126 870 : if (node.Tag () == "tag:yaml.org,2002:binary")
127 : {
128 : ELEKTRA_LOG_DEBUG ("Set metadata type of key to binary");
129 20 : key.setMeta ("type", "binary");
130 : }
131 : ELEKTRA_LOG_DEBUG ("Add key “%s: %s”", key.getName ().c_str (),
132 : key.getBinarySize () == 0 ? "NULL" : key.isBinary () ? "binary value!" : key.get<string> ().c_str ());
133 435 : return key;
134 : }
135 :
136 : /**
137 : * @brief Convert the key value of a YAML meta node to a key
138 : *
139 : * @param node This YAML meta node stores the data this function stores in the returned key
140 : * @param parent This key stores the prefix for the key name
141 : *
142 : * @return A key representing the key value stored in `node`
143 : */
144 36 : Key convertMetaNodeToKey (YAML::Node const & node, Key & parent)
145 : {
146 160 : auto key = node[0].IsNull () ? Key{ parent.getFullName (), KEY_BINARY, KEY_END } :
147 156 : Key{ parent.getFullName (), KEY_VALUE, node[0].as<string> ().c_str (), KEY_END };
148 : ELEKTRA_LOG_DEBUG ("Add key “%s”: “%s”", key.getName ().c_str (),
149 : key.getBinarySize () == 0 ? "NULL" : key.isString () ? key.getString ().c_str () : "binary value!");
150 36 : return key;
151 : }
152 :
153 : /**
154 : * @brief Convert a YAML node to a key set
155 : *
156 : * @param node This YAML node stores the data that should be added to the keyset `mappings`
157 : * @param mappings The key set where the YAML data will be stored
158 : * @param parent This key stores the prefix for the key name
159 : */
160 812 : void convertNodeToKeySet (YAML::Node const & node, KeySet & mappings, Key & parent)
161 : {
162 1624 : if (node.Tag () == "!elektra/meta")
163 : {
164 72 : auto key = convertMetaNodeToKey (node, parent);
165 36 : mappings.append (key);
166 72 : addMetadata (key, node[1]);
167 : }
168 1206 : else if (node.IsScalar () || node.IsNull ())
169 : {
170 1305 : auto key = createLeafKey (node, parent.getFullName ());
171 435 : mappings.append (key);
172 : }
173 341 : else if (node.IsMap ())
174 : {
175 1796 : for (auto element : node)
176 : {
177 1188 : Key key = newKey (element.first.as<string> (), parent);
178 396 : convertNodeToKeySet (element.second, mappings, key);
179 : }
180 : }
181 90 : else if (node.IsSequence ())
182 : {
183 90 : uintmax_t index = 0;
184 90 : uintmax_t lastIndex = 0;
185 906 : for (auto element : node)
186 : {
187 273 : if (lastIndex == UINTMAX_MAX)
188 : {
189 0 : Key key = newArrayKey (parent, lastIndex);
190 0 : throw std::overflow_error ("Unable to add element after “" + key.getName () + "”" + "in array “" +
191 0 : parent.getName () + "”");
192 : }
193 546 : Key key = newArrayKey (parent, index);
194 273 : mappings.append (parent); // Update array metadata
195 273 : convertNodeToKeySet (element, mappings, key);
196 273 : lastIndex = index++;
197 : }
198 : }
199 812 : }
200 : } // end namespace
201 :
202 : /**
203 : * @brief Read a YAML file and add the resulting data to a given key set
204 : *
205 : * @param mappings The key set where the YAML data will be stored
206 : * @param parent This key stores the path to the YAML data file that should be read
207 : */
208 145 : void yamlcpp::yamlRead (KeySet & mappings, Key & parent)
209 : {
210 433 : YAML::Node config = YAML::LoadFile (parent.getString ());
211 :
212 : ELEKTRA_LOG_DEBUG ("Read file “%s”", parent.getString ().c_str ());
213 :
214 : #ifdef HAVE_LOGGER
215 : ostringstream data;
216 : data << config;
217 :
218 : ELEKTRA_LOG_DEBUG ("Read Data:");
219 : ELEKTRA_LOG_DEBUG ("——————————");
220 :
221 : istringstream stream (data.str ());
222 : for (string line; std::getline (stream, line);)
223 : {
224 : ELEKTRA_LOG_DEBUG ("%s", line.c_str ());
225 : }
226 :
227 : ELEKTRA_LOG_DEBUG ("——————————");
228 : #endif
229 :
230 143 : convertNodeToKeySet (config, mappings, parent);
231 : ELEKTRA_LOG_DEBUG ("Added %zd key%s", mappings.size (), mappings.size () == 1 ? "" : "s");
232 143 : }
|