diff --git a/src/util/basyx/util/CMakeLists.txt b/src/util/basyx/util/CMakeLists.txt index 79843d6..e459d46 100644 --- a/src/util/basyx/util/CMakeLists.txt +++ b/src/util/basyx/util/CMakeLists.txt @@ -7,6 +7,8 @@ SET(HEADER_FILES_UTIL ${CMAKE_CURRENT_SOURCE_DIR}/algorithm/string.hpp ${CMAKE_CURRENT_SOURCE_DIR}/container/container.hpp ${CMAKE_CURRENT_SOURCE_DIR}/optional/optional.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/constrained_string/constrained_string.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/constrained_string/regex_checker.hpp ${CMAKE_CURRENT_SOURCE_DIR}/string_view/basic_string_view.hpp ${CMAKE_CURRENT_SOURCE_DIR}/string_view/string_view.hpp ) diff --git a/src/util/basyx/util/constrained_string/constrained_string.hpp b/src/util/basyx/util/constrained_string/constrained_string.hpp new file mode 100644 index 0000000..0fe8cf2 --- /dev/null +++ b/src/util/basyx/util/constrained_string/constrained_string.hpp @@ -0,0 +1,145 @@ +#ifndef BASYX_UTIL_CONSTRAINED_STRING_H +#define BASYX_UTIL_CONSTRAINED_STRING_H + +#include + +namespace basyx { +namespace util { + +class NullConstraint +{ +public: + bool operator()(basyx::util::string_view sv) { return true; }; +}; + +template +class constrained_string +{ +public: + using constraint_t = ConstraintT; + +public: + using iterator = std::string::iterator; + using const_iterator = std::string::const_iterator; + +public: + static constexpr char fill_char = FillChar; + +private: + constraint_t constraint; + std::string content; + +public: + static constexpr std::size_t min = Min; + static constexpr std::size_t max = Max; + +public: + // Constructors + explicit constrained_string() noexcept {}; + constrained_string(const char* c_str) noexcept { this->assign(c_str); } + constrained_string(const std::string& string) noexcept { this->assign(string); }; + constrained_string(util::string_view sv) noexcept { this->assign(sv); } + + // Copy constructors + template + constrained_string(const constrained_string& rhs) noexcept { + this->assign(rhs.str()); + }; + + template + constrained_string& operator=(const constrained_string& rhs) noexcept { + this->assign(rhs.str()); + return *this; + }; + + // Move constructors + template + constrained_string(constrained_string && rhs) noexcept { + this->assign(std::move(rhs.content)); + }; + + template + constrained_string& operator=(constrained_string&& rhs) noexcept { + this->assign(std::move(rhs.content)); + return *this; + }; + + // Assignment operators + constrained_string& operator=(const char* c_str) noexcept { this->assign(c_str); return *this; } + constrained_string& operator=(const std::string& string) noexcept { this->assign(string); return *this; }; + constrained_string& operator=(util::string_view sv) noexcept { this->assign(sv); return *this; } + +public: + bool check_constraint(util::string_view sv) { + //if (sv.size() < Min || sv.size() > Max) + // return false; + + return constraint(sv); + }; + +private: + void assign_fill(util::string_view sv) { + this->content = sv.to_string(); + + if (sv.size() > Max) { + this->content.resize(max, fill_char); + } + else if (sv.size() < Min) { + this->content.resize(min, fill_char); + }; + }; + +public: + void assign(util::string_view sv) { + if (check_constraint(sv)) { + this->assign_fill(sv); + }; + }; + +public: + inline std::size_t size() const { return this->content.size(); }; + inline const char* data() const { return this->content.data(); }; + inline bool empty() const { return this->content.empty(); }; + +public: + inline const std::string& str() const { return this->content; }; + inline operator const std::string& () const { return this->str(); }; + +public: + inline iterator begin() noexcept { return this->content.begin(); }; + inline const_iterator begin() const noexcept { return this->content.cbegin(); }; + + inline iterator end() noexcept { return this->content.end(); }; + inline const_iterator cend() const noexcept { return this->content.end(); }; + +public: + inline iterator rbegin() noexcept { return this->content.rbegin(); }; + inline const_iterator crbegin() const noexcept { return this->content.crbegin() }; + + inline iterator rend() noexcept { return this->content.rend(); }; + inline const_iterator crend() const noexcept { return this->content.crend(); }; +}; + + +// Comparison operators +template +inline bool operator==(const constrained_string& lhs, util::string_view sv) { + return lhs.str().compare(0, sv.size(), sv.data()) == 0; +}; + +template +inline bool operator!=(const constrained_string& rhs, util::string_view sv) { + return !(rhs == sv); +}; + +// Stream operator +template +std::ostream& operator <<(std::ostream& os, const constrained_string& constr_str) { + os << constr_str.str(); + return os; +} + +} +} + +#endif \ No newline at end of file diff --git a/src/util/basyx/util/constrained_string/regex_checker.hpp b/src/util/basyx/util/constrained_string/regex_checker.hpp new file mode 100644 index 0000000..8fbf16b --- /dev/null +++ b/src/util/basyx/util/constrained_string/regex_checker.hpp @@ -0,0 +1,25 @@ +#ifndef BASYX_UTIL_CONSTRAINED_STRING_REGEX_CHECKER_H +#define BASYX_UTIL_CONSTRAINED_STRING_REGEX_CHECKER_H + +#include + +namespace basyx { +namespace util { + +template +class RegExChecker +{ +public: + bool check(basyx::util::string_view sv) { + std::cmatch matches; + + return std::regex_match(sv.begin(), sv.end(), matches, Holder::regex); + }; + + bool operator()(basyx::util::string_view sv) { return check(sv); }; +}; + +} +} + +#endif \ No newline at end of file diff --git a/tests/tests_libaas/test_constrained_string.cpp b/tests/tests_libaas/test_constrained_string.cpp new file mode 100644 index 0000000..2a14c80 --- /dev/null +++ b/tests/tests_libaas/test_constrained_string.cpp @@ -0,0 +1,144 @@ +#include + +#include +#include + +#include + +using namespace basyx; + +class TestRegEx +{ +public: + static std::regex regex; +}; + +std::regex TestRegEx::regex("^([0-9]|[1-9][0-9]*)$"); + +class ConstrainedStringTest : public ::testing::Test +{ +protected: + // Test settings + + // Test variables + + virtual void SetUp() + { + } + + virtual void TearDown() + { + + } +}; + +using label_type_t = util::constrained_string<1, 64>; +using short_label_type_t = util::constrained_string<1, 4>; +using min_label_type_t = util::constrained_string<4, 8>; +using version_revision_type_t = util::constrained_string<1, 4, util::RegExChecker>; + +TEST_F(ConstrainedStringTest, Constructor1) +{ + std::string test_str = "test"; + util::string_view test_sv = test_str; + + label_type_t label_c_str("test"); + label_type_t label_c_str_assign = "test"; + + label_type_t label_str(test_str); + label_type_t label_str_assign = test_str; + + label_type_t label_sv(test_sv); + label_type_t label_sv_assign = test_sv; + + ASSERT_EQ(label_c_str, "test"); + ASSERT_EQ(label_c_str_assign, "test"); + + ASSERT_EQ(label_str, test_str); + ASSERT_EQ(label_str_assign, test_str); + + ASSERT_EQ(label_sv, test_sv); + ASSERT_EQ(label_sv_assign, test_sv); +} + +TEST_F(ConstrainedStringTest, Compare) +{ + label_type_t label_1("test"); + label_type_t label_2("999"); + version_revision_type_t version_1("999"); + + ASSERT_EQ(label_1, label_1); + ASSERT_NE(label_1, label_2); + ASSERT_NE(label_1, version_1); + + ASSERT_EQ(label_1, label_1); + ASSERT_EQ(label_2, label_2); + ASSERT_EQ(version_1, version_1); + ASSERT_EQ(version_1, label_2); +} + +TEST_F(ConstrainedStringTest, EqualsOperator) +{ + label_type_t label_1("test"); + label_type_t label_2("999"); + version_revision_type_t version_1("999"); + + ASSERT_TRUE(label_1 == "test"); + ASSERT_TRUE(label_1 == std::string("test")); + ASSERT_TRUE(label_1 == util::string_view("test")); + ASSERT_FALSE(label_1 == "abcd"); + + ASSERT_TRUE(version_1 == "999"); + ASSERT_TRUE(version_1 == std::string("999")); + ASSERT_TRUE(version_1 == util::string_view("999")); + ASSERT_FALSE(version_1 == "111"); +} + +TEST_F(ConstrainedStringTest, StringInteropTests) +{ + std::string str("abcd"); + label_type_t label_1("test"); + + str = label_1; + ASSERT_EQ(str, "test"); + ASSERT_EQ(str, label_1.str()); + + util::string_view sv = label_1; + ASSERT_EQ(sv, "test"); + ASSERT_EQ(sv, label_1); +} + +TEST_F(ConstrainedStringTest, ConstraintCheckTest) +{ + short_label_type_t label_1{ "abcd" }; + short_label_type_t label_2{ "test" }; + short_label_type_t label_3{ "test1234" }; + version_revision_type_t version_1("999"); + + ASSERT_EQ(label_1, "abcd"); + ASSERT_EQ(label_2, "test"); + ASSERT_EQ(label_3, "test"); + + label_2 = "abcd1234"; + ASSERT_EQ(label_2, "abcd"); + + label_2 = version_1; + ASSERT_EQ(label_2, "999"); + ASSERT_EQ(version_1, "999"); + + version_1 = label_1; + ASSERT_EQ(version_1, "999"); + ASSERT_EQ(label_1, "abcd"); + + version_1 = "0111"; + ASSERT_EQ(version_1, "999"); + + version_1 = "111"; + ASSERT_EQ(version_1, "111"); + + min_label_type_t min_label{ "test" }; + ASSERT_EQ(min_label, "test"); + + min_label = "abc"; + ASSERT_EQ(min_label, "abc-"); +} \ No newline at end of file