~aleteoryx/muditaos

ref: cf1ec5d1029ee40b1f14a816525c5eb99d67e0b0 muditaos/module-db/tests/DbInitializer.cpp -rw-r--r-- 4.1 KiB
cf1ec5d1 — Marcin Zieliński [MOS-769] Make a destructor virtual 3 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "module-db/Database/DatabaseInitializer.hpp"

#include <log/log.hpp>
#include <Utils.hpp>

#include <algorithm>
#include <set>
#include <array>

DatabaseInitializer::DatabaseInitializer(Database *db) : db(db)
{}

bool DatabaseInitializer::run(std::filesystem::path path, std::string ext)
{
    // Database name is database file path, need to strip off all filesystem related stuff(path, extension)
    std::filesystem::path dbpath = db->getName();
    std::string dbname           = dbpath.filename().replace_extension();

    auto files = listFiles(path, dbname, ext);
    for (auto file : files) {
        LOG_DEBUG("Running db script: %s", file.c_str());
        auto commands = readCommands(file);
        if (!executeOnDb(commands)) {
            LOG_ERROR("Can't initialize database [%s] with [%s]", db->getName().c_str(), file.c_str());
            return false;
        }
    }
    return true;
}

std::string DatabaseInitializer::readContent(const char *filename) const noexcept
{
    std::unique_ptr<char[]> fcontent;
    long fsize = 0;

    auto fp = std::fopen(filename, "r");
    if (fp) {
        std::fseek(fp, 0, SEEK_END);
        fsize = std::ftell(fp);
        std::rewind(fp);

        fcontent = std::make_unique<char[]>(fsize + 1);

        std::fread(fcontent.get(), 1, fsize, fp);

        std::fclose(fp);
    }

    return std::string(fcontent.get());
}

std::vector<std::string> DatabaseInitializer::readCommands(std::filesystem::path filePath)
{
    auto fileContent = readContent(filePath.c_str());
    std::string currentStatement{};
    std::vector<std::string> statements{};

    std::string line{};
    for (auto &c : fileContent) {
        if (c != '\n') {
            line += c;
        }
        else {
            if (line.empty() || utils::startsWith(line, "--")) {
                line.clear();
                continue;
            }
            if (utils::endsWith(line, ";")) {
                statements.push_back(currentStatement + line);
                currentStatement.clear();
                line.clear();
                continue;
            }
            currentStatement += line;

            line.clear();
        }
    }
    return statements;
}

std::array<std::string, 3> DatabaseInitializer::splitFilename(std::string filename)
{
    auto name    = filename.substr(0, filename.find("."));
    auto prefix  = name.substr(0, name.find_last_of("_"));
    auto postfix = name.substr(name.find_last_of("_") + 1, std::string::npos);

    return {name, prefix, postfix};
}

std::vector<std::filesystem::path> DatabaseInitializer::listFiles(std::filesystem::path path,
                                                                  std::string prefix,
                                                                  std::string ext)
{
    std::set<std::pair<int, std::filesystem::path>> orderedFiles;
    for (const auto &entry : std::filesystem::directory_iterator(path)) {
        if (!entry.is_directory() && entry.path().has_filename()) {
            try {
                auto parts      = splitFilename(entry.path().filename().string());
                auto filePrefix = parts[1];
                if (filePrefix == prefix) {
                    auto num = std::stoi(parts[2]);
                    if ((num == 1) || (num == 2 && (prefix == "contacts" || prefix == "notifications"))) {
                        orderedFiles.insert({num, entry.path()});
                    }
                }
            }
            catch (std::invalid_argument &e) {
                LOG_INFO("Ignoring file: %s", entry.path().c_str());
            }
        }
    }

    std::vector<std::filesystem::path> files;
    std::for_each(orderedFiles.begin(), orderedFiles.end(), [&](auto item) { files.push_back(item.second); });
    return files;
}

bool DatabaseInitializer::executeOnDb(const std::vector<std::string> statements)
{
    for (auto st : statements) {
        if (!db->execute(st.c_str())) {
            return false;
        }
    }
    return true;
}