~aleteoryx/muditaos

ee3167c165465558de1e89ddb8a1103101193394 — Hubert Chrzaniuk 5 years ago 2b4c005
[EGD-3035] unify time formatting functions
M module-utils/Utils.hpp => module-utils/Utils.hpp +48 -5
@@ 89,14 89,57 @@ namespace utils
        return ss.str();
    }

    static inline void findAndReplaceAll(std::string &data, std::string toSearch, std::string replaceStr)
    static inline void findAndReplaceAll(std::string &data,
                                         const std::vector<std::pair<std::string, std::optional<std::string>>> &values,
                                         std::function<std::string(int)> getReplaceString = nullptr)
    {
        size_t pos = data.find(toSearch);
        while (pos != std::string::npos) {
            data.replace(pos, toSearch.size(), replaceStr);
            pos = data.find(toSearch, pos + replaceStr.size());
        int valIdx = 0;
        for (auto &el : values) {
            std::string preStr, replaceStr, postStr;
            const std::string &toSearch = el.first;

            if (toSearch.empty()) {
                continue;
            }

            auto begin_pos = data.begin();
            auto found_pos = data.begin();
            while (found_pos != data.end()) {
                begin_pos = data.begin();
                found_pos = std::search(begin_pos, data.end(), toSearch.begin(), toSearch.end());
                if (found_pos != data.end()) {
                    preStr = std::string(begin_pos, found_pos);
                    if (replaceStr.empty() && el.second) {
                        replaceStr = *el.second;
                    }
                    else if (replaceStr.empty() && getReplaceString) {
                        replaceStr = getReplaceString(valIdx);
                    }
                    postStr.assign(found_pos + toSearch.size(), data.end());
                    data = preStr + replaceStr + postStr;
                }
            }
            valIdx++;
        }
    }

    static inline void findAndReplaceAll(std::string &data,
                                         const std::vector<std::string> &values,
                                         std::function<std::string(int)> getReplaceString)
    {
        std::vector<std::pair<std::string, std::optional<std::string>>> valuePairs;
        valuePairs.reserve(values.size());
        for (auto val : values) {
            valuePairs.emplace_back(std::make_pair(val, std::nullopt));
        }
        findAndReplaceAll(data, valuePairs, getReplaceString);
    }

    static inline void findAndReplaceAll(std::string &data, const std::string &toSearch, const std::string &replaceStr)
    {
        findAndReplaceAll(data, {{toSearch, replaceStr}});
    }

    static inline bool toNumeric(const std::string &text, uint32_t &value)
    {
        try {

M module-utils/test/test_time_conversion.cpp => module-utils/test/test_time_conversion.cpp +1 -1
@@ 166,7 166,7 @@ bool test_time_from_before(std::ostream &outstream, time_t before, std::string f

    strftime(buf, 128, format_expected.c_str(), &todaytime);

    if (!(mytime.str() == buf)) {
    if (!(mytime.str(format_expected) == buf)) {
        std::cerr << "Error: "
                  << "\n\t" << mytime.str() << "\n\t"
                  << " :vs:"

M module-utils/test/unittest_utils.cpp => module-utils/test/unittest_utils.cpp +73 -0
@@ 75,3 75,76 @@ TEST_CASE("toNumeric tests")
    REQUIRE(ret == true);
    REQUIRE(value == 2);
}

TEST_CASE("findAndReplaceAll tests")
{
    // helper lambda
    auto compare = [](std::string &data, std::string &expected, bool &retVal) {
        if (data.compare(expected)) {
            std::cout << "Expected:" << std::endl
                      << "\t" << expected << std::endl
                      << "But is:" << std::endl
                      << "\t" << data << std::endl;
            retVal = false;
        }
    };

    // test findAndReplaceAll with different data combinations
    enum
    {
        eTestString,
        eToSearch,
        eReplace,
        eExpected
    };
    std::vector<std::vector<std::string>> testValues = {
        // multiple replacements
        {"%T TT %T TT %t %T", "%T", "test", "test TT test TT %t test"},
        {"%T TT %T TT %t %T", "%t", "test", "%T TT %T TT test %T"},
        // capital letter test
        {"%T TT %T TT %T %T", "%t", "test", "%T TT %T TT %T %T"},
        // empty test string
        {"", "%t", "test", ""},
        // no match
        {"TEST", "%t", "test", "TEST"},
        // empty match string
        {"TEST", "", "test", "TEST"},
        // empty test string and match string
        {"", "", "test", ""},
        // empty test, match and replace
        {"", "", "", ""},
        // empty replace string
        {"%T TT %T TT %T %T", " ", "", "%TTT%TTT%T%T"},
    };

    std::string testString;
    bool retVal = true;

    for (auto &testCase : testValues) {
        testString = testCase[eTestString];
        utils::findAndReplaceAll(testString, testCase[eToSearch], testCase[eReplace]);
        compare(testString, testCase[eExpected], retVal);
    }

    // test findAndReplaceAll with replacement function
    std::string testFormat, expectedFormat;
    std::vector<std::string> testValuesFunc = {"A", "B", "C", "D"};
    // test helper lambdas
    auto toLower     = [](unsigned char c) { return std::tolower(c); };
    auto replaceFunc = [&](int idx) { return std::string(1, toLower(testValuesFunc[idx][0])); };

    // create test format
    for (const auto &ch : testValuesFunc) {
        testFormat += ch;
    }
    // create expected result
    expectedFormat = testFormat;
    std::transform(expectedFormat.begin(), expectedFormat.end(), expectedFormat.begin(), [toLower](unsigned char c) {
        return toLower(c);
    });

    utils::findAndReplaceAll(testFormat, testValuesFunc, replaceFunc);
    compare(testFormat, expectedFormat, retVal);

    REQUIRE(retVal == true);
}

M module-utils/time/time_conversion.cpp => module-utils/time/time_conversion.cpp +6 -34
@@ 45,41 45,13 @@ namespace utils
            return retval;
        }

        void Timestamp::replace_specifiers()
        {
            int replacements_n              = 0;
            const int strtof_formatter_size = 2;
            for (auto &el : specifiers_replacement) {
                auto begin_pos = format.begin();
                auto found_pos = format.begin();
                while (found_pos != format.end()) {
                    begin_pos = format.begin();
                    found_pos = std::search(begin_pos, format.end(), el.begin(), el.end());
                    if (found_pos != format.end()) {
                        UTF8 begin = std::string(begin_pos, found_pos);
                        UTF8 day   = get_replacement(Replacements(replacements_n), timeinfo);
                        UTF8 next;
                        // std::next doesnt care for distance... (doesn't return element.end()) need to check it
                        if (std::distance(found_pos, format.end()) <= strtof_formatter_size) {
                            next = "";
                        }
                        else {
                            next = std::string(std::next(found_pos, strtof_formatter_size), format.end());
                        }
                        format = (begin + day + next).c_str();
                    }
                }
                ++replacements_n;
            }
        }

        void Timestamp::set_time(time_t newtime)
        {
            time     = newtime;
            timeinfo = *localtime(&time);
        }

        void Timestamp::set_time(std::string timestr, const char *format)
        void Timestamp::set_time(std::string timestr, const char *fmt)
        {

            std::stringstream stream(timestr);


@@ 96,14 68,14 @@ namespace utils
        }

        constexpr uint32_t datasize = 128;
        UTF8 Timestamp::str(std::string format)
        UTF8 Timestamp::str(std::string fmt)
        {
            if (format.compare("") != 0) {
                this->format = format;
            if (fmt.compare("") != 0) {
                this->format = fmt;
            }
            UTF8 datetimestr = "";
            replace_specifiers();

            auto replaceFunc = [&](int idx) { return get_replacement(Replacements(idx), timeinfo); };
            utils::findAndReplaceAll(this->format, specifiers_replacement, replaceFunc);
            auto data = std::unique_ptr<char[]>(new char[datasize]);
            std::strftime(data.get(), datasize, this->format.c_str(), &timeinfo);
            datetimestr = UTF8(data.get());

M module-utils/time/time_conversion.hpp => module-utils/time/time_conversion.hpp +2 -9
@@ 66,13 66,6 @@ namespace utils
                return nullptr;
            }

            /// replace day mon specifiers (first 2 characters)
            /// cant use std::replace -> due to fact that it doesn't support multiple element replace (or i cant find
            /// it) cant use string::replace -> expcetion out_of_range on size when replacing with bigger (our case)
            /// please be vary when using begin_pos/found_pos (as format in next loops might be in totally different
            /// place)
            void replace_specifiers();

          public:
            Timestamp()
            {


@@ 90,7 83,7 @@ namespace utils
            /// set Time time_t value held (set timestamp)
            void set_time(time_t newtime);
            /// set Time from string
            void set_time(std::string time, const char *format);
            void set_time(std::string time, const char *fmt);
            void set_format(std::string format)
            {
                this->format = format;


@@ 182,7 175,7 @@ namespace utils
            void before_n_sec(time_t val);

            /// Time have str(std::string ) this one uses presets
            virtual UTF8 str(std::string format = "");
            virtual UTF8 str(std::string fmt = "");
            bool isToday();
            bool isYesterday();
        };