Added option style to plantuml config section

This commit is contained in:
Bartek Kryza
2024-03-03 17:01:31 +01:00
parent c4ec8bef8a
commit 76fa811869
34 changed files with 363 additions and 128 deletions

View File

@@ -287,8 +287,8 @@ void generator::generate(const concept_ &c, std::ostream &ostr) const
ostr << indent(1) << "class" ostr << indent(1) << "class"
<< " " << c.alias(); << " " << c.alias();
if (!c.style().empty()) if (!c.style())
ostr << " " << c.style(); ostr << " " << c.style().value();
ostr << " {" << '\n'; ostr << " {" << '\n';
ostr << indent(2) << "<<concept>>\n"; ostr << indent(2) << "<<concept>>\n";

View File

@@ -108,7 +108,7 @@ void generator::generate_alias(const concept_ &c, std::ostream &ostr) const
if (config().generate_fully_qualified_name()) if (config().generate_fully_qualified_name())
ostr << "class" ostr << "class"
<< " \"" << c.name(); << " \"" << c.full_name_no_ns();
else else
ostr << "class" ostr << "class"
<< " \"" << render_name(c.full_name()); << " \"" << render_name(c.full_name());
@@ -137,8 +137,7 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
common_generator<diagram_config, diagram_model>::generate_link(ostr, c); common_generator<diagram_config, diagram_model>::generate_link(ostr, c);
} }
if (!c.style().empty()) generate_style(ostr, c.type_name(), c);
ostr << " " << c.style();
ostr << " {" << '\n'; ostr << " {" << '\n';
@@ -169,8 +168,7 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
catch (error::uml_alias_missing &e) { catch (error::uml_alias_missing &e) {
LOG_DBG("Skipping {} relation from {} to {} due " LOG_DBG("Skipping {} relation from {} to {} due "
"to: {}", "to: {}",
plantuml_common::to_plantuml(r.type(), r.style()), to_string(r.type()), c.full_name(), r.destination(), e.what());
c.full_name(), r.destination(), e.what());
} }
} }
@@ -378,8 +376,7 @@ void generator::generate(const concept_ &c, std::ostream &ostr) const
common_generator<diagram_config, diagram_model>::generate_link(ostr, c); common_generator<diagram_config, diagram_model>::generate_link(ostr, c);
} }
if (!c.style().empty()) generate_style(ostr, c.type_name(), c);
ostr << " " << c.style();
ostr << " {" << '\n'; ostr << " {" << '\n';
@@ -444,8 +441,7 @@ void generator::generate_relationship(
{ {
namespace plantuml_common = clanguml::common::generators::plantuml; namespace plantuml_common = clanguml::common::generators::plantuml;
LOG_DBG("Processing relationship {}", LOG_DBG("Processing relationship {}", to_string(r.type()));
plantuml_common::to_plantuml(r.type(), r.style()));
std::string destination; std::string destination;
@@ -463,7 +459,7 @@ void generator::generate_relationship(
if (!r.multiplicity_source().empty()) if (!r.multiplicity_source().empty())
puml_relation += "\"" + r.multiplicity_source() + "\" "; puml_relation += "\"" + r.multiplicity_source() + "\" ";
puml_relation += plantuml_common::to_plantuml(r.type(), r.style()); puml_relation += plantuml_common::to_plantuml(r, config());
if (!r.multiplicity_destination().empty()) if (!r.multiplicity_destination().empty())
puml_relation += " \"" + r.multiplicity_destination() + "\""; puml_relation += " \"" + r.multiplicity_destination() + "\"";
@@ -491,7 +487,7 @@ void generator::generate_relationships(
continue; continue;
LOG_DBG("== Processing relationship {}", LOG_DBG("== Processing relationship {}",
plantuml_common::to_plantuml(r.type(), r.style())); plantuml_common::to_plantuml(r, config()));
std::stringstream relstr; std::stringstream relstr;
clanguml::common::id_t destination{0}; clanguml::common::id_t destination{0};
@@ -502,7 +498,7 @@ void generator::generate_relationships(
if (!r.multiplicity_source().empty()) if (!r.multiplicity_source().empty())
puml_relation += "\"" + r.multiplicity_source() + "\" "; puml_relation += "\"" + r.multiplicity_source() + "\" ";
puml_relation += plantuml_common::to_plantuml(r.type(), r.style()); puml_relation += plantuml_common::to_plantuml(r, config());
if (!r.multiplicity_destination().empty()) if (!r.multiplicity_destination().empty())
puml_relation += " \"" + r.multiplicity_destination() + "\""; puml_relation += " \"" + r.multiplicity_destination() + "\"";
@@ -541,8 +537,7 @@ void generator::generate_relationships(
catch (error::uml_alias_missing &e) { catch (error::uml_alias_missing &e) {
LOG_DBG("=== Skipping {} relation from {} to {} due " LOG_DBG("=== Skipping {} relation from {} to {} due "
"to: {}", "to: {}",
plantuml_common::to_plantuml(r.type(), r.style()), to_string(r.type()), c.full_name(), destination, e.what());
c.full_name(), destination, e.what());
} }
} }
@@ -587,8 +582,7 @@ void generator::generate_relationships(
if (!model().should_include(r.type())) if (!model().should_include(r.type()))
continue; continue;
LOG_DBG("== Processing relationship {}", LOG_DBG("== Processing relationship {}", to_string(r.type()));
plantuml_common::to_plantuml(r.type(), r.style()));
std::stringstream relstr; std::stringstream relstr;
clanguml::common::id_t destination{0}; clanguml::common::id_t destination{0};
@@ -599,7 +593,7 @@ void generator::generate_relationships(
if (!r.multiplicity_source().empty()) if (!r.multiplicity_source().empty())
puml_relation += "\"" + r.multiplicity_source() + "\" "; puml_relation += "\"" + r.multiplicity_source() + "\" ";
puml_relation += plantuml_common::to_plantuml(r.type(), r.style()); puml_relation += plantuml_common::to_plantuml(r, config());
if (!r.multiplicity_destination().empty()) if (!r.multiplicity_destination().empty())
puml_relation += " \"" + r.multiplicity_destination() + "\""; puml_relation += " \"" + r.multiplicity_destination() + "\"";
@@ -638,8 +632,7 @@ void generator::generate_relationships(
catch (error::uml_alias_missing &e) { catch (error::uml_alias_missing &e) {
LOG_DBG("=== Skipping {} relation from {} to {} due " LOG_DBG("=== Skipping {} relation from {} to {} due "
"to: {}", "to: {}",
plantuml_common::to_plantuml(r.type(), r.style()), to_string(r.type()), c.full_name(), destination, e.what());
c.full_name(), destination, e.what());
} }
} }
@@ -654,8 +647,7 @@ void generator::generate(const enum_ &e, std::ostream &ostr) const
common_generator<diagram_config, diagram_model>::generate_link(ostr, e); common_generator<diagram_config, diagram_model>::generate_link(ostr, e);
} }
if (!e.style().empty()) generate_style(ostr, e.type_name(), e);
ostr << " " << e.style();
ostr << " {" << '\n'; ostr << " {" << '\n';
@@ -687,7 +679,7 @@ void generator::generate_relationships(const enum_ &e, std::ostream &ostr) const
relstr << e.alias() << " " relstr << e.alias() << " "
<< clanguml::common::generators::plantuml::to_plantuml( << clanguml::common::generators::plantuml::to_plantuml(
r.type(), r.style()) r, config())
<< " " << target_alias; << " " << target_alias;
if (!r.label().empty()) if (!r.label().empty())
@@ -701,7 +693,7 @@ void generator::generate_relationships(const enum_ &e, std::ostream &ostr) const
LOG_DBG("Skipping {} relation from {} to {} due " LOG_DBG("Skipping {} relation from {} to {} due "
"to: {}", "to: {}",
clanguml::common::generators::plantuml::to_plantuml( clanguml::common::generators::plantuml::to_plantuml(
r.type(), r.style()), r, config()),
e.full_name(), destination, ex.what()); e.full_name(), destination, ex.what());
} }
} }
@@ -724,8 +716,7 @@ void generator::generate(const package &p, std::ostream &ostr) const
if (p.is_deprecated()) if (p.is_deprecated())
ostr << " <<deprecated>>"; ostr << " <<deprecated>>";
if (!p.style().empty()) generate_style(ostr, p.type_name(), p);
ostr << " " << p.style();
ostr << " {" << '\n'; ostr << " {" << '\n';
} }

View File

@@ -462,12 +462,12 @@ void translation_unit_visitor::process_constraint_requirements(
parm_var_decl) { parm_var_decl) {
parm_var_decl->getQualifiedNameAsString(); parm_var_decl->getQualifiedNameAsString();
auto param_name = parm_var_decl->getQualifiedNameAsString(); auto param_name = parm_var_decl->getNameAsString();
auto param_type = common::to_string( auto param_type = common::to_string(
parm_var_decl->getType(), cpt->getASTContext()); parm_var_decl->getType(), cpt->getASTContext());
LOG_DBG("=== Processing parameter variable declaration: {}, {}", LOG_DBG("=== Processing parameter variable declaration: {}, {}",
param_name, param_type); param_type, param_name);
concept_model.add_parameter( concept_model.add_parameter(
{std::move(param_type), std::move(param_name)}); {std::move(param_type), std::move(param_name)});

View File

@@ -19,7 +19,8 @@
namespace clanguml::common::generators::mermaid { namespace clanguml::common::generators::mermaid {
std::string to_mermaid(relationship_t r, const std::string & /*style*/) std::string to_mermaid(
relationship_t r, const std::optional<std::string> & /*style*/)
{ {
switch (r) { switch (r) {
case relationship_t::kOwnership: case relationship_t::kOwnership:

View File

@@ -38,7 +38,8 @@ using clanguml::common::model::element;
using clanguml::common::model::message_t; using clanguml::common::model::message_t;
using clanguml::common::model::relationship_t; using clanguml::common::model::relationship_t;
std::string to_mermaid(relationship_t r, const std::string &style); std::string to_mermaid(
relationship_t r, const std::optional<std::string> &style);
std::string to_mermaid(access_t scope); std::string to_mermaid(access_t scope);
std::string to_mermaid(message_t r); std::string to_mermaid(message_t r);

View File

@@ -19,28 +19,48 @@
namespace clanguml::common::generators::plantuml { namespace clanguml::common::generators::plantuml {
std::string to_plantuml(relationship_t r, const std::string &style) std::string to_plantuml(const relationship &r, const config::diagram &cfg)
{ {
switch (r) { using common::model::relationship_t;
std::string style;
const auto &inline_style = r.style();
if (inline_style && !inline_style.value().empty()) {
if (inline_style && inline_style.value().back() == ']')
style = *inline_style;
else
style = fmt::format("[{}]", inline_style.value());
}
if (style.empty() && cfg.puml) {
if (auto config_style = cfg.puml().get_style(r.type());
config_style.has_value()) {
style = config_style.value();
}
}
switch (r.type()) {
case relationship_t::kOwnership: case relationship_t::kOwnership:
case relationship_t::kComposition: case relationship_t::kComposition:
return style.empty() ? "*--" : fmt::format("*-[{}]-", style); return style.empty() ? "*--" : fmt::format("*-{}-", style);
case relationship_t::kAggregation: case relationship_t::kAggregation:
return style.empty() ? "o--" : fmt::format("o-[{}]-", style); return style.empty() ? "o--" : fmt::format("o-{}-", style);
case relationship_t::kContainment: case relationship_t::kContainment:
return style.empty() ? "--+" : fmt::format("-[{}]-+", style); return style.empty() ? "--+" : fmt::format("-{}-+", style);
case relationship_t::kAssociation: case relationship_t::kAssociation:
return style.empty() ? "-->" : fmt::format("-[{}]->", style); return style.empty() ? "-->" : fmt::format("-{}->", style);
case relationship_t::kInstantiation: case relationship_t::kInstantiation:
return style.empty() ? "..|>" : fmt::format(".[{}].|>", style); return style.empty() ? "..|>" : fmt::format(".{}.|>", style);
case relationship_t::kFriendship: case relationship_t::kFriendship:
return style.empty() ? "<.." : fmt::format("<.[{}].", style); return style.empty() ? "<.." : fmt::format("<.{}.", style);
case relationship_t::kDependency: case relationship_t::kDependency:
return style.empty() ? "..>" : fmt::format(".[{}].>", style); return style.empty() ? "..>" : fmt::format(".{}.>", style);
case relationship_t::kConstraint: case relationship_t::kConstraint:
return style.empty() ? "..>" : fmt::format(".[{}].>", style); return style.empty() ? "..>" : fmt::format(".{}.>", style);
case relationship_t::kAlias: case relationship_t::kAlias:
return style.empty() ? ".." : fmt::format(".[{}].", style); return style.empty() ? ".." : fmt::format(".{}.", style);
default: default:
return ""; return "";
} }

View File

@@ -19,6 +19,7 @@
#include "common/generators/generator.h" #include "common/generators/generator.h"
#include "common/model/diagram_filter.h" #include "common/model/diagram_filter.h"
#include "common/model/relationship.h"
#include "config/config.h" #include "config/config.h"
#include "util/error.h" #include "util/error.h"
#include "util/util.h" #include "util/util.h"
@@ -36,9 +37,9 @@ namespace clanguml::common::generators::plantuml {
using clanguml::common::model::access_t; using clanguml::common::model::access_t;
using clanguml::common::model::element; using clanguml::common::model::element;
using clanguml::common::model::message_t; using clanguml::common::model::message_t;
using clanguml::common::model::relationship_t; using clanguml::common::model::relationship;
std::string to_plantuml(relationship_t r, const std::string &style); std::string to_plantuml(const relationship &r, const config::diagram &cfg);
std::string to_plantuml(access_t scope); std::string to_plantuml(access_t scope);
std::string to_plantuml(message_t r); std::string to_plantuml(message_t r);
@@ -124,6 +125,19 @@ public:
void generate_notes( void generate_notes(
std::ostream &ostr, const model::element &element) const; std::ostream &ostr, const model::element &element) const;
/**
* @brief Generate diagram element PlantUML style
*
* This method renders a style for a specific `el` element if specified
* in the config file or inline comment directive.
*
* @param ostr Output stream
* @param element_type Name of the element type (e.g. "class")
* @param el Reference to a stylable diagram element
*/
void generate_style(std::ostream &ostr, const std::string &element_type,
const model::stylable_element &el) const;
/** /**
* @brief Generate comment with diagram metadata * @brief Generate comment with diagram metadata
* *
@@ -437,6 +451,22 @@ void generator<C, D>::generate_plantuml_directives(
} }
} }
template <typename C, typename D>
void generator<C, D>::generate_style(std::ostream &ostr,
const std::string &element_type, const model::stylable_element &el) const
{
const auto &config = generators::generator<C, D>::config();
if (el.style() && !el.style().value().empty())
ostr << " " << *el.style();
else if (config.puml) {
if (const auto config_style = config.puml().get_style(element_type);
config_style) {
ostr << " " << *config_style;
}
}
}
template <typename C, typename D> template <typename C, typename D>
void generator<C, D>::generate_notes( void generator<C, D>::generate_notes(
std::ostream &ostr, const model::element &e) const std::ostream &ostr, const model::element &e) const

View File

@@ -93,16 +93,16 @@ public:
void set_name(const std::string &name) { name_ = name; } void set_name(const std::string &name) { name_ = name; }
/** /**
* Return diagram's name. * Return diagram element name.
* *
* @return Diagram's name. * @return Diagram element name.
*/ */
std::string name() const { return name_; } std::string name() const { return name_; }
/** /**
* Return the type name of the diagram. * Return the type name of the diagram element.
* *
* @return Diagrams type name. * @return Diagrams element type name.
*/ */
virtual std::string type_name() const { return "__undefined__"; }; virtual std::string type_name() const { return "__undefined__"; };

View File

@@ -22,6 +22,6 @@ namespace clanguml::common::model {
void stylable_element::set_style(const std::string &style) { style_ = style; } void stylable_element::set_style(const std::string &style) { style_ = style; }
std::string stylable_element::style() const { return style_; } std::optional<std::string> stylable_element::style() const { return style_; }
} }

View File

@@ -17,6 +17,7 @@
*/ */
#pragma once #pragma once
#include <optional>
#include <string> #include <string>
namespace clanguml::common::model { namespace clanguml::common::model {
@@ -40,10 +41,10 @@ public:
* *
* @return Style specification * @return Style specification
*/ */
std::string style() const; std::optional<std::string> style() const;
private: private:
std::string style_; std::optional<std::string> style_;
}; };
} // namespace clanguml::common::model } // namespace clanguml::common::model

View File

@@ -168,6 +168,24 @@ std::string to_string(member_order_t mo)
} }
} }
std::optional<std::string> plantuml::get_style(
const common::model::relationship_t relationship_type) const
{
if (style.count(to_string(relationship_type)) == 0)
return {};
return style.at(to_string(relationship_type));
}
std::optional<std::string> plantuml::get_style(
const std::string &element_type) const
{
if (style.count(element_type) == 0)
return {};
return style.at(element_type);
}
void plantuml::append(const plantuml &r) void plantuml::append(const plantuml &r)
{ {
before.insert(before.end(), r.before.begin(), r.before.end()); before.insert(before.end(), r.before.begin(), r.before.end());

View File

@@ -109,6 +109,11 @@ enum class comment_parser_t {
std::string to_string(comment_parser_t cp); std::string to_string(comment_parser_t cp);
struct plantuml_keyword_mapping_t {
std::map<common::model::relationship_t, std::pair<std::string, std::string>>
relationships;
};
/** /**
* @brief PlantUML diagram config section * @brief PlantUML diagram config section
* *
@@ -123,6 +128,13 @@ struct plantuml {
std::vector<std::string> after; std::vector<std::string> after;
/*! Command template to render diagram using PlantUML */ /*! Command template to render diagram using PlantUML */
std::string cmd; std::string cmd;
/*! Provide customized styles for various elements */
std::map<std::string, std::string> style;
std::optional<std::string> get_style(
const common::model::relationship_t relationship_type) const;
std::optional<std::string> get_style(const std::string &element_type) const;
void append(const plantuml &r); void append(const plantuml &r);
}; };

View File

@@ -160,6 +160,7 @@ types:
before: !optional [string] before: !optional [string]
after: !optional [string] after: !optional [string]
cmd: !optional string cmd: !optional string
style: !optional map_t<string;string>
mermaid: !optional mermaid: !optional
before: !optional [string] before: !optional [string]
after: !optional [string] after: !optional [string]

View File

@@ -411,6 +411,9 @@ template <> struct convert<plantuml> {
if (node["cmd"]) if (node["cmd"])
rhs.cmd = node["cmd"].as<decltype(rhs.cmd)>(); rhs.cmd = node["cmd"].as<decltype(rhs.cmd)>();
if (node["style"])
rhs.style = node["style"].as<decltype(rhs.style)>();
return true; return true;
} }
}; };

View File

@@ -53,7 +53,7 @@ void generator::generate_relationships(
}, },
[&f, &ostr, this](const auto &r) { [&f, &ostr, this](const auto &r) {
ostr << f.alias() << " " ostr << f.alias() << " "
<< plantuml_common::to_plantuml(r.type(), r.style()) << " " << plantuml_common::to_plantuml(r, config()) << " "
<< model().get(r.destination()).value().alias() << '\n'; << model().get(r.destination()).value().alias() << '\n';
}); });
} }

View File

@@ -89,8 +89,8 @@ void generator::generate(const package &p, std::ostream &ostr) const
generate_link(ostr, p); generate_link(ostr, p);
} }
if (!p.style().empty()) if (p.style())
ostr << " " << p.style(); ostr << " " << p.style().value();
ostr << " {" << '\n'; ostr << " {" << '\n';
} }

View File

@@ -4,6 +4,11 @@ diagrams:
glob: glob:
- t00009.cc - t00009.cc
using_namespace: clanguml::t00009 using_namespace: clanguml::t00009
plantuml:
style:
instantiation: up
association: up
aggregation: up
include: include:
namespaces: namespaces:
- clanguml::t00009 - clanguml::t00009

View File

@@ -43,15 +43,18 @@ TEST_CASE("t00009", "[test-case][class]")
REQUIRE_THAT( REQUIRE_THAT(
src, (IsField<Public>("avector", "A<std::vector<std::string>> &"))); src, (IsField<Public>("avector", "A<std::vector<std::string>> &")));
REQUIRE_THAT(src, IsInstantiation(_A("A<T>"), _A("A<int>"))); REQUIRE_THAT(src, IsInstantiation(_A("A<T>"), _A("A<int>"), "up"));
REQUIRE_THAT(src, IsInstantiation(_A("A<T>"), _A("A<std::string>")));
REQUIRE_THAT(src, IsAggregation(_A("B"), _A("A<int>"), "+aint"));
REQUIRE_THAT( REQUIRE_THAT(
src, IsAssociation(_A("B"), _A("A<std::string>"), "+astring")); src, IsInstantiation(_A("A<T>"), _A("A<std::string>"), "up"));
REQUIRE_THAT(
src, IsAggregation(_A("B"), _A("A<int>"), "+aint", "", "", "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsAssociation( IsAssociation(
_A("B"), _A("A<std::vector<std::string>>"), "+avector")); _A("B"), _A("A<std::string>"), "+astring", "", "", "up"));
REQUIRE_THAT(src,
IsAssociation(_A("B"), _A("A<std::vector<std::string>>"),
"+avector", "", "", "up"));
save_puml(config.output_directory(), diagram->name + ".puml", src); save_puml(config.output_directory(), diagram->name + ".puml", src);
} }

View File

@@ -4,6 +4,9 @@ diagrams:
glob: glob:
- t00016.cc - t00016.cc
using_namespace: clanguml::t00016 using_namespace: clanguml::t00016
plantuml:
style:
instantiation: up
include: include:
namespaces: namespaces:
- clanguml::t00016 - clanguml::t00016

View File

@@ -41,16 +41,17 @@ TEST_CASE("t00016", "[test-case][class]")
REQUIRE_THAT(src, IsClassTemplate("is_numeric", "float")); REQUIRE_THAT(src, IsClassTemplate("is_numeric", "float"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsInstantiation(_A("is_numeric<typename>"), _A("is_numeric<int>"))); IsInstantiation(
_A("is_numeric<typename>"), _A("is_numeric<int>"), "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsInstantiation( IsInstantiation(
_A("is_numeric<typename>"), _A("is_numeric<bool>"))); _A("is_numeric<typename>"), _A("is_numeric<bool>"), "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsInstantiation( IsInstantiation(
_A("is_numeric<typename>"), _A("is_numeric<char>"))); _A("is_numeric<typename>"), _A("is_numeric<char>"), "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsInstantiation( IsInstantiation(
_A("is_numeric<typename>"), _A("is_numeric<float>"))); _A("is_numeric<typename>"), _A("is_numeric<float>"), "up"));
save_puml(config.output_directory(), diagram->name + ".puml", src); save_puml(config.output_directory(), diagram->name + ".puml", src);
} }

View File

@@ -5,6 +5,9 @@ diagrams:
- "*.cc" - "*.cc"
generate_template_argument_dependencies: false generate_template_argument_dependencies: false
using_namespace: clanguml::t00019 using_namespace: clanguml::t00019
plantuml:
style:
instantiation: up
include: include:
namespaces: namespaces:
- clanguml::t00019 - clanguml::t00019

View File

@@ -41,18 +41,18 @@ TEST_CASE("t00031", "[test-case][class]")
REQUIRE_THAT(src, IsClass(_A("D"))); REQUIRE_THAT(src, IsClass(_A("D")));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsAssociationWithStyle( IsAssociation(
_A("R"), _A("A"), "+aaa", "#red,dashed,thickness=2")); _A("R"), _A("A"), "+aaa", "", "", "[#red,dashed,thickness=2]"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsCompositionWithStyle( IsComposition(_A("R"), _A("B"), "+bbb", "", "",
_A("R"), _A("B"), "+bbb", "#green,dashed,thickness=4")); "[#green,dashed,thickness=4]"));
REQUIRE_THAT(src, IsDependency(_A("R"), _A("B"))); REQUIRE_THAT(src, IsDependency(_A("R"), _A("B")));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsAggregationWithStyle( IsAggregation(_A("R"), _A("C<int>"), "+ccc", "", "",
_A("R"), _A("C<int>"), "+ccc", "#blue,dotted,thickness=8")); "[#blue,dotted,thickness=8]"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsAssociationWithStyle( IsAssociation(_A("R"), _A("D"), "+ddd", "", "",
_A("R"), _A("D"), "+ddd", "#blue,plain,thickness=16")); "[#blue,plain,thickness=16]"));
save_puml(config.output_directory(), diagram->name + ".puml", src); save_puml(config.output_directory(), diagram->name + ".puml", src);
} }

View File

@@ -4,6 +4,9 @@ diagrams:
glob: glob:
- t00033.cc - t00033.cc
using_namespace: clanguml::t00033 using_namespace: clanguml::t00033
plantuml:
style:
instantiation: up
include: include:
namespaces: namespaces:
- clanguml::t00033 - clanguml::t00033

View File

@@ -48,11 +48,12 @@ TEST_CASE("t00033", "[test-case][class]")
src, IsDependency(_A("B<std::unique_ptr<C<D>>>"), _A("C<D>"))); src, IsDependency(_A("B<std::unique_ptr<C<D>>>"), _A("C<D>")));
REQUIRE_THAT(src, IsDependency(_A("C<D>"), _A("D"))); REQUIRE_THAT(src, IsDependency(_A("C<D>"), _A("D")));
REQUIRE_THAT(src, IsInstantiation(_A("C<T>"), _A("C<D>"))); REQUIRE_THAT(src, IsInstantiation(_A("C<T>"), _A("C<D>"), "up"));
REQUIRE_THAT(
src, IsInstantiation(_A("B<T>"), _A("B<std::unique_ptr<C<D>>>")));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsInstantiation(_A("A<T>"), _A("A<B<std::unique_ptr<C<D>>>>"))); IsInstantiation(_A("B<T>"), _A("B<std::unique_ptr<C<D>>>"), "up"));
REQUIRE_THAT(src,
IsInstantiation(
_A("A<T>"), _A("A<B<std::unique_ptr<C<D>>>>"), "up"));
save_puml(config.output_directory(), diagram->name + ".puml", src); save_puml(config.output_directory(), diagram->name + ".puml", src);
} }

View File

@@ -5,6 +5,9 @@ diagrams:
glob: glob:
- t00042.cc - t00042.cc
using_namespace: clanguml::t00042 using_namespace: clanguml::t00042
plantuml:
style:
instantiation: up
include: include:
specializations: specializations:
- clanguml::t00042::A<T> - clanguml::t00042::A<T>

View File

@@ -4,6 +4,9 @@ diagrams:
glob: glob:
- t00047.cc - t00047.cc
using_namespace: clanguml::t00047 using_namespace: clanguml::t00047
plantuml:
style:
instantiation: up
include: include:
namespaces: namespaces:
- clanguml::t00047 - clanguml::t00047

View File

@@ -6,4 +6,11 @@ diagrams:
include: include:
namespaces: namespaces:
- clanguml::t00059 - clanguml::t00059
plantuml:
style:
instantiation: up
association: up
aggregation: up
dependency: up
constraint: up
using_namespace: clanguml::t00059 using_namespace: clanguml::t00059

View File

@@ -40,9 +40,9 @@ TEST_CASE("t00059", "[test-case][class]")
REQUIRE_THAT(src, IsConcept(_A("orange_c<T>"))); REQUIRE_THAT(src, IsConcept(_A("orange_c<T>")));
REQUIRE_THAT( REQUIRE_THAT(
src, IsConstraint(_A("apple_c<T>"), _A("fruit_c<T>"), "T")); src, IsConstraint(_A("apple_c<T>"), _A("fruit_c<T>"), "T", "up"));
REQUIRE_THAT( REQUIRE_THAT(
src, IsConstraint(_A("orange_c<T>"), _A("fruit_c<T>"), "T")); src, IsConstraint(_A("orange_c<T>"), _A("fruit_c<T>"), "T", "up"));
REQUIRE_THAT( REQUIRE_THAT(
src, IsConceptRequirement(_A("apple_c<T>"), "t.get_sweetness()")); src, IsConceptRequirement(_A("apple_c<T>"), "t.get_sweetness()"));
@@ -60,31 +60,33 @@ TEST_CASE("t00059", "[test-case][class]")
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsDependency(_A("fruit_factory<gala_apple,valencia_orange>"), IsDependency(_A("fruit_factory<gala_apple,valencia_orange>"),
_A("gala_apple"))); _A("gala_apple"), "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsDependency(_A("fruit_factory<gala_apple,valencia_orange>"), IsDependency(_A("fruit_factory<gala_apple,valencia_orange>"),
_A("valencia_orange"))); _A("valencia_orange"), "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsDependency(_A("fruit_factory<empire_apple,lima_orange>"), IsDependency(_A("fruit_factory<empire_apple,lima_orange>"),
_A("empire_apple"))); _A("empire_apple"), "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsDependency(_A("fruit_factory<empire_apple,lima_orange>"), IsDependency(_A("fruit_factory<empire_apple,lima_orange>"),
_A("lima_orange"))); _A("lima_orange"), "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsAggregation(_A("R"), IsAggregation(_A("R"),
_A("fruit_factory<gala_apple,valencia_orange>"), "+factory_1")); _A("fruit_factory<gala_apple,valencia_orange>"), "+factory_1",
"", "", "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsAggregation(_A("R"), IsAggregation(_A("R"),
_A("fruit_factory<empire_apple,lima_orange>"), "+factory_2")); _A("fruit_factory<empire_apple,lima_orange>"), "+factory_2", "",
"", "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsInstantiation(_A("fruit_factory<apple_c TA,orange_c TO>"), IsInstantiation(_A("fruit_factory<apple_c TA,orange_c TO>"),
_A("fruit_factory<gala_apple,valencia_orange>"))); _A("fruit_factory<gala_apple,valencia_orange>"), "up"));
REQUIRE_THAT(src, REQUIRE_THAT(src,
IsInstantiation(_A("fruit_factory<apple_c TA,orange_c TO>"), IsInstantiation(_A("fruit_factory<apple_c TA,orange_c TO>"),
_A("fruit_factory<empire_apple,lima_orange>"))); _A("fruit_factory<empire_apple,lima_orange>"), "up"));
save_puml(config.output_directory(), diagram->name + ".puml", src); save_puml(config.output_directory(), diagram->name + ".puml", src);
} }

17
tests/t00075/.clang-uml Normal file
View File

@@ -0,0 +1,17 @@
diagrams:
t00075_class:
type: class
glob:
- t00075.cc
include:
namespaces:
- clanguml::t00075
generate_packages: true
using_namespace: clanguml::t00075
plantuml:
style:
concept: "#line:green;back:lightblue"
class: "#aliceblue;line:blue;line.dotted;text:blue"
package: "#back:grey"
enum: "#line.dotted"
constraint: "up[#green,dashed,thickness=2]"

34
tests/t00075/t00075.cc Normal file
View File

@@ -0,0 +1,34 @@
namespace clanguml {
namespace t00075 {
namespace ns1 {
namespace ns2 {
template <typename T>
concept C = requires(T t) {
T{};
t.e();
};
enum class E { k1, k2 };
struct A {
E e() const { return E::k1; };
};
class B {
public:
E e() const { return E::k2; };
};
template <C T> class ABE {
T a_or_b;
};
struct R {
ABE<A> a;
ABE<B> b;
};
} // namespace ns2
} // namespace ns1
}
}

92
tests/t00075/test_case.h Normal file
View File

@@ -0,0 +1,92 @@
/**
* tests/t00075/test_case.h
*
* Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
TEST_CASE("t00075", "[test-case][class]")
{
auto [config, db] = load_config("t00075");
auto diagram = config.diagrams["t00075_class"];
REQUIRE(diagram->name == "t00075_class");
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00075_class");
{
auto src = generate_class_puml(diagram, *model);
AliasMatcher _A(src);
REQUIRE_THAT(src, StartsWith("@startuml"));
REQUIRE_THAT(src, EndsWith("@enduml\n"));
// Check if all classes exist
REQUIRE_THAT(src, IsClass(_A("A")));
REQUIRE_THAT(src, IsClass(_A("B")));
REQUIRE_THAT(src, IsClass(_A("ABE<ns1::ns2::C T>")));
REQUIRE_THAT(src, IsClass(_A("R")));
REQUIRE_THAT(src, IsEnum(_A("E")));
REQUIRE_THAT(src, IsConcept(_A("C<T>")));
REQUIRE_THAT(src, IsConceptRequirement(_A("C<T>"), "T{}"));
REQUIRE_THAT(src, IsConceptRequirement(_A("C<T>"), "t.e()"));
REQUIRE_THAT(src, IsConceptRequirement(_A("C<T>"), "(T t)"));
REQUIRE_THAT(src, !IsConceptRequirement(_A("C<T>"), "(T ns1::ns2::t)"));
REQUIRE_THAT(src,
IsConstraint(_A("ABE<ns1::ns2::C T>"), _A("C<T>"), "T",
"up[#green,dashed,thickness=2]"));
save_puml(config.output_directory(), diagram->name + ".puml", src);
}
{
auto j = generate_class_json(diagram, *model);
using namespace json;
save_json(config.output_directory(), diagram->name + ".json", j);
}
{
auto src = generate_class_mermaid(diagram, *model);
mermaid::AliasMatcher _A(src);
using mermaid::IsClass;
using mermaid::IsConcept;
using mermaid::IsConceptRequirement;
using mermaid::IsEnum;
// Check if all classes exist
REQUIRE_THAT(src, IsClass(_A("ns1::ns2::A")));
REQUIRE_THAT(src, IsClass(_A("ns1::ns2::B")));
REQUIRE_THAT(src, IsClass(_A("ns1::ns2::ABE<ns1::ns2::C T>")));
REQUIRE_THAT(src, IsClass(_A("ns1::ns2::R")));
REQUIRE_THAT(src, IsEnum(_A("ns1::ns2::E")));
REQUIRE_THAT(src, IsConcept(_A("ns1::ns2::C<T>")));
REQUIRE_THAT(src, IsConceptRequirement(_A("ns1::ns2::C<T>"), "T{}"));
REQUIRE_THAT(src, IsConceptRequirement(_A("ns1::ns2::C<T>"), "t.e()"));
save_mermaid(config.output_directory(), diagram->name + ".mmd", src);
}
}

View File

@@ -417,6 +417,7 @@ using namespace clanguml::test::matchers;
#include "t00073/test_case.h" #include "t00073/test_case.h"
#if defined(ENABLE_CXX_STD_20_TEST_CASES) #if defined(ENABLE_CXX_STD_20_TEST_CASES)
#include "t00074/test_case.h" #include "t00074/test_case.h"
#include "t00075/test_case.h"
#endif #endif
/// ///

View File

@@ -640,14 +640,14 @@ ContainsMatcher IsInnerClass(std::string const &parent,
ContainsMatcher IsAssociation(std::string const &from, std::string const &to, ContainsMatcher IsAssociation(std::string const &from, std::string const &to,
std::string const &label = "", std::string multiplicity_source = "", std::string const &label = "", std::string multiplicity_source = "",
std::string multiplicity_dest = "", std::string multiplicity_dest = "", std::string style = "",
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
{ {
std::string format_string = "{}"; std::string format_string = "{}";
if (!multiplicity_source.empty()) if (!multiplicity_source.empty())
format_string += " \"" + multiplicity_source + "\""; format_string += " \"" + multiplicity_source + "\"";
format_string += " -->"; format_string += fmt::format(" -{}->", style);
if (!multiplicity_dest.empty()) if (!multiplicity_dest.empty())
format_string += " \"" + multiplicity_dest + "\""; format_string += " \"" + multiplicity_dest + "\"";
@@ -668,14 +668,14 @@ ContainsMatcher IsAssociation(std::string const &from, std::string const &to,
ContainsMatcher IsComposition(std::string const &from, std::string const &to, ContainsMatcher IsComposition(std::string const &from, std::string const &to,
std::string const &label, std::string multiplicity_source = "", std::string const &label, std::string multiplicity_source = "",
std::string multiplicity_dest = "", std::string multiplicity_dest = "", std::string style = "",
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
{ {
std::string format_string = "{}"; std::string format_string = "{}";
if (!multiplicity_source.empty()) if (!multiplicity_source.empty())
format_string += " \"" + multiplicity_source + "\""; format_string += " \"" + multiplicity_source + "\"";
format_string += " *--"; format_string += fmt::format(" *-{}-", style);
if (!multiplicity_dest.empty()) if (!multiplicity_dest.empty())
format_string += " \"" + multiplicity_dest + "\""; format_string += " \"" + multiplicity_dest + "\"";
@@ -689,14 +689,14 @@ ContainsMatcher IsComposition(std::string const &from, std::string const &to,
ContainsMatcher IsAggregation(std::string const &from, std::string const &to, ContainsMatcher IsAggregation(std::string const &from, std::string const &to,
std::string const &label, std::string multiplicity_source = "", std::string const &label, std::string multiplicity_source = "",
std::string multiplicity_dest = "", std::string multiplicity_dest = "", std::string style = "",
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
{ {
std::string format_string = "{}"; std::string format_string = "{}";
if (!multiplicity_source.empty()) if (!multiplicity_source.empty())
format_string += " \"" + multiplicity_source + "\""; format_string += " \"" + multiplicity_source + "\"";
format_string += " o--"; format_string += fmt::format(" o-{}-", style);
if (!multiplicity_dest.empty()) if (!multiplicity_dest.empty())
format_string += " \"" + multiplicity_dest + "\""; format_string += " \"" + multiplicity_dest + "\"";
@@ -708,45 +708,20 @@ ContainsMatcher IsAggregation(std::string const &from, std::string const &to,
caseSensitivity)); caseSensitivity));
} }
ContainsMatcher IsAggregationWithStyle(std::string const &from,
std::string const &to, std::string const &label, std::string style,
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
{
return ContainsMatcher(
CasedString(fmt::format("{} o-[{}]- {} : {}", from, style, to, label),
caseSensitivity));
}
ContainsMatcher IsAssociationWithStyle(std::string const &from,
std::string const &to, std::string const &label, std::string style,
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
{
return ContainsMatcher(
CasedString(fmt::format("{} -[{}]-> {} : {}", from, style, to, label),
caseSensitivity));
}
ContainsMatcher IsCompositionWithStyle(std::string const &from,
std::string const &to, std::string const &label, std::string style,
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
{
return ContainsMatcher(
CasedString(fmt::format("{} *-[{}]- {} : {}", from, style, to, label),
caseSensitivity));
}
ContainsMatcher IsInstantiation(std::string const &from, std::string const &to, ContainsMatcher IsInstantiation(std::string const &from, std::string const &to,
std::string style = "",
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
{ {
return ContainsMatcher( return ContainsMatcher(CasedString(
CasedString(fmt::format("{} ..|> {}", to, from), caseSensitivity)); fmt::format("{} .{}.|> {}", to, style, from), caseSensitivity));
} }
ContainsMatcher IsDependency(std::string const &from, std::string const &to, ContainsMatcher IsDependency(std::string const &from, std::string const &to,
std::string style = "",
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
{ {
return ContainsMatcher( return ContainsMatcher(CasedString(
CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity)); fmt::format("{} .{}.> {}", from, style, to), caseSensitivity));
} }
namespace mermaid { namespace mermaid {
@@ -767,15 +742,16 @@ ContainsMatcher IsIncludeDependency(std::string const &from,
} }
ContainsMatcher IsConstraint(std::string const &from, std::string const &to, ContainsMatcher IsConstraint(std::string const &from, std::string const &to,
std::string const &label = {}, std::string const &label = {}, std::string style = "",
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
{ {
if (label.empty()) if (label.empty())
return ContainsMatcher(
CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity));
else
return ContainsMatcher(CasedString( return ContainsMatcher(CasedString(
fmt::format("{} ..> {} : {}", from, to, label), caseSensitivity)); fmt::format("{} .{}.> {}", from, style, to), caseSensitivity));
else
return ContainsMatcher(
CasedString(fmt::format("{} .{}.> {} : {}", from, style, to, label),
caseSensitivity));
} }
ContainsMatcher IsConceptRequirement(std::string const &cpt, ContainsMatcher IsConceptRequirement(std::string const &cpt,

View File

@@ -219,6 +219,9 @@ test_cases:
- name: t00074 - name: t00074
title: Test case for rendering concepts without requirements title: Test case for rendering concepts without requirements
description: description:
- name: t00075
title: Test case for class diagram styles in config file
description:
Sequence diagrams: Sequence diagrams:
- name: t20001 - name: t20001
title: Basic sequence diagram test case title: Basic sequence diagram test case