ArKanjo 0.5.1
A tool for find code duplicated functions in codebases
preprocessor_build.cpp
Go to the documentation of this file.
2#include <cassert>
3#include <iostream>
4#include <algorithm>
5#include <optional>
6
9
11
12std::tuple<std::string, double, size_t, std::vector<std::string>, Granularity>
13PreprocessorBuild::read_parameters(const ParsedOptions& options) {
14 fm::write(INITIAL_MESSAGE);
15
16 std::string path_str;
17
18 if (auto path = options.get("path")) {
19 path_str = *path;
20
21 if (!fs::exists(path_str)) {
22 std::cout << ERROR_PATH_MESSAGE << "\n";
23 path_str.clear();
24 }
25 }
26
27 if (path_str.empty()) {
28 fm::write(PROJECT_PATH_MESSAGE);
29 std::cin >> path_str;
30 }
31 fs::path path(path_str);
32
33 double similarity = 0.0;
34
35 if (auto similarity_value = options.get_as<double>("similarity"))
36 similarity = *similarity_value;
37
38 if (similarity == 0.0) {
39 fm::write(MINIMUM_SIMILARITY_MESSAGE);
40 std::string similarity_message;
41 std::cin >> similarity_message;
42 similarity = stod(similarity_message);
43 }
44
45 size_t use_duplication_finder_index = 0;
46 bool method_from_option = false;
47
48 if (auto method = options.get("method")) {
49 auto found = std::find_if(MethodsType.begin(), MethodsType.end(),
50 [&method](const MethodInfo& mi) {
51 return std::to_string(mi.id) == *method || mi.name == *method;
52 });
53 if (found == MethodsType.end()) {
54 std::string valid_methods;
55 for (size_t i = 0; i < MethodsType.size(); ++i) {
56 if (i > 0) valid_methods += ", ";
57 valid_methods += std::to_string(MethodsType[i].id);
58 valid_methods += "|";
59 valid_methods += MethodsType[i].name;
60 }
61 throw CLIError(
62 "Invalid --method value: '" +
63 std::string(*method) +
64 "'. Use " + valid_methods + "."
65 );
66 }
67 use_duplication_finder_index = found->id - 1;
68 method_from_option = true;
69 }
70
71 if (!method_from_option) {
72 while (true) {
73 fm::write(MESSAGE_DUPLICATION_FINDER_TYPE);
74 for (size_t i = 0; i < MethodsType.size(); ++i) {
75 std::cout << i + 1 << ") " << MethodsType[i].description << '\n';
76 }
77 std::cin >> use_duplication_finder_index;
78 if (
79 use_duplication_finder_index == 0 ||
80 use_duplication_finder_index > MethodsType.size()
81 ) {
82 throw CLIError(INVALID_CODE_DUPLICATION_FINDER);
83 }
84 break;
85 }
86 --use_duplication_finder_index;
87 }
88
90
91 if (auto granularity_value = options.get("granularity")) {
92 std::string_view granularity_option = *granularity_value;
93 if (granularity_option == "file") {
94 granularity = Granularity::File;
95 } else if (granularity_option == "function") {
96 granularity = Granularity::Function;
97 } else {
98 throw CLIError(
99 "Invalid --granularity value: '" +
100 std::string(granularity_option) +
101 "'. Use 'function' (default) or 'file'."
102 );
103 }
104 }
105
106 auto pass_through_args = options.extra_args;
107
108 return {path, similarity, use_duplication_finder_index, pass_through_args, granularity};
109}
110
111void PreprocessorBuild::preprocess(const fs::path& path, double similarity, size_t use_duplication_finder_index,
112 const std::vector<std::string>& pass_through_args,
113 Granularity granularity) {
114 auto start = std::chrono::high_resolution_clock::now();
115
116 fm::write(BREAKER_MESSAGE);
117
118 fs::path base_path = Config::config().base_path / Config::config().name_container;
119
120 if (fs::exists(base_path)) {
121 fs::remove_all(base_path);
122
123 if (mode_verbose)
124 fm::write("Removed existing container folder");
125 }
126
127 fs::create_directories(base_path);
128
129 auto start_breaker = std::chrono::high_resolution_clock::now();
130 auto method = MethodsType[use_duplication_finder_index].create(
131 base_path, similarity, pass_through_args);
132
133 SkipStats stats = {};
134
135 FunctionBreaker function_breaker;
136 auto size_files = function_breaker.process(path, [&method](const FunctionData& fd) {
137 method->on_function(fd);
138 }, stats, minimum_lines, granularity);
139
140 if (mode_verbose) {
141 fm::write("\tFound " + std::to_string(size_files) + " files");
142 fm::write("\tSkipped " + std::to_string(stats.errors) +" parser errors");
143 fm::time("\tExecution time breaker:", start_breaker);
144 }
145
146 auto start_duplication = std::chrono::high_resolution_clock::now();
147 fm::write(DUPLICATION_MESSAGE);
148
149 method->execute();
150 if (mode_verbose)
151 fm::time("\tExecution time duplication:", start_duplication);
152
154
155 fm::write(END_MESSAGE);
156
157 fm::time("Execution time:", start);
158}
159
160PreprocessorBuild::PreprocessorBuild(bool force_preprocess, const fs::path& path, double similarity) {
161 fs::path base_path = Config::config().base_path / Config::config().name_container;
162 if (force_preprocess || !std::filesystem::exists(base_path / CONFIG_PATH)) {
163 preprocess(path, similarity, 0);
164 }
165}
166
168 if (options.has("json"))
169 throw CLIError("--json is not supported in this command.");
170
171 try {
172 if (auto minimum_lines = options.get_as<int>("minimum-lines"))
173 this->minimum_lines = *minimum_lines;
174 } catch (const std::invalid_argument&) {
175 throw CLIError(
176 "--minimum-lines must be a valid number (passing " +
177 std::string(*options.get("minimum-lines")) + ")"
178 );
179 } catch (const std::out_of_range&) {
180 throw CLIError("--minimum-lines outside the permitted range");
181 }
182
183 return true;
184}
185
186bool PreprocessorBuild::run([[maybe_unused]] const ParsedOptions& options) {
187 mode_verbose = options.has("verbose");
188
189 fs::path base_path = Config::config().base_path / Config::config().name_container;
190 auto [path, similarity, use_duplication_finder_index,
191 pass_through_args, granularity] = read_parameters(options);
192 preprocess(path, similarity, use_duplication_finder_index,
193 pass_through_args, granularity);
194
195 return true;
196}
Base class for CLI-related errors.
Definition: cli_error.hpp:10
const CliOption * options() const final
static Config & config()
Gets the singleton configuration instance.
Definition: config.cpp:68
static void write(const std::string &template_str, const std::vector< T > &data, enum Format effective=Format::AUTO, RowColorFn color_fn=nullptr, std::ostream &out=std::cout)
static void time(const std::string &str, std::chrono::high_resolution_clock::time_point start, std::ostream &out=std::cout)
Main function extraction processor.
int process(const fs::path &folder_path, std::function< void(const FunctionData &)> on_function, SkipStats &stats, int minimum_lines=0, Granularity granularity=Granularity::Function)
Processes all files in directory.
static void save_current_run_params(const fs::path &path, const fs::path &cache_path)
Saves preprocessing parameters for future runs.
static constexpr const char * CONFIG_PATH
Configuration file path.
bool validate(const ParsedOptions &options) override
Validate the arguments already analyzed.
PreprocessorBuild()=default
bool run(const ParsedOptions &options) override
Executes the main logic of the command.
Configuration management interface.
Granularity
Comparison granularity used when breaking the codebase.
Codebase preprocessing interface.
std::filesystem::path base_path
Default base path for temporary files.
Definition: schema.hpp:16
std::filesystem::path name_container
Name of the cache container.
Definition: schema.hpp:18