LCOV - code coverage report
Current view: top level - src/tools/kdb/gen/highlevel - enums.cpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 112 118 94.9 %
Date: 2019-09-12 12:28:41 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #include "enums.hpp"
      10             : #include "common.hpp"
      11             : #include <command.hpp>
      12             : #include <kdbease.h>
      13             : #include <kdbhelper.h>
      14             : 
      15          10 : kainjow::mustache::list EnumProcessor::getValues (const std::string & prefix, const kdb::Key & key, std::string & fromStringSwitch,
      16             :                                                   std::string & valuesString, size_t & trieDepth)
      17             : {
      18             :         using namespace kainjow::mustache;
      19             : 
      20          40 :         if (!key.hasMeta ("check/enum"))
      21             :         {
      22             :                 return {};
      23             :         }
      24             : 
      25          20 :         list values;
      26          20 :         std::stringstream ss;
      27          20 :         std::set<std::pair<std::string, std::string>> stringValues;
      28             : 
      29          50 :         const auto end = key.getMeta<std::string> ("check/enum");
      30          10 :         if (ckdb::elektraArrayValidateBaseNameString (end.c_str ()) < 0)
      31             :         {
      32           0 :                 throw CommandAbortException ("The key '" + key.getName () + "' has invalid check/enum metadata: " + end);
      33             :         }
      34             : 
      35          10 :         kdb::long_long_t i = 0;
      36          30 :         std::string cur = "#0";
      37        1102 :         while (cur <= end)
      38             :         {
      39        1092 :                 if (key.hasMeta ("check/enum/" + cur))
      40             :                 {
      41          76 :                         auto name = prefix + "_";
      42         114 :                         const std::string & stringValue = key.getMeta<std::string> ("check/enum/" + cur);
      43         114 :                         name += camelCaseToMacroCase (stringValue);
      44          38 :                         escapeNonAlphaNum (name);
      45          76 :                         auto value = std::to_string (i);
      46         114 :                         if (key.hasMeta ("gen/enum/" + cur + "/value"))
      47             :                         {
      48          64 :                                 value = key.getMeta<std::string> ("gen/enum/" + cur + "/value");
      49             :                         }
      50         190 :                         values.emplace_back (object{ { "name", name }, { "value", value }, { "string_value", stringValue } });
      51          76 :                         stringValues.insert ({ stringValue, name });
      52         114 :                         ss << name << "=" << value << "\n";
      53             :                 }
      54         546 :                 ++i;
      55        1092 :                 auto indexString = std::to_string (i);
      56        4368 :                 cur = "#" + std::string (indexString.length () - 1, '_') + std::to_string (i);
      57             :         }
      58             : 
      59          20 :         EnumTrie trie (stringValues);
      60          10 :         trieDepth = trie.getDepth ();
      61          20 :         fromStringSwitch = trie.createSwitch ();
      62             : 
      63          20 :         valuesString = ss.str ();
      64             : 
      65          10 :         return values;
      66             : }
      67             : 
      68          10 : std::string EnumProcessor::getType (const kdb::Key & key, const std::string & tagName, bool & genType)
      69             : {
      70          40 :         genType = key.hasMeta ("gen/enum/type");
      71          22 :         return genType ? key.getMeta<std::string> ("gen/enum/type") : snakeCaseToPascalCase (tagName);
      72             : }
      73             : 
      74          10 : bool EnumProcessor::shouldGenerateTypeDef (const kdb::Key & key)
      75             : {
      76          48 :         return !key.hasMeta ("gen/enum/create") || key.getMeta<std::string> ("gen/enum/create") == "1";
      77             : }
      78             : 
      79          10 : kainjow::mustache::object EnumProcessor::process (const kdb::Key & key, const std::string & tagName)
      80             : {
      81             :         using namespace kainjow::mustache;
      82             : 
      83          20 :         auto name = key.getName ();
      84          10 :         name.erase (0, sizeof ("spec") - 1);
      85             : 
      86             :         bool genType;
      87          20 :         auto enumType = getType (key, tagName, genType);
      88          20 :         auto typeName = "Enum" + enumType;
      89             : 
      90          24 :         auto nativeType = genType ? enumType : "Elektra" + typeName;
      91          20 :         std::string fromStringSwitch;
      92             : 
      93          20 :         std::string valuesString;
      94             :         size_t trieDepth;
      95          30 :         auto values = getValues (camelCaseToMacroCase (nativeType), key, fromStringSwitch, valuesString, trieDepth);
      96             : 
      97          10 :         auto isNew = true;
      98          10 :         auto generateTypeDef = shouldGenerateTypeDef (key);
      99             : 
     100          20 :         auto other = enumTypes.find (typeName);
     101          20 :         if (other != enumTypes.end ())
     102             :         {
     103           6 :                 auto otherValuesString = other->second.second;
     104           2 :                 if (otherValuesString != valuesString)
     105             :                 {
     106           0 :                         auto otherKey = other->second.first;
     107           0 :                         auto msg = "The key '" + name;
     108           0 :                         msg += "' uses the same 'gen/enum/type' as the key '" + otherKey +
     109           0 :                                "', but their 'check/enum' values are different!";
     110           0 :                         throw CommandAbortException (msg);
     111             :                 }
     112             : 
     113           2 :                 isNew = false;
     114             :         }
     115             : 
     116          30 :         enumTypes[typeName] = std::make_pair (name, valuesString);
     117             : 
     118          10 :         auto useTrie = conversion == EnumConversion::Trie || (conversion == EnumConversion::Auto && trieDepth < 3);
     119             : 
     120             :         return object{ { "new", isNew },
     121             :                        { "name", name },
     122          20 :                        { "tag_name", snakeCaseToMacroCase (tagName) },
     123             :                        { "type_name", typeName },
     124             :                        { "native_type", nativeType },
     125             :                        { "generate_typedef?", generateTypeDef },
     126             :                        { "values", values },
     127             :                        { "switch_from_string?", useTrie },
     128          80 :                        { "from_string_code", fromStringSwitch } };
     129             : }
     130             : 
     131          40 : EnumTrie::EnumTrie (const std::set<std::pair<std::string, std::string>> & values)
     132             : {
     133          84 :         for (auto v = values.begin (); v != values.end ();)
     134             :         {
     135          64 :                 char c = v->first.at (0);
     136          64 :                 std::string p;
     137          32 :                 if (c != '\0')
     138             :                 {
     139          32 :                         p += c;
     140             :                 }
     141          32 :                 std::set<std::pair<std::string, std::string>> vals;
     142             : 
     143         260 :                 while (v != values.end () && v->first.at (0) == c)
     144             :                 {
     145          76 :                         vals.insert (*v);
     146             :                         ++v;
     147             :                 }
     148          32 :                 insert (p, vals);
     149             :         }
     150          10 : }
     151             : 
     152          48 : void EnumTrie::insert (const std::string & prefix, const std::set<std::pair<std::string, std::string>> & values)
     153             : {
     154         144 :         std::unique_ptr<EnumTrie> child (new EnumTrie ());
     155          48 :         if (values.size () == 1)
     156             :         {
     157         144 :                 child->stringValue = values.begin ()->first;
     158         108 :                 child->name = values.begin ()->second;
     159             :         }
     160             :         else
     161             :         {
     162          42 :                 for (auto v = values.begin (); v != values.end ();)
     163             :                 {
     164          18 :                         if (v->first.size () == prefix.size ())
     165             :                         {
     166           8 :                                 child->stringValue = values.begin ()->first;
     167           8 :                                 child->name = values.begin ()->second;
     168           2 :                                 ++v;
     169           2 :                                 continue;
     170             :                         }
     171             : 
     172          48 :                         char c = v->first.at (prefix.size ());
     173          32 :                         std::string p = prefix;
     174          32 :                         p += c;
     175          16 :                         std::set<std::pair<std::string, std::string>> vals;
     176             : 
     177         164 :                         while (v != values.end () && (v->first.size () > prefix.size () ? v->first.at (prefix.size ()) : '\0') == c)
     178             :                         {
     179          48 :                                 vals.insert (*v);
     180             :                                 ++v;
     181             :                         }
     182          16 :                         child->insert (p, vals);
     183             :                 }
     184             :         }
     185             : 
     186          96 :         children[prefix.back ()] = std::move (child);
     187          48 : }
     188             : 
     189          10 : std::string EnumTrie::createSwitch ()
     190             : {
     191          20 :         std::stringstream ss;
     192          10 :         createSwitch (ss, 0);
     193          20 :         return ss.str ();
     194             : }
     195             : 
     196          58 : bool EnumTrie::createSwitch (std::stringstream & ss, size_t index)
     197             : {
     198         116 :         if (children.empty ())
     199             :         {
     200          72 :                 if (stringValue.empty ())
     201             :                 {
     202             :                         return false;
     203             :                 }
     204             : 
     205         180 :                 ss << "*variable = " << name << ";" << std::endl;
     206         108 :                 ss << "return 1;" << std::endl;
     207          36 :                 return false;
     208             :         }
     209             : 
     210         110 :         ss << "switch (string[" << index << "])" << std::endl;
     211          66 :         ss << "{" << std::endl;
     212         114 :         for (auto & child : children)
     213             :         {
     214         240 :                 ss << "case '" << child.first << "':" << std::endl;
     215          96 :                 if (child.second->createSwitch (ss, index + 1))
     216             :                 {
     217          20 :                         ss << "break;" << std::endl;
     218             :                 }
     219             :         }
     220          66 :         ss << "}" << std::endl;
     221             : 
     222          44 :         if (!stringValue.empty ())
     223             :         {
     224          10 :                 ss << "*variable = " << name << ";" << std::endl;
     225           6 :                 ss << "return 1;" << std::endl;
     226           2 :                 return false;
     227             :         }
     228             :         return true;
     229             : }
     230             : 
     231          58 : size_t EnumTrie::getDepth ()
     232             : {
     233         116 :         if (children.empty ())
     234             :         {
     235             :                 return 1;
     236             :         }
     237             : 
     238          22 :         std::vector<size_t> childDepths;
     239             :         std::transform (children.begin (), children.end (), std::back_inserter (childDepths),
     240         162 :                         [](const std::pair<const char, std::unique_ptr<EnumTrie>> & p) { return p.second->getDepth (); });
     241             : 
     242          66 :         return *std::max_element (childDepths.begin (), childDepths.end ()) + 1;
     243        7164 : }

Generated by: LCOV version 1.13