Added module_access diagram filter (#101)
This commit is contained in:
@@ -23,6 +23,14 @@
|
||||
namespace clanguml::class_diagram::model {
|
||||
using nlohmann::json;
|
||||
|
||||
void set_module(nlohmann::json &j, const common::model::element &e)
|
||||
{
|
||||
if (e.module()) {
|
||||
j["module"]["name"] = e.module().value();
|
||||
j["module"]["is_private"] = e.module_private();
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const class_element &c)
|
||||
{
|
||||
j["name"] = c.name();
|
||||
@@ -94,6 +102,8 @@ void to_json(nlohmann::json &j, const class_ &c)
|
||||
j["methods"] = c.methods();
|
||||
j["bases"] = c.parents();
|
||||
|
||||
set_module(j, c);
|
||||
|
||||
j["template_parameters"] = c.template_params();
|
||||
}
|
||||
|
||||
@@ -102,6 +112,8 @@ void to_json(nlohmann::json &j, const enum_ &c)
|
||||
j = dynamic_cast<const common::model::element &>(c);
|
||||
j["is_nested"] = c.is_nested();
|
||||
j["constants"] = c.constants();
|
||||
|
||||
set_module(j, c);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const concept_ &c)
|
||||
@@ -109,6 +121,8 @@ void to_json(nlohmann::json &j, const concept_ &c)
|
||||
j = dynamic_cast<const common::model::element &>(c);
|
||||
j["parameters"] = c.requires_parameters();
|
||||
j["statements"] = c.requires_statements();
|
||||
|
||||
set_module(j, c);
|
||||
}
|
||||
|
||||
} // namespace clanguml::class_diagram::model
|
||||
|
||||
@@ -568,6 +568,31 @@ tvl::value_t access_filter::match(
|
||||
[&a](const auto &access) { return a == access; });
|
||||
}
|
||||
|
||||
module_access_filter::module_access_filter(
|
||||
filter_t type, std::vector<module_access_t> access)
|
||||
: filter_visitor{type}
|
||||
, access_{std::move(access)}
|
||||
{
|
||||
}
|
||||
|
||||
tvl::value_t module_access_filter::match(
|
||||
const diagram & /*d*/, const element &e) const
|
||||
{
|
||||
if (!e.module().has_value())
|
||||
return {};
|
||||
|
||||
if (access_.empty())
|
||||
return {};
|
||||
|
||||
return tvl::any_of(
|
||||
access_.begin(), access_.end(), [&e](const auto &access) {
|
||||
if (access == module_access_t::kPublic)
|
||||
return !e.module_private();
|
||||
else
|
||||
return e.module_private();
|
||||
});
|
||||
}
|
||||
|
||||
context_filter::context_filter(
|
||||
filter_t type, std::vector<config::context_config> context)
|
||||
: filter_visitor{type}
|
||||
@@ -924,6 +949,9 @@ void diagram_filter::init_filters(const config::diagram &c)
|
||||
add_inclusive_filter(std::make_unique<modules_filter>(
|
||||
filter_t::kInclusive, c.include().modules));
|
||||
|
||||
add_inclusive_filter(std::make_unique<module_access_filter>(
|
||||
filter_t::kInclusive, c.include().module_access));
|
||||
|
||||
add_inclusive_filter(std::make_unique<relationship_filter>(
|
||||
filter_t::kInclusive, c.include().relationships));
|
||||
|
||||
@@ -1037,6 +1065,9 @@ void diagram_filter::init_filters(const config::diagram &c)
|
||||
add_exclusive_filter(std::make_unique<modules_filter>(
|
||||
filter_t::kExclusive, c.exclude().modules));
|
||||
|
||||
add_exclusive_filter(std::make_unique<module_access_filter>(
|
||||
filter_t::kExclusive, c.exclude().module_access));
|
||||
|
||||
add_exclusive_filter(std::make_unique<paths_filter>(
|
||||
filter_t::kExclusive, c.root_directory(), c.exclude().paths));
|
||||
|
||||
|
||||
@@ -457,6 +457,20 @@ private:
|
||||
std::vector<access_t> access_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Match diagram elements based on module access (public or private).
|
||||
*/
|
||||
struct module_access_filter : public filter_visitor {
|
||||
module_access_filter(filter_t type, std::vector<module_access_t> access);
|
||||
|
||||
~module_access_filter() override = default;
|
||||
|
||||
tvl::value_t match(const diagram &d, const element &a) const override;
|
||||
|
||||
private:
|
||||
std::vector<module_access_t> access_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Match diagram elements which are in within a 'radius' distance relationship
|
||||
* to any of the elements specified in context.
|
||||
|
||||
@@ -101,6 +101,23 @@ public:
|
||||
*/
|
||||
std::optional<std::string> module() const { return module_; }
|
||||
|
||||
/**
|
||||
* Set whether the element is in a private module
|
||||
*
|
||||
* @param module C++20 module.
|
||||
*/
|
||||
void set_module_private(const bool module_private)
|
||||
{
|
||||
module_private_ = module_private;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the element is in a private module.
|
||||
*
|
||||
* @return C++20 module.
|
||||
*/
|
||||
bool module_private() const { return module_private_; }
|
||||
|
||||
/**
|
||||
* Return elements full name.
|
||||
*
|
||||
@@ -135,5 +152,6 @@ private:
|
||||
namespace_ ns_;
|
||||
namespace_ using_namespace_;
|
||||
std::optional<std::string> module_;
|
||||
bool module_private_{false};
|
||||
};
|
||||
} // namespace clanguml::common::model
|
||||
|
||||
@@ -70,6 +70,19 @@ std::string to_string(access_t a)
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(module_access_t a)
|
||||
{
|
||||
switch (a) {
|
||||
case module_access_t::kPublic:
|
||||
return "public";
|
||||
case module_access_t::kPrivate:
|
||||
return "private";
|
||||
default:
|
||||
assert(false);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(message_t r)
|
||||
{
|
||||
switch (r) {
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace clanguml::common::model {
|
||||
|
||||
enum class diagram_t { kClass, kSequence, kPackage, kInclude };
|
||||
|
||||
enum class module_access_t { kPublic, kPrivate };
|
||||
enum class access_t { kPublic, kProtected, kPrivate, kNone };
|
||||
|
||||
enum class relationship_t {
|
||||
@@ -77,6 +78,8 @@ std::string to_string(relationship_t r);
|
||||
|
||||
std::string to_string(access_t r);
|
||||
|
||||
std::string to_string(module_access_t r);
|
||||
|
||||
std::string to_string(message_t m);
|
||||
|
||||
std::string to_string(diagram_t r);
|
||||
|
||||
@@ -168,7 +168,13 @@ void translation_unit_visitor::set_owning_module(
|
||||
{
|
||||
if (const clang::Module *module = decl.getOwningModule();
|
||||
module != nullptr) {
|
||||
element.set_module(module->Name);
|
||||
std::string module_name = module->Name;
|
||||
if (module->isPrivateModule()) {
|
||||
// Clang just maps private modules names to "<private>"
|
||||
module_name = module->getTopLevelModule()->Name;
|
||||
}
|
||||
element.set_module(module_name);
|
||||
element.set_module_private(module->isPrivateModule());
|
||||
}
|
||||
}
|
||||
} // namespace clanguml::common::visitor
|
||||
@@ -194,6 +194,22 @@ struct filter {
|
||||
*/
|
||||
std::vector<common::string_or_regex> modules;
|
||||
|
||||
/*! @brief Access type filter
|
||||
*
|
||||
* This filter allows to filter class members methods based on their access:
|
||||
* - public
|
||||
* - private
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```yaml
|
||||
* include:
|
||||
* module_access:
|
||||
* - public
|
||||
* ```
|
||||
*/
|
||||
std::vector<common::model::module_access_t> module_access;
|
||||
|
||||
/*! @brief Elements filter
|
||||
*
|
||||
* Example:
|
||||
@@ -245,8 +261,8 @@ struct filter {
|
||||
*
|
||||
* ```yaml
|
||||
* include:
|
||||
* relationships:
|
||||
* - inheritance
|
||||
* access:
|
||||
* - public
|
||||
* ```
|
||||
*/
|
||||
std::vector<common::model::access_t> access;
|
||||
|
||||
@@ -91,6 +91,9 @@ types:
|
||||
- public
|
||||
- protected
|
||||
- private
|
||||
module_access_filter_t: !variant
|
||||
- public
|
||||
- private
|
||||
method_type_filter_t: !variant
|
||||
- constructor
|
||||
- destructor
|
||||
@@ -123,6 +126,7 @@ types:
|
||||
element_types: !optional [element_types_filter_t]
|
||||
relationships: !optional [relationship_filter_t]
|
||||
access: !optional [access_filter_t]
|
||||
module_access: !optional [module_access_filter_t]
|
||||
subclasses: !optional [regex_or_string_t]
|
||||
parents: !optional [regex_or_string_t]
|
||||
specializations: !optional [regex_or_string_t]
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace YAML {
|
||||
using clanguml::common::namespace_or_regex;
|
||||
using clanguml::common::string_or_regex;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::module_access_t;
|
||||
using clanguml::common::model::relationship_t;
|
||||
using clanguml::config::callee_type;
|
||||
using clanguml::config::class_diagram;
|
||||
@@ -241,6 +242,23 @@ template <> struct convert<access_t> {
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// config module_access_t decoder
|
||||
//
|
||||
template <> struct convert<module_access_t> {
|
||||
static bool decode(const Node &node, module_access_t &rhs)
|
||||
{
|
||||
if (node.as<std::string>() == "public")
|
||||
rhs = module_access_t::kPublic;
|
||||
else if (node.as<std::string>() == "private")
|
||||
rhs = module_access_t::kPrivate;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// config method_type decoder
|
||||
//
|
||||
@@ -483,6 +501,10 @@ template <> struct convert<filter> {
|
||||
rhs.modules.push_back({ns});
|
||||
}
|
||||
|
||||
if (node["module_access"])
|
||||
rhs.module_access =
|
||||
node["module_access"].as<decltype(rhs.module_access)>();
|
||||
|
||||
if (node["relationships"])
|
||||
rhs.relationships =
|
||||
node["relationships"].as<decltype(rhs.relationships)>();
|
||||
|
||||
@@ -69,6 +69,12 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const access_t &a)
|
||||
return out;
|
||||
}
|
||||
|
||||
YAML::Emitter &operator<<(YAML::Emitter &out, const module_access_t &a)
|
||||
{
|
||||
out << to_string(a);
|
||||
return out;
|
||||
}
|
||||
|
||||
YAML::Emitter &operator<<(YAML::Emitter &out, const diagram_t &d)
|
||||
{
|
||||
out << to_string(d);
|
||||
@@ -124,6 +130,8 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f)
|
||||
out << YAML::Key << "namespaces" << YAML::Value << f.namespaces;
|
||||
if (!f.modules.empty())
|
||||
out << YAML::Key << "modules" << YAML::Value << f.modules;
|
||||
if (!f.module_access.empty())
|
||||
out << YAML::Key << "module_access" << YAML::Value << f.module_access;
|
||||
if (!f.access.empty())
|
||||
out << YAML::Key << "access" << YAML::Value << f.access;
|
||||
if (!f.context.empty())
|
||||
|
||||
@@ -9,4 +9,6 @@ diagrams:
|
||||
exclude:
|
||||
modules:
|
||||
- t00070.lib2
|
||||
module_access:
|
||||
- private
|
||||
using_namespace: clanguml::t00070
|
||||
@@ -8,4 +8,9 @@ template <typename T> class BB {
|
||||
};
|
||||
|
||||
enum class BBB { bbb1, bbb2 };
|
||||
}
|
||||
|
||||
module :private;
|
||||
namespace clanguml::t00070 {
|
||||
class BBBB { };
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import t00070;
|
||||
import t00070.lib1;
|
||||
import t00070.lib2;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ TEST_CASE("t00070", "[test-case][class]")
|
||||
REQUIRE_THAT(src, StartsWith("@startuml"));
|
||||
REQUIRE_THAT(src, EndsWith("@enduml\n"));
|
||||
|
||||
REQUIRE_THAT(src, !IsClass(_A("A")));
|
||||
REQUIRE_THAT(src, IsClass(_A("A")));
|
||||
REQUIRE_THAT(src, IsClass(_A("B")));
|
||||
REQUIRE_THAT(src, !IsClass(_A("C")));
|
||||
|
||||
@@ -43,6 +43,7 @@ TEST_CASE("t00070", "[test-case][class]")
|
||||
REQUIRE_THAT(src, !IsClassTemplate("CC", "T"));
|
||||
|
||||
REQUIRE_THAT(src, IsEnum(_A("BBB")));
|
||||
REQUIRE_THAT(src, !IsClass(_A("BBBB")));
|
||||
REQUIRE_THAT(src, !IsEnum(_A("CCC")));
|
||||
|
||||
save_puml(config.output_directory(), diagram->name + ".puml", src);
|
||||
@@ -53,6 +54,15 @@ TEST_CASE("t00070", "[test-case][class]")
|
||||
|
||||
using namespace json;
|
||||
|
||||
REQUIRE(IsClass(j, "A"));
|
||||
REQUIRE(IsClass(j, "B"));
|
||||
REQUIRE(!IsClass(j, "C"));
|
||||
|
||||
REQUIRE(InPublicModule(j, "A", "t00070"));
|
||||
REQUIRE(InPublicModule(j, "B", "t00070.lib1"));
|
||||
|
||||
REQUIRE(!IsClass(j, "BBBB"));
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
@@ -63,7 +73,7 @@ TEST_CASE("t00070", "[test-case][class]")
|
||||
using mermaid::IsClass;
|
||||
using mermaid::IsEnum;
|
||||
|
||||
REQUIRE_THAT(src, !IsClass(_A("A")));
|
||||
REQUIRE_THAT(src, IsClass(_A("A")));
|
||||
REQUIRE_THAT(src, IsClass(_A("B")));
|
||||
REQUIRE_THAT(src, !IsClass(_A("C")));
|
||||
|
||||
@@ -71,6 +81,7 @@ TEST_CASE("t00070", "[test-case][class]")
|
||||
REQUIRE_THAT(src, !IsClass(_A("CC<T>")));
|
||||
|
||||
REQUIRE_THAT(src, IsEnum(_A("BBB")));
|
||||
REQUIRE_THAT(src, !IsClass(_A("BBBB")));
|
||||
REQUIRE_THAT(src, !IsEnum(_A("CCC")));
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", src);
|
||||
|
||||
@@ -1278,6 +1278,22 @@ bool IsClass(const nlohmann::json &j, const std::string &name)
|
||||
return e && e->at("type") == "class";
|
||||
}
|
||||
|
||||
bool InPublicModule(const nlohmann::json &j, const std::string &element,
|
||||
const std::string &module)
|
||||
{
|
||||
auto e = get_element(j, expand_name(j, element));
|
||||
return e && e->contains("module") && e->at("module")["name"] == module &&
|
||||
!e->at("module")["is_private"];
|
||||
}
|
||||
|
||||
bool InPrivateModule(const nlohmann::json &j, const std::string &element,
|
||||
const std::string &module)
|
||||
{
|
||||
auto e = get_element(j, expand_name(j, element));
|
||||
return e && e->contains("module") && e->at("module")["name"] == module &&
|
||||
e->at("module")["is_private"];
|
||||
}
|
||||
|
||||
bool IsAbstractClass(const nlohmann::json &j, const std::string &name)
|
||||
{
|
||||
auto e = get_element(j, expand_name(j, name));
|
||||
|
||||
Reference in New Issue
Block a user