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(
|
namespace_filter::namespace_filter(
|
||||||
filter_t type, std::vector<namespace_> namespaces)
|
filter_t type, std::vector<config::namespace_or_regex> namespaces)
|
||||||
: filter_visitor{type}
|
: filter_visitor{type}
|
||||||
, namespaces_{std::move(namespaces)}
|
, namespaces_{std::move(namespaces)}
|
||||||
{
|
{
|
||||||
@@ -185,8 +185,17 @@ tvl::value_t namespace_filter::match(
|
|||||||
if (ns.is_empty())
|
if (ns.is_empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return tvl::any_of(namespaces_.begin(), namespaces_.end(),
|
return tvl::any_of(
|
||||||
[&ns](const auto &nsit) { return ns.starts_with(nsit) || ns == nsit; });
|
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(
|
tvl::value_t namespace_filter::match(
|
||||||
@@ -195,25 +204,45 @@ tvl::value_t namespace_filter::match(
|
|||||||
if (dynamic_cast<const package *>(&e) != nullptr) {
|
if (dynamic_cast<const package *>(&e) != nullptr) {
|
||||||
return tvl::any_of(namespaces_.begin(), namespaces_.end(),
|
return tvl::any_of(namespaces_.begin(), namespaces_.end(),
|
||||||
[&e, is_inclusive = is_inclusive()](const auto &nsit) {
|
[&e, is_inclusive = is_inclusive()](const auto &nsit) {
|
||||||
auto element_full_name_starts_with_namespace =
|
if (std::holds_alternative<namespace_>(nsit.value())) {
|
||||||
(e.get_namespace() | e.name()).starts_with(nsit);
|
const auto &ns_pattern = std::get<namespace_>(nsit.value());
|
||||||
auto element_full_name_equals_pattern =
|
|
||||||
(e.get_namespace() | e.name()) == nsit;
|
|
||||||
auto namespace_starts_with_element_qualified_name =
|
|
||||||
nsit.starts_with(e.get_namespace());
|
|
||||||
|
|
||||||
auto result = element_full_name_starts_with_namespace ||
|
auto element_full_name_starts_with_namespace =
|
||||||
element_full_name_equals_pattern;
|
(e.get_namespace() | e.name()).starts_with(ns_pattern);
|
||||||
|
|
||||||
if (is_inclusive)
|
auto element_full_name_equals_pattern =
|
||||||
result =
|
(e.get_namespace() | e.name()) == ns_pattern;
|
||||||
result || namespace_starts_with_element_qualified_name;
|
|
||||||
|
|
||||||
return result;
|
auto namespace_starts_with_element_qualified_name =
|
||||||
|
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;
|
||||||
|
|
||||||
|
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(
|
element_filter::element_filter(
|
||||||
@@ -524,7 +553,8 @@ tvl::value_t paths_filter::match(
|
|||||||
|
|
||||||
auto sl_path = std::filesystem::path{p.file()};
|
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()) {
|
if (p.file().empty() || sl_path.is_relative()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,8 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct namespace_filter : public filter_visitor {
|
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;
|
~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;
|
tvl::value_t match(const diagram &d, const element &e) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<namespace_> namespaces_;
|
std::vector<config::namespace_or_regex> namespaces_;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct element_filter : public filter_visitor {
|
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);
|
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);
|
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 };
|
enum class method_arguments { full, abbreviated, none };
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ struct diagram_template {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct filter {
|
struct filter {
|
||||||
std::vector<common::model::namespace_> namespaces;
|
std::vector<namespace_or_regex> namespaces;
|
||||||
|
|
||||||
std::vector<string_or_regex> elements;
|
std::vector<string_or_regex> elements;
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ using clanguml::config::location_t;
|
|||||||
using clanguml::config::member_order_t;
|
using clanguml::config::member_order_t;
|
||||||
using clanguml::config::method_arguments;
|
using clanguml::config::method_arguments;
|
||||||
using clanguml::config::method_type;
|
using clanguml::config::method_type;
|
||||||
|
using clanguml::config::namespace_or_regex;
|
||||||
using clanguml::config::package_diagram;
|
using clanguml::config::package_diagram;
|
||||||
using clanguml::config::package_type_t;
|
using clanguml::config::package_type_t;
|
||||||
using clanguml::config::plantuml;
|
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
|
// filter Yaml decoder
|
||||||
//
|
//
|
||||||
@@ -368,7 +386,7 @@ template <> struct convert<filter> {
|
|||||||
{
|
{
|
||||||
if (node["namespaces"]) {
|
if (node["namespaces"]) {
|
||||||
auto namespace_list =
|
auto namespace_list =
|
||||||
node["namespaces"].as<std::vector<std::string>>();
|
node["namespaces"].as<decltype(rhs.namespaces)>();
|
||||||
for (const auto &ns : namespace_list)
|
for (const auto &ns : namespace_list)
|
||||||
rhs.namespaces.push_back({ns});
|
rhs.namespaces.push_back({ns});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,19 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const string_or_regex &m)
|
|||||||
return out;
|
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)
|
YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f)
|
||||||
{
|
{
|
||||||
out << YAML::BeginMap;
|
out << YAML::BeginMap;
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ TEST_CASE("t00002", "[test-case][class]")
|
|||||||
REQUIRE(diagram->name == "t00002_class");
|
REQUIRE(diagram->name == "t00002_class");
|
||||||
|
|
||||||
REQUIRE(diagram->include().namespaces.size() == 1);
|
REQUIRE(diagram->include().namespaces.size() == 1);
|
||||||
REQUIRE_THAT(diagram->include().namespaces,
|
|
||||||
VectorContains(
|
|
||||||
clanguml::common::model::namespace_{"clanguml::t00002"}));
|
|
||||||
|
|
||||||
REQUIRE(diagram->exclude().namespaces.size() == 0);
|
REQUIRE(diagram->exclude().namespaces.size() == 0);
|
||||||
|
|
||||||
|
|||||||
@@ -39,4 +39,13 @@ diagrams:
|
|||||||
- r: 'ns1::.+::ns3::.+'
|
- r: 'ns1::.+::ns3::.+'
|
||||||
exclude:
|
exclude:
|
||||||
elements:
|
elements:
|
||||||
- ns1::ns2::ClassZ
|
- ns1::ns2::ClassZ
|
||||||
|
regex_namespace_test:
|
||||||
|
type: class
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- ns1::ns2
|
||||||
|
- r: '.*interface.*'
|
||||||
|
exclude:
|
||||||
|
namespaces:
|
||||||
|
- r: '.*detail.*'
|
||||||
@@ -144,4 +144,62 @@ TEST_CASE("Test elements regexp filter", "[unit-test]")
|
|||||||
c.set_name("ClassA");
|
c.set_name("ClassA");
|
||||||
|
|
||||||
CHECK(filter.should_include(c));
|
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