23#include <inja/inja.hpp>
34 , together_group_stack_{
true}
40 ostr <<
"classDiagram\n";
48 assert(!full_name.empty());
52 auto class_label =
config().simplify_template_type(render_name(full_name));
54 ostr << indent(1) <<
"class " << c.
alias() <<
"[\""
55 << escape_name(class_label) <<
"\"]\n";
63 std::string class_type{
"class"};
65 ostr << indent(1) <<
"class " << c.
alias();
70 ostr << indent(2) <<
"<<union>>\n";
72 ostr << indent(2) <<
"<<abstract>>\n";
88 std::set<std::string> rendered_relations;
90 std::stringstream all_relations_str;
96 LOG_DBG(
"Skipping {} relation from {} to {} due "
98 to_string(r.type()), c.
full_name(), r.destination(), e.what());
105 std::vector<clanguml::class_diagram::model::class_member> members{
110 for (
const auto &m : members) {
111 if (!
config().include_relations_also_as_members() &&
112 rendered_relations.find(m.name()) != rendered_relations.end())
120 ostr << indent(1) <<
"}" <<
'\n';
122 if (
config().generate_links) {
128 for (
const auto &member : c.
members())
131 for (
const auto &method : c.
methods())
147 const std::vector<class_method> &methods, std::ostream &ostr)
const
149 auto sorted_methods = methods;
152 for (
const auto &m : sorted_methods) {
159 const std::vector<class_method> &methods)
const
161 std::map<std::string, std::vector<class_method>> result;
167 for (
const auto &m : methods) {
168 if (m.is_constructor() || m.is_destructor()) {
169 result[
"constructors"].push_back(m);
171 else if (m.is_copy_assignment() || m.is_move_assignment()) {
172 result[
"assignment"].push_back(m);
174 else if (m.is_operator()) {
175 result[
"operators"].push_back(m);
178 result[
"other"].push_back(m);
189 const auto &uns =
config().using_namespace();
191 constexpr auto kAbbreviatedMethodArgumentsLength{15};
195 std::string type{uns.relative(
config().simplify_template_type(m.
type()))};
197 ostr << indent(2) << mermaid_common::to_mermaid(m.
access()) << m.
name();
204 if (
config().generate_method_arguments() !=
206 std::vector<std::string> params;
208 std::back_inserter(params), [
this](
const auto &mp) {
209 return config().simplify_template_type(
210 mp.to_string(config().using_namespace()));
212 auto args_string = fmt::format(
"{}", fmt::join(params,
", "));
213 if (
config().generate_method_arguments() ==
216 args_string, kAbbreviatedMethodArgumentsLength);
224 std::vector<std::string> method_mods;
226 method_mods.emplace_back(
"default");
229 method_mods.emplace_back(
"const");
232 method_mods.emplace_back(
"constexpr");
235 method_mods.emplace_back(
"consteval");
238 method_mods.emplace_back(
"coroutine");
241 if (!method_mods.empty()) {
242 ostr << fmt::format(
"[{}] ", fmt::join(method_mods,
","));
245 ostr << escape_name(render_name(type));
258 const auto &uns =
config().using_namespace();
262 ostr << indent(2) << mermaid_common::to_mermaid(m.
access()) << m.
name()
264 << escape_name(uns.relative(
265 config().simplify_template_type(render_name(m.
type()))));
270 ostr << indent(1) <<
"class"
273 ostr <<
" {" <<
'\n';
274 ostr << indent(2) <<
"<<concept>>\n";
276 if (
config().generate_concept_requirements() &&
278 std::vector<std::string> parameters;
281 parameters.emplace_back(
282 escape_name(p.to_string(
config().using_namespace())));
286 << fmt::format(
"\"({})\"\n", fmt::join(parameters,
","));
290 << fmt::format(
"\"{}\"\n", escape_name(req,
false));
294 ostr << indent(1) <<
"}" <<
'\n';
296 if (
config().generate_links) {
304 for (
const auto &decorator : member.
decorators()) {
305 auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
306 if (note && note->applies_to_diagram(
config().name)) {
307 ostr << indent(1) <<
"note for " << alias <<
" \"" << note->text
315 for (
const auto &p :
model()) {
316 if (
auto *pkg =
dynamic_cast<package *
>(p.get()); pkg) {
319 else if (
auto *cls =
dynamic_cast<class_ *
>(p.get()); cls) {
322 else if (
auto *enm =
dynamic_cast<enum_ *
>(p.get()); enm) {
325 else if (
auto *cpt =
dynamic_cast<concept_ *
>(p.get()); cpt) {
332 const relationship &r, std::set<std::string> &rendered_relations)
const
336 LOG_DBG(
"Processing relationship {}", to_string(r.
type()));
338 std::string destination;
341 if (!target_element.has_value())
343 "Missing element in the model for ID: {}", r.
destination())};
345 destination = target_element.value().full_name(
false);
348 destination = destination.substr(2, destination.size());
350 std::string mmd_relation;
354 mmd_relation += mermaid_common::to_mermaid(r.
type());
359 if (!r.
label().empty()) {
360 if (r.
type() == relationship_t::kFriendship)
361 rendered_relations.emplace(fmt::format(
362 "{}[friend]", mermaid_common::to_mermaid(r.
access())));
364 rendered_relations.emplace(r.
label());
369 const class_ &c, std::ostream &ostr)
const
376 std::set<std::string> rendered_relations;
378 std::stringstream all_relations_str;
379 std::set<std::string> unique_relations;
382 LOG_DBG(
"== Processing relationship {}", to_string(r.type()));
384 std::stringstream relstr;
387 destination = r.destination();
389 std::string relation_str;
391 if (!r.multiplicity_source().empty())
392 relation_str +=
"\"" + r.multiplicity_source() +
"\" ";
394 relation_str += mermaid_common::to_mermaid(r.type());
396 if (!r.multiplicity_destination().empty())
397 relation_str +=
" \"" + r.multiplicity_destination() +
"\"";
399 std::string target_alias;
401 target_alias =
model().to_alias(destination);
404 LOG_DBG(
"Failed to find alias to {}", destination);
412 if (r.type() == relationship_t::kContainment) {
413 relstr << indent(1) << target_alias <<
" " << relation_str
417 relstr << indent(1) << c.
alias() <<
" " << relation_str <<
" "
423 if (!r.label().empty()) {
424 auto lbl = r.label();
425 if (r.type() == relationship_t::kFriendship)
427 relstr << mermaid_common::to_mermaid(r.access()) << lbl;
428 rendered_relations.emplace(r.label());
431 if (unique_relations.count(relstr.str()) == 0) {
432 unique_relations.emplace(relstr.str());
436 LOG_DBG(
"=== Adding relation {}", relstr.str());
438 all_relations_str << relstr.str();
442 LOG_DBG(
"=== Skipping {} relation from {} to {} due "
444 to_string(r.type()), c.
full_name(), destination, e.what());
448 if (
model().should_include(relationship_t::kExtension)) {
449 for (
const auto &b : c.
parents()) {
450 std::stringstream relstr;
452 auto target_alias =
model().to_alias(b.id());
458 relstr << indent(1) << target_alias <<
" <|-- " << c.
alias()
460 all_relations_str << relstr.str();
463 LOG_DBG(
"=== Skipping inheritance relation from {} to {} due "
465 b.name(), c.
name(), e.what());
470 ostr << all_relations_str.str();
474 const concept_ &c, std::ostream &ostr)
const
481 std::set<std::string> rendered_relations;
483 std::stringstream all_relations_str;
484 std::set<std::string> unique_relations;
487 LOG_DBG(
"== Processing relationship {}", to_string(r.type()));
489 std::stringstream relstr;
492 destination = r.destination();
494 std::string mmd_relation;
495 if (!r.multiplicity_source().empty())
496 mmd_relation +=
"\"" + r.multiplicity_source() +
"\" ";
498 mmd_relation += mermaid_common::to_mermaid(r.type());
500 if (!r.multiplicity_destination().empty())
501 mmd_relation +=
" \"" + r.multiplicity_destination() +
"\"";
503 std::string target_alias;
505 target_alias =
model().to_alias(destination);
508 LOG_DBG(
"Failed to find alias to {}", destination);
516 if (r.type() == relationship_t::kContainment) {
517 relstr << indent(1) << target_alias <<
" " << mmd_relation
521 relstr << indent(1) << c.
alias() <<
" " << mmd_relation <<
" "
527 if (!r.label().empty()) {
528 auto lbl = r.label();
529 if (r.type() == relationship_t::kFriendship)
531 relstr << mermaid_common::to_mermaid(r.access()) << lbl;
532 rendered_relations.emplace(r.label());
535 if (unique_relations.count(relstr.str()) == 0) {
536 unique_relations.emplace(relstr.str());
540 LOG_DBG(
"=== Adding relation {}", relstr.str());
542 all_relations_str << relstr.str();
546 LOG_DBG(
"=== Skipping {} relation from {} to {} due "
548 to_string(r.type()), c.
full_name(), destination, e.what());
552 ostr << all_relations_str.str();
559 std::stringstream relstr;
561 destination = r.destination();
563 auto target_alias =
model().to_alias(destination);
569 if (r.type() == relationship_t::kContainment) {
570 relstr << indent(1) << target_alias <<
" "
576 relstr << indent(1) << e.
alias() <<
" "
579 <<
" " << target_alias;
582 auto lbl = r.label();
583 if (r.type() == relationship_t::kFriendship)
586 relstr <<
" : " << r.label();
590 ostr << relstr.str();
593 LOG_DBG(
"Skipping {} relation from {} to {} due "
603 ostr << indent(1) <<
"class " << e.
alias();
605 ostr <<
" {" <<
'\n';
607 ostr << indent(2) <<
"<<enumeration>>\n";
609 for (
const auto &enum_constant : e.
constants()) {
610 ostr << indent(2) << enum_constant <<
'\n';
613 ostr << indent(1) <<
"}" <<
'\n';
615 if (
config().generate_links) {
624 for (
const auto &subpackage : p) {
625 if (
dynamic_cast<package *
>(subpackage.get()) !=
nullptr) {
627 const auto &sp =
dynamic_cast<package &
>(*subpackage);
628 if (!sp.is_empty()) {
636 else if (
auto *cls =
dynamic_cast<class_ *
>(subpackage.get()); cls) {
637 if (
model().should_include(*subpackage)) {
638 auto together_group =
639 config().get_together_group(cls->full_name(
false));
640 if (together_group) {
642 together_group.value(), cls);
650 else if (
auto *enm =
dynamic_cast<enum_ *
>(subpackage.get()); enm) {
651 if (
model().should_include(*subpackage)) {
652 auto together_group =
653 config().get_together_group(subpackage->full_name(
false));
654 if (together_group) {
656 together_group.value(), enm);
664 else if (
auto *cpt =
dynamic_cast<concept_ *
>(subpackage.get()); cpt) {
665 if (
model().should_include(*subpackage)) {
666 auto together_group =
667 config().get_together_group(cpt->full_name(
false));
668 if (together_group) {
670 together_group.value(), cpt);
682 const package &p, std::ostream &ostr)
const
684 for (
const auto &subpackage : p) {
685 if (
dynamic_cast<package *
>(subpackage.get()) !=
nullptr) {
689 const auto &sp =
dynamic_cast<package &
>(*subpackage);
693 else if (
dynamic_cast<class_ *
>(subpackage.get()) !=
nullptr) {
694 if (
model().should_include(*subpackage)) {
696 dynamic_cast<class_ &
>(*subpackage), ostr);
699 else if (
dynamic_cast<enum_ *
>(subpackage.get()) !=
nullptr) {
700 if (
model().should_include(*subpackage)) {
702 dynamic_cast<enum_ &
>(*subpackage), ostr);
705 else if (
dynamic_cast<concept_ *
>(subpackage.get()) !=
nullptr) {
706 if (
model().should_include(*subpackage)) {
708 dynamic_cast<concept_ &
>(*subpackage), ostr);
725 for (
const auto &p :
model()) {
726 if (
auto *pkg =
dynamic_cast<package *
>(p.get()); pkg) {
727 if (!pkg->is_empty())
730 else if (
auto *cls =
dynamic_cast<class_ *
>(p.get()); cls) {
731 auto together_group =
732 config().get_together_group(cls->full_name(
false));
733 if (together_group) {
735 together_group.value(), cls);
742 else if (
auto *enm =
dynamic_cast<enum_ *
>(p.get()); enm) {
743 auto together_group =
744 config().get_together_group(enm->full_name(
false));
745 if (together_group) {
747 together_group.value(), enm);
754 else if (
auto *cpt =
dynamic_cast<concept_ *
>(p.get()); cpt) {
755 auto together_group =
756 config().get_together_group(cpt->full_name(
false));
757 if (together_group) {
759 together_group.value(), cpt);
771 for (
const auto &[group_name, group_elements] :
774 for (
auto *e : group_elements) {
775 if (
auto *cls =
dynamic_cast<class_ *
>(e); cls) {
779 if (
auto *enm =
dynamic_cast<enum_ *
>(e); enm) {
783 if (
auto *cpt =
dynamic_cast<concept_ *
>(e); cpt) {