aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Common/FileRenamer.cpp
blob: 33b55233a2ee6597b573b0a9383beff9df6f922e (plain) (blame)
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
#include <Common/FileRenamer.h>

#include <Common/DateLUT.h>
#include <Common/Exception.h>

#include <chrono>
#include <filesystem>
#include <format>
#include <map>
#include <re2/re2.h>

#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>

namespace fs = std::filesystem;

namespace DB
{

namespace ErrorCodes
{
    extern const int BAD_ARGUMENTS;
}

FileRenamer::FileRenamer() = default;

FileRenamer::FileRenamer(const String & renaming_rule)
    : rule(renaming_rule)
{
    FileRenamer::validateRenamingRule(rule, true);
}

String FileRenamer::generateNewFilename(const String & filename) const
{
    // Split filename and extension
    String file_base = fs::path(filename).stem();
    String file_ext = fs::path(filename).extension();

    // Get current timestamp in microseconds
    String timestamp;
    if (rule.find("%t") != String::npos)
    {
        auto now = std::chrono::system_clock::now();
        timestamp = std::to_string(timeInMicroseconds(now));
    }

    // Define placeholders and their corresponding values
    std::map<String, String> placeholders =
    {
        {"%a", filename},
        {"%f", file_base},
        {"%e", file_ext},
        {"%t", timestamp},
        {"%%", "%"}
    };

    // Replace placeholders with their actual values
    String new_name = rule;
    for (const auto & [placeholder, value] : placeholders)
        boost::replace_all(new_name, placeholder, value);

    return new_name;
}

bool FileRenamer::isEmpty() const
{
    return rule.empty();
}

bool FileRenamer::validateRenamingRule(const String & rule, bool throw_on_error)
{
    // Check if the rule contains invalid placeholders
    re2::RE2 invalid_placeholder_pattern("^([^%]|%[afet%])*$");
    if (!re2::RE2::FullMatch(rule, invalid_placeholder_pattern))
    {
        if (throw_on_error)
            throw Exception(ErrorCodes::BAD_ARGUMENTS, "Invalid renaming rule: Allowed placeholders only %a, %f, %e, %t, and %%");
        return false;
    }

    // Replace valid placeholders with empty strings and count remaining percentage signs.
    String replaced_rule = rule;
    boost::replace_all(replaced_rule, "%a", "");
    boost::replace_all(replaced_rule, "%f", "");
    boost::replace_all(replaced_rule, "%e", "");
    boost::replace_all(replaced_rule, "%t", "");
    if (std::count(replaced_rule.begin(), replaced_rule.end(), '%') % 2)
    {
        if (throw_on_error)
            throw Exception(ErrorCodes::BAD_ARGUMENTS, "Invalid renaming rule: Odd number of consecutive percentage signs");
        return false;
    }

    return true;
}


} // DB