LCOV - code coverage report
Current view: top level - src/plugins/yamlsmith - yamlsmith.cpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 72 75 96.0 %
Date: 2019-09-12 12:28:41 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Source for yamlsmith plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : // -- Imports ------------------------------------------------------------------------------------------------------------------------------
      11             : 
      12             : #include <fstream>
      13             : #include <iostream>
      14             : 
      15             : #include "yamlsmith.hpp"
      16             : 
      17             : #include <kdb.hpp>
      18             : #include <kdbease.h>
      19             : #include <kdberrors.h>
      20             : 
      21             : using std::endl;
      22             : using std::ofstream;
      23             : using std::string;
      24             : 
      25             : using ckdb::Key;
      26             : using ckdb::KeySet;
      27             : 
      28             : using ckdb::keyNew;
      29             : 
      30             : using CppKey = kdb::Key;
      31             : using CppKeySet = kdb::KeySet;
      32             : using NameIterator = kdb::NameIterator;
      33             : 
      34             : // -- Functions ----------------------------------------------------------------------------------------------------------------------------
      35             : 
      36             : namespace
      37             : {
      38             : 
      39             : /**
      40             :  * @brief This function returns a key set containing the contract of the plugin.
      41             :  *
      42             :  * @return A contract describing the functionality of this plugin
      43             :  */
      44         108 : CppKeySet contractYamlsmith ()
      45             : {
      46             :         return CppKeySet{ 30,
      47             :                           keyNew ("system/elektra/modules/yamlsmith", KEY_VALUE, "yamlsmith plugin waits for your orders", KEY_END),
      48             :                           keyNew ("system/elektra/modules/yamlsmith/exports", KEY_END),
      49             :                           keyNew ("system/elektra/modules/yamlsmith/exports/get", KEY_FUNC, elektraYamlsmithGet, KEY_END),
      50             :                           keyNew ("system/elektra/modules/yamlsmith/exports/set", KEY_FUNC, elektraYamlsmithSet, KEY_END),
      51             : #include ELEKTRA_README
      52             :                           keyNew ("system/elektra/modules/yamlsmith/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END),
      53             :                           keyNew ("system/elektra/modules/yamlcpp/config/needs/boolean/restore", KEY_VALUE, "#1", KEY_END),
      54         108 :                           KS_END };
      55             : }
      56             : 
      57             : /**
      58             :  * @brief This function collects leaf keys (keys without any key below) for a given key set.
      59             :  *
      60             :  * @param keys This parameter stores the key set for which this function retrieves all leaf keys.
      61             :  *
      62             :  * @return A key set containing only leaf keys
      63             :  */
      64          20 : CppKeySet leaves (CppKeySet const & keys)
      65             : {
      66          20 :         CppKeySet leaves;
      67             : 
      68          20 :         auto current = keys.begin ();
      69          40 :         if (current == keys.end ()) return leaves;
      70             : 
      71           0 :         CppKey previous = *current;
      72         348 :         while (++current != keys.end ())
      73             :         {
      74         216 :                 bool isLeaf = !current->isBelow (previous);
      75          72 :                 if (isLeaf)
      76             :                 {
      77             :                         leaves.append (previous);
      78             :                 }
      79             : 
      80         216 :                 previous = *current;
      81             :         }
      82             :         // The last key is always a leaf
      83          20 :         leaves.append (previous);
      84             : 
      85             :         return leaves;
      86             : }
      87             : 
      88             : /**
      89             :  * @brief This function counts the levels of a certain key name.
      90             :  *
      91             :  * @param key This parameter stores the key for which this function retrieves the number of parts.
      92             :  *
      93             :  * @return The number of parts of `key`
      94             :  */
      95          20 : size_t countKeyLevels (CppKey const & key)
      96             : {
      97             :         auto keyIterator = key.begin ();
      98             :         size_t levels = 0;
      99             : 
     100         226 :         while (keyIterator != key.end ())
     101             :         {
     102         124 :                 keyIterator++;
     103          62 :                 levels++;
     104             :         }
     105          20 :         return levels;
     106             : }
     107             : 
     108             : /**
     109             :  * @brief This function writes a YAML collection entry (either mapping key or array element) to the given stream.
     110             :  *
     111             :  * @pre The parameter `output` must be a valid and open output stream.
     112             :  *
     113             :  * @param output This parameter specifies where this function should put the serialized YAML data.
     114             :  * @param key The function uses the basename of this key to decide if the entry is an array element or a mapping key.
     115             :  * @param indent This string specifies the indentation for the collection entry.
     116             :  */
     117          82 : inline void writeCollectionEntry (ofstream & output, CppKey const & key, string const & indent)
     118             : {
     119         164 :         output << indent;
     120          82 :         if (elektraArrayValidateName (*key) == 1)
     121             :         {
     122          30 :                 output << "-" << endl;
     123             :         }
     124             :         else
     125             :         {
     126         335 :                 output << key.getBaseName () << ":" << endl;
     127             :         }
     128          82 : }
     129             : 
     130             : /**
     131             :  * @brief This function returns a name iterator for a key that starts after `levelsToSkip` levels.
     132             :  *
     133             :  * @param key This parameter stores the key for which this function returns a name iterator.
     134             :  * @param levelsToSkip This value stores the number of parts of `key` that the returned iterator should skip.
     135             :  *
     136             :  * @return A name iterator for `key`, that skips the first `levelsToSkip` parts of `key`
     137             :  */
     138         136 : inline NameIterator getIteratorSkippedLevels (CppKey const & key, size_t levelsToSkip)
     139             : {
     140         136 :         auto iterator = key.begin ();
     141         548 :         for (auto levels = levelsToSkip; levels > 0; levels--)
     142             :         {
     143         824 :                 iterator++;
     144             :         }
     145             : 
     146         136 :         return iterator;
     147             : }
     148             : 
     149             : /**
     150             :  * @brief This function writes a representation of a key value to the given output stream.
     151             :  *
     152             :  * @param output This parameter specifies where this function should emit the serialized YAML data.
     153             :  * @param key This parameter stores the key which stores the value this function should emit to `output`.
     154             :  */
     155          68 : void writeYAMLScalar (ofstream & output, CppKey const & key)
     156             : {
     157          70 :         if (!key.isString ()) return;
     158             : 
     159         133 :         string value = key.getString ();
     160             : 
     161          67 :         if (value == "0")
     162             :         {
     163           1 :                 output << "false";
     164             :                 return;
     165             :         }
     166             : 
     167          66 :         if (value == "1")
     168             :         {
     169           0 :                 output << "true";
     170             :                 return;
     171             :         }
     172             : 
     173         264 :         output << '"' << value << '"';
     174             : }
     175             : 
     176             : /**
     177             :  * @brief This function converts a `KeySet` into the YAML serialization format.
     178             :  *
     179             :  * @pre The parameter `output` must be a valid and open output stream.
     180             :  *
     181             :  * @param output This parameter specifies where this function should emit the serialized YAML data.
     182             :  * @param keys This parameter stores the key set which this function converts to YAML data.
     183             :  * @param parent This value represents the root key of `keys`.
     184             :  */
     185          20 : void writeYAML (ofstream & output, CppKeySet && keys, CppKey const & parent)
     186             : {
     187          20 :         auto levelsParent = countKeyLevels (parent);
     188             : 
     189             :         ELEKTRA_LOG_DEBUG ("Convert %zu key%s", keys.size (), keys.size () == 1 ? "" : "s");
     190          20 :         keys.rewind ();
     191         488 :         for (CppKey last = parent; keys.next (); last = keys.current ())
     192             :         {
     193             :                 ELEKTRA_LOG_DEBUG ("Convert key ā€œ%s: %sā€", keys.current ().getName ().c_str (), keys.current ().getString ().c_str ());
     194             : 
     195             :                 // Skip common prefix (parent key name) for all keys in key set
     196          68 :                 auto relativeLast = getIteratorSkippedLevels (last, levelsParent);
     197         136 :                 auto relative = getIteratorSkippedLevels (keys.current (), levelsParent);
     198             : 
     199             :                 // Add indentation for each part of the key that was already added to the file
     200          68 :                 string indent;
     201         863 :                 while (relativeLast != last.end () && relative != keys.current ().end () && *relative == *relativeLast)
     202             :                 {
     203          50 :                         relative++;
     204          50 :                         relativeLast++;
     205             :                         indent += "  ";
     206             :                 }
     207             : 
     208             :                 // Add YAML mapping key for each part of the key we did not already write into the file
     209         204 :                 auto endCurrent = keys.current ().end ();
     210         136 :                 CppKey current{ "user", KEY_END };
     211             : 
     212         150 :                 while (relative != endCurrent)
     213             :                 {
     214         164 :                         current.addBaseName (*relative);
     215             :                         ELEKTRA_LOG_DEBUG ("Current name: %s", current.getName ().c_str ());
     216         246 :                         writeCollectionEntry (output, *current, indent);
     217         164 :                         relative++;
     218             :                         indent += "  ";
     219             :                 }
     220             : 
     221         136 :                 output << indent;
     222         136 :                 writeYAMLScalar (output, keys.current ());
     223         136 :                 output << endl;
     224             :         }
     225          20 : }
     226             : 
     227             : } // end namespace
     228             : 
     229             : extern "C" {
     230             : // ====================
     231             : // = Plugin Interface =
     232             : // ====================
     233             : 
     234             : /** @see elektraDocGet */
     235         108 : int elektraYamlsmithGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
     236             : {
     237         216 :         CppKey parent{ parentKey };
     238         216 :         CppKeySet keys{ returned };
     239             : 
     240         324 :         if (parent.getName () == "system/elektra/modules/yamlsmith")
     241             :         {
     242         324 :                 keys.append (contractYamlsmith ());
     243         108 :                 parent.release ();
     244             :                 keys.release ();
     245             : 
     246             :                 return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     247             :         }
     248             : 
     249             :         parent.release ();
     250             : 
     251             :         return ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
     252             : }
     253             : 
     254             : /** @see elektraDocSet */
     255          20 : int elektraYamlsmithSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
     256             : {
     257          40 :         CppKey parent{ parentKey };
     258          40 :         CppKeySet keys{ returned };
     259             : 
     260          60 :         ofstream file{ parent.getString () };
     261          20 :         if (file.is_open ())
     262             :         {
     263          40 :                 writeYAML (file, leaves (keys), parent);
     264             :         }
     265             :         else
     266             :         {
     267           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (parent.getKey (), "Unable to open file '%s'", parent.getString ().c_str ());
     268             :         }
     269             : 
     270          20 :         parent.release ();
     271          20 :         keys.release ();
     272             : 
     273          20 :         return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     274             : }
     275             : 
     276         520 : Plugin * ELEKTRA_PLUGIN_EXPORT
     277             : {
     278             :         return elektraPluginExport ("yamlsmith", ELEKTRA_PLUGIN_GET, &elektraYamlsmithGet, ELEKTRA_PLUGIN_SET, &elektraYamlsmithSet,
     279         520 :                                     ELEKTRA_PLUGIN_END);
     280             : }
     281             : 
     282         274 : } // end extern "C"

Generated by: LCOV version 1.13