Browse Source

Add key value file parser

- add KVParser class to parse a .kv file
- add tests for KVParser class
Patrick-Christopher Mattulat 3 years ago
parent
commit
6f3f4e3ec1

+ 4 - 2
CMakeLists.txt

@@ -94,7 +94,8 @@ set(SOURCE_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/event/EventManager.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/serialization/json/event/SerializableJSONEvent.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/kv/KVPair.cpp
-        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/kv/KVDocument.cpp)
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/kv/KVDocument.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/kv/KVParser.cpp)
 
 if (${LS_STD_BUILD_WITH_TESTS})
     set(TEST_FILES
@@ -151,7 +152,8 @@ if (${LS_STD_BUILD_WITH_TESTS})
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/event/EventManagerTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/serialization/json/event/SerializableJSONEventTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/kv/KVPairTest.cpp
-            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/kv/KVDocumentTest.cpp)
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/kv/KVDocumentTest.cpp
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/kv/KVParserTest.cpp)
 endif ()
 
 ##########################################################

+ 23 - 0
include/ls_std/io/kv/KVParseData.hpp

@@ -0,0 +1,23 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#ifndef LS_STD_KV_PARSE_DATA_HPP
+#define LS_STD_KV_PARSE_DATA_HPP
+
+#include <string>
+#include <ls_std/boxing/String.hpp>
+
+namespace ls_std {
+  struct KVParseData {
+    std::string::size_type index {};
+    ls_std::String line {};
+  };
+}
+
+#endif

+ 44 - 0
include/ls_std/io/kv/KVParser.hpp

@@ -0,0 +1,44 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#ifndef LS_STD_KV_PARSER_HPP
+#define LS_STD_KV_PARSER_HPP
+
+#include <ls_std/base/Class.hpp>
+#include <ls_std/base/Types.hpp>
+#include "KVDocument.hpp"
+#include "KVParseData.hpp"
+#include <memory>
+
+namespace ls_std {
+  class KVParser : public ls_std::Class {
+    public:
+
+      explicit KVParser(const std::shared_ptr<ls_std::KVDocument>& _document);
+      ~KVParser() override = default;
+
+      std::shared_ptr<ls_std::KVDocument> getDocument();
+      void parse(const ls_std::byte_field& _data);
+      void setDocument(const std::shared_ptr<ls_std::KVDocument>& _document);
+
+    private:
+
+      std::shared_ptr<ls_std::KVDocument> document {};
+
+      void _assignDocument(const std::shared_ptr<ls_std::KVDocument>& _document);
+      static bool _lineHasPair(ls_std::KVParseData _parseData);
+      void _parse(const ls_std::byte_field& _data);
+      void _parsePair(ls_std::KVParseData _parseData);
+      static ls_std::KVParseData _readLine(const ls_std::byte_field& _data, std::string::size_type _index);
+      static void _readLineWithUnixLineBreak(ls_std::KVParseData& _parseData);
+      static void _readLineWithWindowsLineBreak(ls_std::KVParseData& _parseData);
+  };
+}
+
+#endif

+ 1 - 0
include/ls_std/ls_std.hpp

@@ -55,6 +55,7 @@
 #include "io/StorableFile.hpp"
 #include "io/kv/KVPair.hpp"
 #include "io/kv/KVDocument.hpp"
+#include "io/kv/KVParser.hpp"
 
 #include "logic/State.hpp"
 #include "logic/StateConnection.hpp"

+ 96 - 0
source/ls_std/io/kv/KVParser.cpp

@@ -0,0 +1,96 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#include <ls_std/io/kv/KVParser.hpp>
+#include <ls_std/exception/NullPointerException.hpp>
+#include <ls_std/io/NewLine.hpp>
+
+ls_std::KVParser::KVParser(const std::shared_ptr<ls_std::KVDocument> &_document) : ls_std::Class("KVParser")
+{
+  this->_assignDocument(_document);
+}
+
+std::shared_ptr<ls_std::KVDocument> ls_std::KVParser::getDocument()
+{
+  return this->document;
+}
+
+void ls_std::KVParser::parse(const ls_std::byte_field &_data)
+{
+  this->_parse(_data);
+}
+
+void ls_std::KVParser::setDocument(const std::shared_ptr<ls_std::KVDocument> &_document)
+{
+  this->_assignDocument(_document);
+}
+
+void ls_std::KVParser::_assignDocument(const std::shared_ptr<ls_std::KVDocument> &_document)
+{
+  if(_document == nullptr) {
+    throw ls_std::NullPointerException {};
+  }
+
+  this->document = _document;
+}
+
+bool ls_std::KVParser::_lineHasPair(ls_std::KVParseData _parseData)
+{
+  return _parseData.line.contains("=") && _parseData.line.contains(";");
+}
+
+void ls_std::KVParser::_parse(const ls_std::byte_field &_data)
+{
+  for(std::string::size_type index = 0 ; index < _data.size() ; index++) {
+    ls_std::KVParseData parseData = ls_std::KVParser::_readLine(_data, index);
+    this->_parsePair(parseData);
+    index = parseData.index;
+  }
+}
+
+void ls_std::KVParser::_parsePair(ls_std::KVParseData _parseData)
+{
+  if(ls_std::KVParser::_lineHasPair(_parseData)) {
+    size_t equalSignPosition = _parseData.line.toString().find('=');
+    ls_std::kv_key key = _parseData.line.toString().substr(0, equalSignPosition);
+    ls_std::kv_value value = _parseData.line.toString().substr(equalSignPosition + 1);
+    value = value.substr(0, value.find(';'));
+
+    this->document->addPair(ls_std::KVPair {key, value});
+  }
+}
+
+ls_std::KVParseData ls_std::KVParser::_readLine(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  ls_std::KVParseData parseData {};
+  parseData.line = _data.substr(_index);
+
+  if(parseData.line.contains(ls_std::NewLine::getWindowsNewLine())) {
+    ls_std::KVParser::_readLineWithWindowsLineBreak(parseData);
+  } else {
+    if(parseData.line.contains(ls_std::NewLine::getUnixNewLine())) {
+      ls_std::KVParser::_readLineWithUnixLineBreak(parseData);
+    }
+  }
+
+  parseData.index = _index + parseData.line.toString().size();
+  return parseData;
+}
+
+void ls_std::KVParser::_readLineWithUnixLineBreak(ls_std::KVParseData& _parseData)
+{
+  size_t newLinePosition = _parseData.line.toString().find(ls_std::NewLine::getUnixNewLine());
+  _parseData.line = _parseData.line.toString().substr(0, newLinePosition);
+}
+
+void ls_std::KVParser::_readLineWithWindowsLineBreak(ls_std::KVParseData &_parseData)
+{
+  size_t newLinePosition = _parseData.line.toString().find(ls_std::NewLine::getWindowsNewLine());
+  _parseData.line = _parseData.line.toString().substr(0, newLinePosition);
+}

+ 58 - 0
test/cases/io/kv/KVParserTest.cpp

@@ -0,0 +1,58 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include <ls_std/ls_std.hpp>
+
+namespace {
+  class KVParserTest : public ::testing::Test {
+    protected:
+
+      KVParserTest() = default;
+      ~KVParserTest() override = default;
+
+      void SetUp() override {}
+      void TearDown() override {}
+  };
+
+  TEST_F(KVParserTest, getDocument)
+  {
+    ls_std::KVParser parser {std::make_shared<ls_std::KVDocument>()};
+    ASSERT_TRUE(parser.getDocument() != nullptr);
+  }
+
+  TEST_F(KVParserTest, parse)
+  {
+    std::shared_ptr<ls_std::KVDocument> document = std::make_shared<ls_std::KVDocument>();
+    ls_std::KVParser parser {document};
+    ls_std::byte_field data = "# starting comment\n\nport=8080; # some comment\nhost=localhost;\nservice-name=deamon;";
+    parser.parse(data);
+
+    ASSERT_EQ(3, document->getPairs().size());
+    ASSERT_TRUE(document->hasPair("port"));
+    ASSERT_TRUE(document->hasPair("host"));
+    ASSERT_TRUE(document->hasPair("service-name"));
+
+    ASSERT_STREQ("8080", document->getPairs().at("port").getValue().c_str());
+    ASSERT_STREQ("localhost", document->getPairs().at("host").getValue().c_str());
+    ASSERT_STREQ("deamon", document->getPairs().at("service-name").getValue().c_str());
+  }
+
+  TEST_F(KVParserTest, setDocument)
+  {
+    std::shared_ptr<ls_std::KVDocument> document1 = std::make_shared<ls_std::KVDocument>();
+    std::shared_ptr<ls_std::KVDocument> document2 = std::make_shared<ls_std::KVDocument>();
+
+    ls_std::KVParser parser {document1};
+    ASSERT_TRUE(parser.getDocument() == document1);
+
+    parser.setDocument(document2);
+    ASSERT_TRUE(parser.getDocument() == document2);
+  }
+}