Added regex support to namespaces filter
This commit is contained in:
@@ -173,7 +173,7 @@ tvl::value_t anyof_filter::match(
|
||||
}
|
||||
|
||||
namespace_filter::namespace_filter(
|
||||
filter_t type, std::vector<namespace_> namespaces)
|
||||
filter_t type, std::vector<config::namespace_or_regex> namespaces)
|
||||
: filter_visitor{type}
|
||||
, namespaces_{std::move(namespaces)}
|
||||
{
|
||||
@@ -185,8 +185,17 @@ tvl::value_t namespace_filter::match(
|
||||
if (ns.is_empty())
|
||||
return {};
|
||||
|
||||
return tvl::any_of(namespaces_.begin(), namespaces_.end(),
|
||||
[&ns](const auto &nsit) { return ns.starts_with(nsit) || ns == nsit; });
|
||||
return tvl::any_of(
|
||||
namespaces_.begin(), namespaces_.end(), [&ns](const auto &nsit) {
|
||||
if (std::holds_alternative<namespace_>(nsit.value())) {
|
||||
const auto &ns_pattern = std::get<namespace_>(nsit.value());
|
||||
return ns.starts_with(ns_pattern) || ns == ns_pattern;
|
||||
}
|
||||
else {
|
||||
const auto ®ex = std::get<config::regex>(nsit.value());
|
||||
return regex == ns.to_string();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tvl::value_t namespace_filter::match(
|
||||
@@ -195,25 +204,45 @@ tvl::value_t namespace_filter::match(
|
||||
if (dynamic_cast<const package *>(&e) != nullptr) {
|
||||
return tvl::any_of(namespaces_.begin(), namespaces_.end(),
|
||||
[&e, is_inclusive = is_inclusive()](const auto &nsit) {
|
||||
if (std::holds_alternative<namespace_>(nsit.value())) {
|
||||
const auto &ns_pattern = std::get<namespace_>(nsit.value());
|
||||
|
||||
auto element_full_name_starts_with_namespace =
|
||||
(e.get_namespace() | e.name()).starts_with(nsit);
|
||||
(e.get_namespace() | e.name()).starts_with(ns_pattern);
|
||||
|
||||
auto element_full_name_equals_pattern =
|
||||
(e.get_namespace() | e.name()) == nsit;
|
||||
(e.get_namespace() | e.name()) == ns_pattern;
|
||||
|
||||
auto namespace_starts_with_element_qualified_name =
|
||||
nsit.starts_with(e.get_namespace());
|
||||
ns_pattern.starts_with(e.get_namespace());
|
||||
|
||||
auto result = element_full_name_starts_with_namespace ||
|
||||
element_full_name_equals_pattern;
|
||||
|
||||
if (is_inclusive)
|
||||
result =
|
||||
result || namespace_starts_with_element_qualified_name;
|
||||
result = result ||
|
||||
namespace_starts_with_element_qualified_name;
|
||||
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return std::get<config::regex>(nsit.value()) ==
|
||||
e.full_name(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
return tvl::any_of(namespaces_.begin(), namespaces_.end(),
|
||||
[&e](const auto &nsit) { return e.get_namespace().starts_with(nsit); });
|
||||
|
||||
return tvl::any_of(
|
||||
namespaces_.begin(), namespaces_.end(), [&e](const auto &nsit) {
|
||||
if (std::holds_alternative<namespace_>(nsit.value())) {
|
||||
return e.get_namespace().starts_with(
|
||||
std::get<namespace_>(nsit.value()));
|
||||
}
|
||||
else {
|
||||
return std::get<config::regex>(nsit.value()) ==
|
||||
e.full_name(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
element_filter::element_filter(
|
||||
@@ -524,7 +553,8 @@ tvl::value_t paths_filter::match(
|
||||
|
||||
auto sl_path = std::filesystem::path{p.file()};
|
||||
|
||||
// Matching source paths doesn't make sens if they are not absolute or empty
|
||||
// Matching source paths doesn't make sens if they are not absolute or
|
||||
// empty
|
||||
if (p.file().empty() || sl_path.is_relative()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -112,7 +112,8 @@ private:
|
||||
};
|
||||
|
||||
struct namespace_filter : public filter_visitor {
|
||||
namespace_filter(filter_t type, std::vector<namespace_> namespaces);
|
||||
namespace_filter(
|
||||
filter_t type, std::vector<config::namespace_or_regex> namespaces);
|
||||
|
||||
~namespace_filter() override = default;
|
||||
|
||||
@@ -121,7 +122,7 @@ struct namespace_filter : public filter_visitor {
|
||||
tvl::value_t match(const diagram &d, const element &e) const override;
|
||||
|
||||
private:
|
||||
std::vector<namespace_> namespaces_;
|
||||
std::vector<config::namespace_or_regex> namespaces_;
|
||||
};
|
||||
|
||||
struct element_filter : public filter_visitor {
|
||||
|
||||
@@ -48,7 +48,7 @@ struct regex {
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const std::string &v)
|
||||
[[nodiscard]] bool operator==(const std::string &v) const
|
||||
{
|
||||
return std::regex_match(v, regexp);
|
||||
}
|
||||
@@ -108,7 +108,7 @@ using string_or_regex = or_regex<std::string>;
|
||||
|
||||
std::string to_string(string_or_regex sr);
|
||||
|
||||
using namespace_or_regex = std::variant<common::model::namespace_, regex>;
|
||||
using namespace_or_regex = or_regex<common::model::namespace_>;
|
||||
|
||||
enum class method_arguments { full, abbreviated, none };
|
||||
|
||||
@@ -148,7 +148,7 @@ struct diagram_template {
|
||||
};
|
||||
|
||||
struct filter {
|
||||
std::vector<common::model::namespace_> namespaces;
|
||||
std::vector<namespace_or_regex> namespaces;
|
||||
|
||||
std::vector<string_or_regex> elements;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ using clanguml::config::location_t;
|
||||
using clanguml::config::member_order_t;
|
||||
using clanguml::config::method_arguments;
|
||||
using clanguml::config::method_type;
|
||||
using clanguml::config::namespace_or_regex;
|
||||
using clanguml::config::package_diagram;
|
||||
using clanguml::config::package_type_t;
|
||||
using clanguml::config::plantuml;
|
||||
@@ -360,6 +361,23 @@ template <> struct convert<string_or_regex> {
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct convert<namespace_or_regex> {
|
||||
static bool decode(const Node &node, namespace_or_regex &rhs)
|
||||
{
|
||||
using namespace std::string_literals;
|
||||
if (node.IsMap()) {
|
||||
auto pattern = node["r"].as<std::string>();
|
||||
auto rx = std::regex(pattern);
|
||||
rhs = namespace_or_regex{std::move(rx), std::move(pattern)};
|
||||
}
|
||||
else {
|
||||
rhs = namespace_or_regex{node.as<std::string>()};
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// filter Yaml decoder
|
||||
//
|
||||
@@ -368,7 +386,7 @@ template <> struct convert<filter> {
|
||||
{
|
||||
if (node["namespaces"]) {
|
||||
auto namespace_list =
|
||||
node["namespaces"].as<std::vector<std::string>>();
|
||||
node["namespaces"].as<decltype(rhs.namespaces)>();
|
||||
for (const auto &ns : namespace_list)
|
||||
rhs.namespaces.push_back({ns});
|
||||
}
|
||||
|
||||
@@ -65,6 +65,19 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const string_or_regex &m)
|
||||
return out;
|
||||
}
|
||||
|
||||
YAML::Emitter &operator<<(YAML::Emitter &out, const namespace_or_regex &m)
|
||||
{
|
||||
if (std::holds_alternative<common::model::namespace_>(m.value())) {
|
||||
out << std::get<common::model::namespace_>(m.value());
|
||||
}
|
||||
else {
|
||||
out << YAML::Key << "r" << YAML::Value
|
||||
<< std::get<regex>(m.value()).pattern;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f)
|
||||
{
|
||||
out << YAML::BeginMap;
|
||||
|
||||
@@ -25,9 +25,6 @@ TEST_CASE("t00002", "[test-case][class]")
|
||||
REQUIRE(diagram->name == "t00002_class");
|
||||
|
||||
REQUIRE(diagram->include().namespaces.size() == 1);
|
||||
REQUIRE_THAT(diagram->include().namespaces,
|
||||
VectorContains(
|
||||
clanguml::common::model::namespace_{"clanguml::t00002"}));
|
||||
|
||||
REQUIRE(diagram->exclude().namespaces.size() == 0);
|
||||
|
||||
|
||||
@@ -40,3 +40,12 @@ diagrams:
|
||||
exclude:
|
||||
elements:
|
||||
- ns1::ns2::ClassZ
|
||||
regex_namespace_test:
|
||||
type: class
|
||||
include:
|
||||
namespaces:
|
||||
- ns1::ns2
|
||||
- r: '.*interface.*'
|
||||
exclude:
|
||||
namespaces:
|
||||
- r: '.*detail.*'
|
||||
@@ -145,3 +145,61 @@ TEST_CASE("Test elements regexp filter", "[unit-test]")
|
||||
|
||||
CHECK(filter.should_include(c));
|
||||
}
|
||||
|
||||
TEST_CASE("Test namespaces regexp filter", "[unit-test]")
|
||||
{
|
||||
using clanguml::class_diagram::model::class_method;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::diagram_filter;
|
||||
using clanguml::common::model::namespace_;
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::source_file;
|
||||
|
||||
using clanguml::class_diagram::model::class_;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["regex_namespace_test"];
|
||||
clanguml::class_diagram::model::diagram diagram;
|
||||
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
class_ c{{}};
|
||||
|
||||
c.set_namespace(namespace_{"ns1::ns2"});
|
||||
c.set_name("ClassA");
|
||||
|
||||
CHECK(filter.should_include(c));
|
||||
|
||||
c.set_namespace(namespace_{"ns1::ns2::detail"});
|
||||
c.set_name("ClassAImpl");
|
||||
|
||||
CHECK(!filter.should_include(c));
|
||||
|
||||
c.set_namespace(namespace_{"ns1::interface"});
|
||||
c.set_name("IClassA");
|
||||
|
||||
CHECK(filter.should_include(c));
|
||||
|
||||
CHECK(!filter.should_include(namespace_{"ns1"}));
|
||||
CHECK(filter.should_include(namespace_{"ns1::ns2"}));
|
||||
CHECK(!filter.should_include(namespace_{"ns1::ns2::detail"}));
|
||||
CHECK(filter.should_include(namespace_{"ns1::interface"}));
|
||||
|
||||
package p{{}};
|
||||
|
||||
p.set_namespace({"ns1"});
|
||||
p.set_name("ns2");
|
||||
|
||||
CHECK(filter.should_include(p));
|
||||
|
||||
p.set_namespace({"ns1::ns2"});
|
||||
p.set_name("detail");
|
||||
|
||||
CHECK(!filter.should_include(p));
|
||||
|
||||
p.set_namespace({"ns1"});
|
||||
p.set_name("interface");
|
||||
|
||||
CHECK(filter.should_include(p));
|
||||
}
|
||||
Reference in New Issue
Block a user