25 inja::json &context,
const std::string &prefix)
27 if (!context.contains(
"element"))
30 if (!context[
"element"].contains(
"source"))
33 auto &source = context[
"element"][
"source"];
35 if (source.at(
"path").empty())
38 auto path = std::filesystem::path{source.at(
"path").get<std::string>()};
39 auto prefix_path = std::filesystem::path(prefix);
41 source[
"path"] = relative(path, prefix_path);
47 const std::vector<std::string> &diagram_names,
50 std::map<std::string, std::vector<std::string>> &translation_units_map)
52 for (
const auto &[name, diagram] : config.
diagrams) {
55 if (!diagram_names.empty() && !
util::contains(diagram_names, name))
58 translation_units_map[name] =
62 LOG_DBG(
"Found {} translation units for diagram '{}'",
63 translation_units_map.at(name).size(), name);
68 std::shared_ptr<config::diagram> diagram_config)
71 switch (generator_type) {
73 cmd = diagram_config->
puml().cmd;
76 cmd = diagram_config->
mermaid().cmd;
83 throw std::runtime_error(
84 fmt::format(
"No render command template provided for {} diagrams",
89 LOG_INFO(
"Rendering diagram {} using {}", diagram_config->
name,
97template <
typename DiagramConfig,
typename GeneratorTag,
typename DiagramModel>
99 const std::string &name, std::shared_ptr<clanguml::config::diagram> diagram,
100 const DiagramModel &model)
102 using diagram_generator =
105 if constexpr (!std::is_same_v<diagram_generator, not_supported>) {
107 std::stringstream buffer;
108 buffer << diagram_generator(
109 dynamic_cast<DiagramConfig &
>(*diagram), *model);
113 auto path = std::filesystem::path{od} /
114 fmt::format(
"{}.{}", name, GeneratorTag::extension);
116 ofs.open(path, std::ofstream::out | std::ofstream::trunc);
121 LOG_INFO(
"Written {} diagram to {}", name, path.string());
124 LOG_INFO(
"Serialization to {} not supported for {}",
125 GeneratorTag::extension, name);
129template <
typename DiagramConfig>
131 std::shared_ptr<clanguml::config::diagram> diagram,
133 const std::vector<std::string> &translation_units,
136 using diagram_config = DiagramConfig;
141 diagram_config, diagram_visitor>(db, diagram->name,
142 dynamic_cast<diagram_config &
>(*diagram), translation_units,
143 runtime_config.
verbose, std::move(progress));
145 if constexpr (std::is_same_v<DiagramConfig, config::sequence_diagram>) {
147 auto from_values = model->list_from_values();
150 for (
const auto &from : from_values) {
151 std::cout << from <<
'\n';
155 inja::json j = inja::json::array();
156 for (
const auto &from : from_values) {
157 j.emplace_back(logging::escape_json(from));
159 std::cout << j.dump();
165 auto to_values = model->list_to_values();
167 for (
const auto &to : to_values) {
168 std::cout << to <<
'\n';
172 inja::json j = inja::json::array();
173 for (
const auto &to : to_values) {
174 j.emplace_back(logging::escape_json(to));
176 std::cout << j.dump();
182 for (
const auto generator_type : runtime_config.
generators) {
214 std::shared_ptr<clanguml::config::diagram> diagram,
216 const std::vector<std::string> &translation_units,
227 if (diagram->type() == diagram_t::kClass) {
228 detail::generate_diagram_impl<class_diagram>(name, diagram, db,
229 translation_units, runtime_config, std::move(progress));
231 else if (diagram->type() == diagram_t::kSequence) {
232 detail::generate_diagram_impl<sequence_diagram>(name, diagram, db,
233 translation_units, runtime_config, std::move(progress));
235 else if (diagram->type() == diagram_t::kPackage) {
236 detail::generate_diagram_impl<package_diagram>(name, diagram, db,
237 translation_units, runtime_config, std::move(progress));
239 else if (diagram->type() == diagram_t::kInclude) {
240 detail::generate_diagram_impl<include_diagram>(name, diagram, db,
241 translation_units, runtime_config, std::move(progress));
248 const std::map<std::string, std::vector<std::string>>
249 &translation_units_map)
252 std::vector<std::future<void>> futs;
254 std::unique_ptr<progress_indicator_base> indicator;
260 <<
"Processing translation units and generating diagrams:\n";
261 indicator = std::make_unique<progress_indicator>();
264 indicator = std::make_unique<json_logger_progress_indicator>();
268 std::vector<std::exception_ptr> errors;
270 for (
const auto &[name, diagram] : config.
diagrams) {
273 if (!diagram_names.empty() && !
util::contains(diagram_names, name))
277 bool at_least_one_generator_supports_diagram_type{
false};
278 for (
const auto generator_type : runtime_config.
generators) {
280 if (generator_supports_diagram_type<plantuml_generator_tag>(
282 at_least_one_generator_supports_diagram_type =
true;
285 if (generator_supports_diagram_type<json_generator_tag>(
287 at_least_one_generator_supports_diagram_type =
true;
290 if (generator_supports_diagram_type<mermaid_generator_tag>(
292 at_least_one_generator_supports_diagram_type =
true;
295 if (generator_supports_diagram_type<graphml_generator_tag>(
297 at_least_one_generator_supports_diagram_type =
true;
301 if (!at_least_one_generator_supports_diagram_type) {
302 LOG_INFO(
"Diagram '{}' not supported by any of selected "
303 "generators - skipping...",
308 const auto &valid_translation_units = translation_units_map.at(name);
310 LOG_DBG(
"Found {} possible translation units for diagram '{}'",
311 valid_translation_units.size(), name);
313 const auto matching_commands_count =
314 db->count_matching_commands(valid_translation_units);
316 if (matching_commands_count == 0) {
317 const auto error_msg = fmt::format(
318 "Diagram '{}' generation failed: no translation units "
319 "found. Please make sure that your 'glob' patterns match "
320 "at least 1 file in 'compile_commands.json'.",
324 indicator->add_progress_bar(
326 indicator->fail(name);
328 throw std::runtime_error(error_msg);
330 catch (std::runtime_error &e) {
331 errors.emplace_back(std::current_exception());
341 LOG_DBG(
"Found {} matching translation unit commands for diagram {}",
342 matching_commands_count, name);
344 auto generator = [&name = name, &diagram = diagram, &indicator,
345 db = std::ref(*db), matching_commands_count,
346 translation_units = valid_translation_units,
347 runtime_config]()
mutable ->
void {
350 indicator->add_progress_bar(name, matching_commands_count,
354 runtime_config, [&indicator, &name]() {
356 indicator->increment(name);
360 indicator->complete(name);
369 indicator->fail(name);
372 catch (std::exception &e) {
374 indicator->fail(name);
377 "Failed to generate diagram '{}': {}", name, e.
what());
379 throw std::runtime_error(fmt::format(
380 "Failed to generate diagram '{}': {}", name, e.
what()));
384 futs.emplace_back(generator_executor.add(std::move(
generator)));
387 for (
auto &fut : futs) {
391 catch (std::exception &e) {
392 errors.emplace_back(std::current_exception());
399 if (errors.empty()) {
400 std::cout << termcolor::white <<
"Done\n";
403 std::cout << termcolor::white <<
"\n";
405 std::cout << termcolor::reset;
411 for (
auto &e : errors) {
413 std::rethrow_exception(e);
417 logging::logger_type_t::text) {
419 fmt::println(
"ERROR: Failed to generate {} diagram '{}' due to "
423 fmt::println(
" - {}", d);
430 j[
"clang_errors"] = inja::json::array();
432 j[
"clang_errors"].emplace_back(d);
435 spdlog::get(
"clanguml-logger")
436 ->log(spdlog::level::err,
438 R
"("file": "{}", "line": {}, "message": {})"),
442 catch (
const std::exception &e) {
444 logging::logger_type_t::text) {
445 fmt::println(
"ERROR: {}", e.
what());
458 switch (diagram_type) {
460 return indicators::Color::yellow;
462 return indicators::Color::blue;
464 return indicators::Color::cyan;
466 return indicators::Color::magenta;
468 return indicators::Color::unspecified;