diff --git a/src/common/model/filters/diagram_filter.cc b/src/common/model/filters/diagram_filter.cc index 6df729589..3ce21b9ea 100644 --- a/src/common/model/filters/diagram_filter.cc +++ b/src/common/model/filters/diagram_filter.cc @@ -449,7 +449,7 @@ tvl::value_t modules_filter::match( } element_filter::element_filter( - filter_t type, std::vector elements) + filter_t type, std::vector elements) : filter_visitor{type} , elements_{std::move(elements)} { @@ -463,8 +463,14 @@ tvl::value_t element_filter::match(const diagram &d, const element &e) const return tvl::any_of( elements_.begin(), elements_.end(), [&e](const auto &el) { - return ((el == e.full_name(false)) || - (el == fmt::format("::{}", e.full_name(false)))); + // First check if elements type matches the filter + if ((el.type != config::element_filter_t::filtered_type::any) && + (config::to_string(el.type) != e.type_name())) { + return false; + } + + return ((el.name == e.full_name(false)) || + (el.name == fmt::format("::{}", e.full_name(false)))); }); } @@ -481,19 +487,24 @@ tvl::value_t element_filter::match( dynamic_cast(d); return tvl::any_of(elements_.begin(), elements_.end(), [&sequence_model, &p](const auto &el) { - if (p.type_name() == "method") { + // First check if elements type matches the filter + if (el.type != config::element_filter_t::filtered_type::any && + config::to_string(el.type) != p.type_name()) { + return false; + } + if (p.type_name() == "method") { const auto &m = dynamic_cast(p); const auto class_id = m.class_id(); const auto &class_participant = sequence_model.get_participant(class_id) .value(); - return el == p.full_name(false) || - el == class_participant.full_name(false); + return el.name == p.full_name(false) || + el.name == class_participant.full_name(false); } - return el == p.full_name(false); + return el.name == p.full_name(false); }); } @@ -1084,4 +1095,4 @@ bool diagram_filter::should_include(const std::string &name) const return should_include(ns, n); } -} // namespace clanguml::common::model +} // namespace clanguml::common::model \ No newline at end of file diff --git a/src/common/model/filters/diagram_filter.h b/src/common/model/filters/diagram_filter.h index b13dea876..d2008dc9b 100644 --- a/src/common/model/filters/diagram_filter.h +++ b/src/common/model/filters/diagram_filter.h @@ -265,7 +265,7 @@ struct modules_filter : public filter_visitor { */ struct element_filter : public filter_visitor { element_filter( - filter_t type, std::vector elements); + filter_t type, std::vector elements); ~element_filter() override = default; @@ -275,7 +275,7 @@ struct element_filter : public filter_visitor { const sequence_diagram::model::participant &p) const override; private: - std::vector elements_; + std::vector elements_; }; /** diff --git a/src/config/config.cc b/src/config/config.cc index d20a96441..bfd5a054d 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -47,6 +47,29 @@ std::string to_string(const hint_t t) } } +std::string to_string(element_filter_t::filtered_type ft) +{ + switch (ft) { + case element_filter_t::filtered_type::any: + return "any"; + case element_filter_t::filtered_type::class_: + return "class"; + case element_filter_t::filtered_type::function: + return "function"; + case element_filter_t::filtered_type::method: + return "method"; + case element_filter_t::filtered_type::enum_: + return "enum"; + case element_filter_t::filtered_type::concept_: + return "concept_"; + case element_filter_t::filtered_type::package: + return "package"; + default: + assert(false); + return ""; + } +} + std::string to_string(const method_arguments ma) { switch (ma) { diff --git a/src/config/config.h b/src/config/config.h index 0e15ce19a..30e60a383 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -46,6 +46,23 @@ struct runtime_config; */ namespace config { +struct element_filter_t { + enum class filtered_type { + any, + function, + class_, + method, + enum_, + concept_, + package + }; + + filtered_type type{filtered_type::any}; + common::string_or_regex name; +}; + +std::string to_string(element_filter_t::filtered_type ft); + /*! Select how the method arguments should be rendered */ enum class method_arguments { full, /*! Full */ @@ -250,7 +267,7 @@ struct filter { * - r: ".*Enum.*" * ``` */ - std::vector elements; + std::vector elements; /*! @brief Element types filter * @@ -850,6 +867,8 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const package_diagram &c); YAML::Emitter &operator<<(YAML::Emitter &out, const layout_hint &c); +YAML::Emitter &operator<<(YAML::Emitter &out, const element_filter_t &ef); + #ifdef _MSC_VER YAML::Emitter &operator<<(YAML::Emitter &out, const std::filesystem::path &p); @@ -886,5 +905,4 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const access_t &r); YAML::Emitter &operator<<(YAML::Emitter &out, const diagram_t &d); /** @} */ // end of yaml_emitters } // namespace common::model - } // namespace clanguml diff --git a/src/config/schema.h b/src/config/schema.h index 995810c3e..f389ab7ba 100644 --- a/src/config/schema.h +++ b/src/config/schema.h @@ -139,10 +139,16 @@ const std::string schema_str = R"( context_filter_t: - regex_or_string_t - context_filter_match_t + element_typed_filter_t: + type: string + name: regex_or_string_t + element_filter_t: + - regex_or_string_t + - element_typed_filter_t filter_t: namespaces: !optional [regex_or_string_t] modules: !optional [regex_or_string_t] - elements: !optional [regex_or_string_t] + elements: !optional [element_filter_t] element_types: !optional [element_types_filter_t] relationships: !optional [relationship_filter_t] access: !optional [access_filter_t] diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index e86feb7d0..c4335d227 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -37,6 +37,7 @@ using clanguml::config::config; using clanguml::config::context_config; using clanguml::config::context_direction_t; using clanguml::config::diagram_template; +using clanguml::config::element_filter_t; using clanguml::config::filter; using clanguml::config::generate_links_config; using clanguml::config::git_config; @@ -530,6 +531,52 @@ template <> struct convert { } }; +template <> struct convert { + static bool decode(const Node &node, element_filter_t &rhs) + { + using namespace std::string_literals; + if (node.IsMap()) { + if (has_key(node, "r")) { + rhs.type = element_filter_t::filtered_type::any; + auto pattern = node["r"].as(); + auto rx = std::regex(pattern); + rhs.name = string_or_regex(std::move(rx), std::move(pattern)); + } + else if (has_key(node, "type")) { + rhs.type = element_filter_t::filtered_type::any; + if (node["type"].as() == "class") + rhs.type = element_filter_t::filtered_type::class_; + else if (node["type"].as() == "enum") + rhs.type = element_filter_t::filtered_type::enum_; + else if (node["type"].as() == "function") + rhs.type = element_filter_t::filtered_type::function; + else if (node["type"].as() == "method") + rhs.type = element_filter_t::filtered_type::method; + else if (node["type"].as() == "concept") + rhs.type = element_filter_t::filtered_type::concept_; + else if (node["type"].as() == "concept") + rhs.type = element_filter_t::filtered_type::concept_; + + auto name = node["name"]; + if (name.IsMap() && has_key(name, "r")) { + auto pattern = name["r"].as(); + auto rx = std::regex(pattern); + rhs.name = + string_or_regex(std::move(rx), std::move(pattern)); + } + else + rhs.name = name.as(); + } + } + else { + rhs.type = element_filter_t::filtered_type::any; + rhs.name = string_or_regex{node.as()}; + } + + return true; + } +}; + // // filter Yaml decoder // diff --git a/src/config/yaml_emitters.cc b/src/config/yaml_emitters.cc index 0e0e66de7..1ae1779e3 100644 --- a/src/config/yaml_emitters.cc +++ b/src/config/yaml_emitters.cc @@ -413,4 +413,13 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const package_diagram &c) return out; } +YAML::Emitter &operator<<(YAML::Emitter &out, const element_filter_t &ef) +{ + out << YAML::BeginMap; + out << YAML::Key << "type" << YAML::Value << to_string(ef.type); + out << YAML::Key << "name" << YAML::Value << ef.name; + out << YAML::EndMap; + + return out; +} } // namespace clanguml::config \ No newline at end of file diff --git a/tests/test_config_data/filters.yml b/tests/test_config_data/filters.yml index 9549357ff..76ed5f4c0 100644 --- a/tests/test_config_data/filters.yml +++ b/tests/test_config_data/filters.yml @@ -48,6 +48,18 @@ diagrams: exclude: elements: - ns1::ns2::ClassZ + regex_typed_elements_test: + type: class + include: + elements: + - type: class + name: 'MyTypeClass' + - type: class + name: + r: 'MyType.+' + exclude: + elements: + - ns1::ns2::ClassZ regex_namespace_test: type: class include: diff --git a/tests/test_filters.cc b/tests/test_filters.cc index 5f6bf5411..37a7ce0ea 100644 --- a/tests/test_filters.cc +++ b/tests/test_filters.cc @@ -20,6 +20,7 @@ #include "doctest/doctest.h" #include "class_diagram/model/class.h" +#include "class_diagram/model/enum.h" #include "cli/cli_handler.h" #include "common/model/filters/diagram_filter_factory.h" #include "common/model/source_file.h" @@ -218,6 +219,38 @@ TEST_CASE("Test elements regexp filter") CHECK(filter.should_include(c)); } +TEST_CASE("Test typed elements filter") +{ + using clanguml::class_diagram::model::class_; + using clanguml::class_diagram::model::class_method; + using clanguml::class_diagram::model::enum_; + using clanguml::common::model::access_t; + using clanguml::common::model::diagram_filter; + using clanguml::common::model::diagram_filter_factory; + using clanguml::common::model::namespace_; + using clanguml::common::model::package; + using clanguml::common::model::source_file; + + auto cfg = clanguml::config::load("./test_config_data/filters.yml"); + + auto &config = *cfg.diagrams["regex_typed_elements_test"]; + clanguml::class_diagram::model::diagram diagram; + + auto filter_ptr = diagram_filter_factory::create(diagram, config); + diagram_filter &filter = *filter_ptr; + + class_ c{{}}; + + c.set_name("MyTypeClass"); + + CHECK(filter.should_include(c)); + + enum_ e{{}}; + e.set_name("MyTypeClass"); + + CHECK(!filter.should_include(e)); +} + TEST_CASE("Test namespaces regexp filter") { using clanguml::class_diagram::model::class_;