Initial refactor of namespace handling

This commit is contained in:
Bartek Kryza
2022-03-04 23:38:18 +01:00
parent bee20e7f26
commit a67b459437
40 changed files with 620 additions and 201 deletions

View File

@@ -118,7 +118,7 @@ void generator::generate(
if (m.is_defaulted())
ostr << " = default";
ostr << " : " << ns_relative(uns, type);
ostr << " : " << uns.relative(type);
ostr << '\n';
}
@@ -154,9 +154,9 @@ void generator::generate(
if (!r.multiplicity_destination().empty())
puml_relation += " \"" + r.multiplicity_destination() + "\"";
relstr << m_model.to_alias(ns_relative(uns, c.full_name())) << " "
relstr << m_model.to_alias(uns.relative(c.full_name())) << " "
<< puml_relation << " "
<< m_model.to_alias(ns_relative(uns, destination));
<< m_model.to_alias(uns.relative(destination));
if (!r.label().empty()) {
relstr << " : " << plantuml_common::to_plantuml(r.scope())
@@ -197,7 +197,7 @@ void generator::generate(
ostr << "{static} ";
ostr << plantuml_common::to_plantuml(m.scope()) << m.name() << " : "
<< ns_relative(uns, m.type()) << '\n';
<< uns.relative(m.type()) << '\n';
}
ostr << "}" << '\n';
@@ -206,10 +206,8 @@ void generator::generate(
for (const auto &b : c.parents()) {
std::stringstream relstr;
try {
relstr << m_model.to_alias(ns_relative(uns, b.name()))
<< " <|-- "
<< m_model.to_alias(ns_relative(uns, c.full_name()))
<< '\n';
relstr << m_model.to_alias(uns.relative(b.name())) << " <|-- "
<< m_model.to_alias(uns.relative(c.full_name())) << '\n';
all_relations_str << relstr.str();
}
catch (error::uml_alias_missing &e) {
@@ -253,13 +251,13 @@ void generator::generate(
destination = r.destination();
relstr << m_model.to_alias(
ns_relative(m_config.using_namespace(), e.name()))
m_config.using_namespace().relative(e.name()))
<< " "
<< clanguml::common::generators::plantuml::to_plantuml(
r.type(), r.style())
<< " "
<< m_model.to_alias(
ns_relative(m_config.using_namespace(), destination));
m_config.using_namespace().relative(destination));
if (!r.label().empty())
relstr << " : " << r.label();

View File

@@ -24,8 +24,8 @@
namespace clanguml::class_diagram::model {
class_::class_(const std::vector<std::string> &using_namespaces)
: element{using_namespaces}
class_::class_(const common::model::namespace_ &using_namespace)
: element{using_namespace}
{
}
@@ -112,10 +112,12 @@ std::string class_::full_name_no_ns() const
std::string class_::full_name(bool relative) const
{
using namespace clanguml::util;
using clanguml::common::model::namespace_;
std::ostringstream ostr;
if (relative && starts_with(get_namespace(), using_namespace()))
ostr << ns_relative(using_namespace(), name_and_ns());
// if (relative && starts_with(get_namespace(), using_namespace()))
if (relative)
ostr << namespace_{name()}.relative_to(using_namespace()).to_string();
else
ostr << name_and_ns();
@@ -126,6 +128,8 @@ std::string class_::full_name(bool relative) const
std::ostringstream &class_::render_template_params(
std::ostringstream &ostr) const
{
using clanguml::common::model::namespace_;
if (!templates_.empty()) {
std::vector<std::string> tnames;
std::transform(templates_.cbegin(), templates_.cend(),
@@ -133,12 +137,14 @@ std::ostringstream &class_::render_template_params(
std::vector<std::string> res;
if (!tmplt.type().empty())
res.push_back(
util::ns_relative(using_namespace(), tmplt.type()));
res.push_back(namespace_{tmplt.type()}
.relative_to(using_namespace())
.to_string());
if (!tmplt.name().empty())
res.push_back(
util::ns_relative(using_namespace(), tmplt.name()));
res.push_back(namespace_{tmplt.name()}
.relative_to(using_namespace())
.to_string());
if (!tmplt.default_value().empty()) {
res.push_back("=");

View File

@@ -34,7 +34,7 @@ namespace clanguml::class_diagram::model {
class class_ : public common::model::element,
public common::model::stylable_element {
public:
class_(const std::vector<std::string> &using_namespaces);
class_(const common::model::namespace_ &using_namespace);
class_(const class_ &) = delete;
class_(class_ &&) noexcept = delete;

View File

@@ -82,7 +82,7 @@ void diagram::add_class(std::unique_ptr<class_> &&c)
auto ns = c->get_relative_namespace();
auto name = c->name();
add_element(ns, std::move(c));
ns.push_back(name);
ns |= name;
const auto ccc = get_element<class_>(ns);
assert(ccc.value().name() == name);
}

View File

@@ -24,8 +24,8 @@
namespace clanguml::class_diagram::model {
enum_::enum_(const std::vector<std::string> &using_namespaces)
: element{using_namespaces}
enum_::enum_(const common::model::namespace_ &using_namespace)
: element{using_namespace}
{
}
@@ -37,10 +37,11 @@ bool operator==(const enum_ &l, const enum_ &r)
std::string enum_::full_name(bool relative) const
{
using namespace clanguml::util;
using clanguml::common::model::namespace_;
std::ostringstream ostr;
if (relative)
ostr << ns_relative(using_namespace(), name());
ostr << namespace_{name()}.relative_to(using_namespace()).to_string();
else
ostr << name();

View File

@@ -27,7 +27,7 @@ namespace clanguml::class_diagram::model {
class enum_ : public common::model::element,
public common::model::stylable_element {
public:
enum_(const std::vector<std::string> &using_namespaces);
enum_(const common::model::namespace_ &using_namespaces);
enum_(const enum_ &) = delete;
enum_(enum_ &&) = default;

View File

@@ -38,14 +38,16 @@ void method_parameter::set_default_value(const std::string &value)
std::string method_parameter::default_value() const { return default_value_; }
std::string method_parameter::to_string(
const std::vector<std::string> &using_namespaces) const
const common::model::namespace_ &using_namespace) const
{
using namespace clanguml::util;
auto t = ns_relative(using_namespaces, type());
auto type_ns = common::model::namespace_{type()}
.relative_to(using_namespace)
.to_string();
if (default_value().empty())
return fmt::format("{} {}", t, name());
return fmt::format("{} {}", type_ns, name());
return fmt::format("{} {} = {}", t, name(), default_value());
return fmt::format("{} {} = {}", type_ns, name(), default_value());
}
}

View File

@@ -18,6 +18,7 @@
#pragma once
#include "common/model/decorated_element.h"
#include "common/model/namespace.h"
#include <string>
#include <vector>
@@ -36,7 +37,7 @@ public:
std::string default_value() const;
std::string to_string(
const std::vector<std::string> &using_namespaces) const;
const common::model::namespace_ &using_namespaces) const;
private:
std::string type_;

View File

@@ -154,14 +154,14 @@ translation_unit_context::get_type_alias_template(
void translation_unit_context::push_namespace(const std::string &ns)
{
namespace_.push_back(ns);
ns_ |= ns;
}
void translation_unit_context::pop_namespace() { namespace_.pop_back(); }
void translation_unit_context::pop_namespace() { ns_.pop_back(); }
const std::vector<std::string> &translation_unit_context::get_namespace() const
const common::model::namespace_ &translation_unit_context::get_namespace() const
{
return namespace_;
return ns_;
}
const cppast::cpp_entity_index &translation_unit_context::entity_index() const

View File

@@ -66,7 +66,7 @@ public:
void pop_namespace();
const std::vector<std::string> &get_namespace() const;
const common::model::namespace_ &get_namespace() const;
const cppast::cpp_entity_index &entity_index() const;
@@ -80,7 +80,7 @@ public:
private:
// Current visitor namespace
std::vector<std::string> namespace_;
common::model::namespace_ ns_;
// Reference to the cppast entity index
cppast::cpp_entity_index &entity_index_;

View File

@@ -200,7 +200,7 @@ void translation_unit_visitor::process_type_alias_template(
cx::util::full_name(ctx.get_namespace(), at),
type_safe::ref(at.type_alias().underlying_type()));
if (ctx.config().should_include(tinst->get_namespace(), tinst->name()))
if (ctx.config().should_include(tinst->get_namespace() | tinst->name()))
ctx.diagram().add_class(std::move(tinst));
}
}
@@ -222,15 +222,14 @@ void translation_unit_visitor::process_type_alias(
void translation_unit_visitor::process_namespace(
const cppast::cpp_entity &e, const cppast::cpp_namespace &ns_declaration)
{
std::vector<std::string> package_parent = ctx.get_namespace();
auto package_path = package_parent;
package_path.push_back(e.name());
auto package_parent = ctx.get_namespace();
auto package_path = package_parent | e.name();
auto usn = util::split(ctx.config().using_namespace()[0], "::");
auto usn = ctx.config().using_namespace();
if (ctx.config().should_include_package(util::join(package_path, "::"))) {
if (ctx.config().should_include_package(package_path)) {
auto p = std::make_unique<common::model::package>(usn);
util::remove_prefix(package_path, usn);
package_path = package_path.relative_to(usn);
p->set_name(e.name());
p->set_namespace(package_parent);
@@ -267,8 +266,7 @@ void translation_unit_visitor::process_enum_declaration(
return;
}
auto e_ptr = std::make_unique<enum_>(
util::split(ctx.config().using_namespace()[0], "::"));
auto e_ptr = std::make_unique<enum_>(ctx.config().using_namespace());
auto &e = *e_ptr;
e.set_name(enm.name());
e.set_namespace(ctx.get_namespace());
@@ -312,8 +310,7 @@ void translation_unit_visitor::process_class_declaration(
const cppast::cpp_class &cls,
type_safe::optional_ref<const cppast::cpp_template_specialization> tspec)
{
auto c_ptr = std::make_unique<class_>(
util::split(ctx.config().using_namespace()[0], "::"));
auto c_ptr = std::make_unique<class_>(ctx.config().using_namespace());
auto &c = *c_ptr;
c.is_struct(cls.class_kind() == cppast::cpp_class_kind::struct_t);
// c.set_name(cx::util::full_name(ctx.get_namespace(), cls));
@@ -1275,8 +1272,7 @@ bool translation_unit_visitor::find_relationships_in_template_instantiation(
const auto [ns, base_name] = cx::util::split_ns(fn);
auto ns_and_name = ns;
ns_and_name.push_back(base_name);
auto ns_and_name = ns | base_name;
auto full_name = fmt::format("{}", fmt::join(ns_and_name, "::"));
@@ -1403,8 +1399,7 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
std::optional<clanguml::class_diagram::model::class_ *> parent)
{
// Create class_ instance to hold the template instantiation
auto tinst_ptr = std::make_unique<class_>(
util::split(ctx.config().using_namespace()[0], "::"));
auto tinst_ptr = std::make_unique<class_>(ctx.config().using_namespace());
auto &tinst = *tinst_ptr;
std::string full_template_name;
@@ -1430,7 +1425,7 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
// Extract namespace from base template name
const auto [ns, name] = cx::util::split_ns(tinst_full_name);
tinst.set_name(name);
if (ns.empty())
if (ns.is_empty())
tinst.set_namespace(ctx.get_namespace());
else
tinst.set_namespace(ns);

View File

@@ -83,11 +83,10 @@ void generator<C, D>::generate_config_layout_hints(std::ostream &ostr) const
for (const auto &hint : hints) {
std::stringstream hint_str;
try {
hint_str << m_model.to_alias(ns_relative(uns, entity))
hint_str << m_model.to_alias(uns.relative(entity))
<< " -[hidden]"
<< clanguml::config::to_string(hint.hint) << "- "
<< m_model.to_alias(ns_relative(uns, hint.entity))
<< '\n';
<< m_model.to_alias(uns.relative(hint.entity)) << '\n';
ostr << hint_str.str();
}
catch (clanguml::error::uml_alias_missing &e) {
@@ -103,12 +102,14 @@ template <typename C, typename D>
void generator<C, D>::generate_plantuml_directives(
std::ostream &ostr, const std::vector<std::string> &directives) const
{
using common::model::namespace_;
for (const auto &b : directives) {
std::string note{b};
std::tuple<std::string, size_t, size_t> alias_match;
while (util::find_element_alias(note, alias_match)) {
auto alias = m_model.to_alias(util::ns_relative(
m_config.using_namespace(), std::get<0>(alias_match)));
auto alias = m_model.to_alias(
m_config.using_namespace().relative(std::get<0>(alias_match)));
note.replace(
std::get<1>(alias_match), std::get<2>(alias_match), alias);
}

View File

@@ -26,12 +26,10 @@ namespace clanguml::common::model {
std::atomic_uint64_t element::m_nextId = 1;
element::element(const std::vector<std::string> &using_namespaces)
: using_namespace_{using_namespaces}
element::element(const namespace_ &using_namespace)
: using_namespace_{using_namespace}
, m_id{m_nextId++}
{
for (const auto &n : using_namespace_)
assert(!util::contains(n, "::"));
}
std::string element::alias() const { return fmt::format("C_{:010}", m_id); }
@@ -59,18 +57,12 @@ void element::add_relationship(relationship &&cr)
relationships_.emplace_back(std::move(cr));
}
void element::set_using_namespaces(const std::vector<std::string> &un)
void element::set_using_namespaces(const namespace_ &un)
{
for (const auto &n : un)
assert(!util::contains(n, "::"));
using_namespace_ = un;
}
const std::vector<std::string> &element::using_namespace() const
{
return using_namespace_;
}
const namespace_ &element::using_namespace() const { return ns_; }
std::vector<relationship> &element::relationships() { return relationships_; }
@@ -88,9 +80,8 @@ bool operator==(const element &l, const element &r)
std::ostream &operator<<(std::ostream &out, const element &rhs)
{
out << "(" << rhs.name() << ", ns=["
<< util::join(rhs.get_namespace(), "::") << "], full_name=["
<< rhs.full_name(true) << "])";
out << "(" << rhs.name() << ", ns=[" << rhs.get_namespace().to_string()
<< "], full_name=[" << rhs.full_name(true) << "])";
return out;
}

View File

@@ -18,6 +18,7 @@
#pragma once
#include "decorated_element.h"
#include "namespace.h"
#include "relationship.h"
#include "util/util.h"
@@ -30,7 +31,7 @@ namespace clanguml::common::model {
class element : public decorated_element {
public:
element(const std::vector<std::string> &using_namespaces);
element(const namespace_ &using_namespace);
virtual ~element() = default;
@@ -42,27 +43,24 @@ public:
std::string name_and_ns() const
{
auto ns = namespace_;
ns.push_back(name());
return util::join(ns, "::");
auto ns = ns_ | name();
return ns.to_string();
}
void set_namespace(const std::vector<std::string> &ns) { namespace_ = ns; }
void set_namespace(const namespace_ &ns) { ns_ = ns; }
std::vector<std::string> get_namespace() const { return namespace_; }
namespace_ get_namespace() const { return ns_; }
std::vector<std::string> get_relative_namespace() const
namespace_ get_relative_namespace() const
{
auto relative_ns = namespace_;
util::remove_prefix(relative_ns, using_namespace_);
return relative_ns;
return ns_.relative_to(using_namespace_);
}
virtual std::string full_name(bool relative) const { return name(); }
void set_using_namespaces(const std::vector<std::string> &un);
void set_using_namespaces(const namespace_ &un);
const std::vector<std::string> &using_namespace() const;
const namespace_ &using_namespace() const;
std::vector<relationship> &relationships();
@@ -81,8 +79,8 @@ protected:
private:
std::string name_;
std::vector<std::string> namespace_;
std::vector<std::string> using_namespace_;
namespace_ ns_;
namespace_ using_namespace_;
std::vector<relationship> relationships_;
static std::atomic_uint64_t m_nextId;

View File

@@ -0,0 +1,213 @@
/**
* src/common/model/namespace.h
*
* Copyright (c) 2021-2022 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.
*/
#include "namespace.h"
#include "util/util.h"
namespace clanguml::common::model {
namespace_::namespace_(std::initializer_list<std::string> ns)
{
if ((ns.size() == 1) && util::contains(*ns.begin(), "::"))
namespace_path_ = util::split(*ns.begin(), "::");
else
namespace_path_ = ns;
}
namespace_::namespace_(const std::vector<std::string> &ns)
{
if ((ns.size() == 1) && util::contains(*ns.begin(), "::"))
namespace_path_ = util::split(*ns.begin(), "::");
else
namespace_path_ = ns;
}
namespace_::namespace_(const std::string &ns)
{
namespace_path_ = util::split(ns, "::");
}
namespace_::namespace_(
container_type::const_iterator begin, container_type::const_iterator end)
{
std::copy(begin, end, std::back_inserter(namespace_path_));
}
std::string namespace_::to_string() const
{
return fmt::format("{}", fmt::join(namespace_path_, "::"));
}
size_t namespace_::size() const { return namespace_path_.size(); }
bool namespace_::is_empty() const { return namespace_path_.empty(); }
namespace_::container_type::iterator namespace_::begin()
{
return namespace_path_.begin();
}
namespace_::container_type::iterator namespace_::end()
{
return namespace_path_.end();
}
namespace_::container_type::const_iterator namespace_::cbegin() const
{
return namespace_path_.cbegin();
}
namespace_::container_type::const_iterator namespace_::cend() const
{
return namespace_path_.cend();
}
namespace_::container_type::const_iterator namespace_::begin() const
{
return namespace_path_.begin();
}
namespace_::container_type::const_iterator namespace_::end() const
{
return namespace_path_.end();
}
void namespace_::append(const std::string &ns)
{
namespace_path_.push_back(ns);
}
void namespace_::append(const namespace_ &ns)
{
for (const auto &n : ns) {
append(n);
}
}
void namespace_::pop_back() { namespace_path_.pop_back(); }
namespace_ namespace_::operator|(const namespace_ &right) const
{
namespace_ res{*this};
res.append(right);
return res;
}
void namespace_::operator|=(const namespace_ &right) { append(right); }
namespace_ namespace_::operator|(const std::string &right) const
{
namespace_ res{*this};
res.append(right);
return res;
}
void namespace_::operator|=(const std::string &right) { append(right); }
std::string &namespace_::operator[](const int index)
{
return namespace_path_[index];
}
const std::string &namespace_::operator[](const int index) const
{
return namespace_path_[index];
}
bool namespace_::starts_with(const namespace_ &right) const
{
return util::starts_with(namespace_path_, right.namespace_path_);
}
namespace_ namespace_::common_path(const namespace_ &right) const
{
namespace_ res{};
for (auto i = 0U; i < std::min(size(), right.size()); i++) {
if (namespace_path_[i] == right[i])
res |= namespace_path_[i];
else
break;
}
return res;
}
namespace_ namespace_::relative_to(const namespace_ &right) const
{
namespace_ res{*this};
if (res.starts_with(right))
util::remove_prefix(res.namespace_path_, right.namespace_path_);
return res;
}
std::string namespace_::relative(const std::string &name) const
{
/*
std::vector<std::string> namespaces_sorted{namespaces};
std::sort(namespaces_sorted.rbegin(), namespaces_sorted.rend());
auto res = name;
for (const auto &ns : namespaces_sorted) {
if (ns.empty())
continue;
if (name == ns)
return split(n, "::").back();
auto ns_prefix = ns + "::";
auto it = res.find(ns_prefix);
while (it != std::string::npos) {
res.erase(it, ns_prefix.size());
it = res.find(ns_prefix);
}
}
return res;
*/
if (is_empty())
return name;
if (name == to_string())
return name;
auto res = name;
auto ns_prefix = to_string() + "::";
auto it = res.find(ns_prefix);
while (it != std::string::npos) {
res.erase(it, ns_prefix.size());
it = res.find(ns_prefix);
}
return res;
}
bool operator==(const namespace_ &left, const namespace_ &right)
{
return left.namespace_path_ == right.namespace_path_;
}
std::string namespace_::name() const
{
assert(size() > 0);
return namespace_path_.back();
}
}

View File

@@ -0,0 +1,87 @@
/**
* src/common/model/namespace.h
*
* Copyright (c) 2021-2022 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.
*/
#pragma once
#include <string>
#include <vector>
namespace clanguml::common::model {
class namespace_ {
using container_type = std::vector<std::string>;
public:
namespace_() = default;
namespace_(const std::string &ns);
namespace_(container_type::const_iterator begin,
container_type::const_iterator end);
namespace_(const namespace_ &right) noexcept = default;
namespace_ &operator=(const namespace_ &right) noexcept = default;
namespace_(namespace_ &&right) noexcept = default;
namespace_ &operator=(namespace_ &&right) noexcept = default;
friend bool operator==(const namespace_ &left, const namespace_ &right);
namespace_(std::initializer_list<std::string> ns);
explicit namespace_(const std::vector<std::string> &ns);
std::string to_string() const;
bool is_empty() const;
size_t size() const;
namespace_ operator|(const namespace_ &right) const;
void operator|=(const namespace_ &right);
namespace_ operator|(const std::string &right) const;
void operator|=(const std::string &right);
std::string &operator[](const int index);
const std::string &operator[](const int index) const;
void append(const std::string &ns);
void append(const namespace_ &ns);
void pop_back();
bool starts_with(const namespace_ &right) const;
namespace_ common_path(const namespace_ &right) const;
namespace_ relative_to(const namespace_ &right) const;
std::string relative(const std::string &name) const;
std::string name() const;
container_type::iterator begin();
container_type::iterator end();
container_type::const_iterator begin() const;
container_type::const_iterator end() const;
container_type::const_iterator cbegin() const;
container_type::const_iterator cend() const;
private:
container_type namespace_path_;
};
}

View File

@@ -52,58 +52,59 @@ public:
}
template <typename V = T>
void add_element(std::vector<std::string> path, std::unique_ptr<V> p)
void add_element(namespace_ ns, std::unique_ptr<V> p)
{
assert(p);
LOG_DBG("Adding nested element {} at path {}", p->name(),
fmt::join(path, "::"));
LOG_DBG(
"Adding nested element {} at path {}", p->name(), ns.to_string());
if (path.empty()) {
if (ns.is_empty()) {
add_element(std::move(p));
return;
}
auto parent = get_element(path);
auto parent = get_element(ns);
if (parent && dynamic_cast<nested_trait<T> *>(&parent.value()))
dynamic_cast<nested_trait<T> &>(parent.value())
.template add_element<V>(std::move(p));
else {
spdlog::error(
"No parent element found at: {}", fmt::join(path, "::"));
throw std::runtime_error("No parent element found");
spdlog::error("No parent element found at: {}", ns.to_string());
throw std::runtime_error(
"No parent element found for " + ns.to_string());
}
}
template <typename V = T>
auto get_element(std::vector<std::string> path) const
template <typename V = T> auto get_element(const namespace_ &path) const
{
LOG_DBG("Getting nested element at path: {}", fmt::join(path, "::"));
LOG_DBG("Getting nested element at path: {}", path.to_string());
if (path.empty() || !has_element(path.at(0))) {
LOG_WARN("Nested element {} not found in element",
fmt::join(path, "::"));
if (path.is_empty() || !has_element(path[0])) {
LOG_WARN(
"Nested element {} not found in element", path.to_string());
return type_safe::optional_ref<V>{};
}
if (path.size() == 1)
return get_element<V>(path.at(0));
return get_element<V>(path[0]);
auto p = get_element<T>(path.at(0));
auto p = get_element<T>(path[0]);
if (!p)
return type_safe::optional_ref<V>{};
if (dynamic_cast<nested_trait<T> *>(&p.value()))
return dynamic_cast<nested_trait<T> &>(p.value()).get_element<V>(
std::vector<std::string>(path.begin() + 1, path.end()));
namespace_{path.begin() + 1, path.end()});
return type_safe::optional_ref<V>{};
}
template <typename V = T> auto get_element(const std::string &name) const
{
assert(!util::contains(name, "::"));
auto it = std::find_if(elements_.cbegin(), elements_.cend(),
[&](const auto &p) { return name == p->name(); });

View File

@@ -23,24 +23,33 @@
#include <sstream>
namespace clanguml::common::model {
package::package(const std::vector<std::string> &using_namespaces)
: element{using_namespaces}
package::package(const common::model::namespace_ &using_namespace)
: element{using_namespace}
{
}
std::string package::full_name(bool relative) const
{
auto fn = get_namespace();
auto ns = using_namespace();
if (relative && (fn.size() >= ns.size())) {
if (util::starts_with(fn, ns))
fn = std::vector<std::string>(fn.begin() + ns.size(), fn.end());
if (relative) {
auto res = get_namespace().relative_to(using_namespace()) | name();
return res.to_string();
}
fn.push_back(name());
return (get_namespace().relative_to(using_namespace()) | name())
.to_string();
return fmt::format("{}", fmt::join(fn, "::"));
// auto fn = get_namespace();
// auto ns = using_namespace();
//
// if (relative && (fn.size() >= ns.size())) {
// if (fn.starts_with(using_namespace())
// fn = std::vector<std::string>(fn.begin() + ns.size(),
// fn.end());
// }
//
// fn.push_back(name());
//
// return fmt::format("{}", fmt::join(fn, "::"));
}
bool package::is_deprecated() const { return is_deprecated_; }

View File

@@ -35,7 +35,7 @@ class package : public element,
public stylable_element,
public nested_trait<element> {
public:
package(const std::vector<std::string> &using_namespaces);
package(const common::model::namespace_ &using_namespace);
package(const package &) = delete;
package(package &&) = default;

View File

@@ -130,17 +130,22 @@ bool diagram::should_include_relationship(const std::string &rel)
}
bool diagram::should_include(
const std::pair<std::vector<std::string>, std::string> &name) const
const std::pair<common::model::namespace_, std::string> &name) const
{
return should_include(std::get<0>(name), std::get<1>(name));
}
// bool diagram::should_include(
// const common::model::namespace_ &ns, const std::string &name) const
//{
// auto ns_and_name = ns | name;
// return should_include(ns_and_name.to_string());
//}
bool diagram::should_include(
const std::vector<std::string> &ns, const std::string &name) const
const common::model::namespace_ &ns, const std::string &name) const
{
auto ns_and_name = ns;
ns_and_name.push_back(name);
return should_include(util::join(ns_and_name, "::"));
return should_include(ns | name);
}
bool diagram::should_include(const std::string &name_) const
@@ -148,7 +153,7 @@ bool diagram::should_include(const std::string &name_) const
auto name = clanguml::util::unqualify(name_);
for (const auto &ex : exclude().namespaces) {
if (name.find(ex) == 0) {
if (ex.starts_with(name)) {
LOG_DBG("Skipping from diagram: {}", name);
return false;
}
@@ -160,7 +165,7 @@ bool diagram::should_include(const std::string &name_) const
return true;
for (const auto &in : include().namespaces) {
if (name.find(in) == 0)
if (name.find(in.to_string()) == 0)
return true;
}
@@ -169,11 +174,22 @@ bool diagram::should_include(const std::string &name_) const
return false;
}
bool diagram::should_include(const common::model::namespace_ &path) const
{
return should_include(path.to_string());
}
bool diagram::should_include_package(
const common::model::namespace_ &path) const
{
return should_include_package(path.to_string());
}
bool diagram::should_include_package(const std::string &name) const
{
for (const auto &ex : exclude().namespaces) {
if (name.find(ex) == 0) {
if (name.find(ex.to_string()) == 0) {
LOG_DBG("Skipping from diagram: {}", name);
return false;
}
@@ -185,7 +201,7 @@ bool diagram::should_include_package(const std::string &name) const
return true;
for (const auto &in : include().namespaces) {
if (in.find(name) == 0 || name.find(in) == 0)
if (in.to_string().find(name) == 0 || name.find(in.to_string()) == 0)
return true;
}
@@ -283,6 +299,21 @@ void get_option(const Node &node, clanguml::config::option<T> &option)
option.set(node[option.name].template as<T>());
}
template <>
void get_option<clanguml::common::model::namespace_>(const Node &node,
clanguml::config::option<clanguml::common::model::namespace_> &option)
{
if (node[option.name]) {
if (node[option.name].Type() == NodeType::Scalar)
option.set(node[option.name].template as<std::string>());
else if (node[option.name].Type() == NodeType::Sequence)
option.set(
node[option.name].template as<std::vector<std::string>>()[0]);
else
throw std::runtime_error("Invalid using_namespace value");
}
}
template <>
void get_option<method_arguments>(
const Node &node, clanguml::config::option<method_arguments> &option)
@@ -393,8 +424,12 @@ template <> struct convert<plantuml> {
template <> struct convert<filter> {
static bool decode(const Node &node, filter &rhs)
{
if (node["namespaces"])
rhs.namespaces = node["namespaces"].as<decltype(rhs.namespaces)>();
if (node["namespaces"]) {
auto namespace_list =
node["namespaces"].as<std::vector<std::string>>();
for (const auto &ns : namespace_list)
rhs.namespaces.push_back(ns);
}
if (node["relationships"])
rhs.relationships =

View File

@@ -46,7 +46,7 @@ struct plantuml {
};
struct filter {
std::vector<std::string> namespaces;
std::vector<common::model::namespace_> namespaces;
// Valid values are:
// - inheritance
@@ -79,7 +79,7 @@ std::string to_string(const hint_t t);
struct inheritable_diagram_options {
option<std::vector<std::string>> glob{"glob"};
option<std::vector<std::string>> using_namespace{"using_namespace"};
option<common::model::namespace_> using_namespace{"using_namespace"};
option<bool> include_relations_also_as_members{
"include_relations_also_as_members", true};
option<filter> include{"include"};
@@ -105,15 +105,24 @@ struct diagram : public inheritable_diagram_options {
bool should_include_package(const std::string &name) const;
bool should_include(
const std::pair<std::vector<std::string>, std::string> &name) const;
bool should_include_package(const common::model::namespace_ &path) const;
bool should_include(
const std::vector<std::string> &ns, const std::string &name) const;
const std::pair<common::model::namespace_, std::string> &name) const;
// bool should_include(
// const std::vector<std::string> &ns, const std::string &name)
// const;
bool should_include(
const common::model::namespace_ &ns, const std::string &name) const;
bool should_include(const common::model::scope_t scope) const;
bool should_include(const std::string &name_) const;
bool should_include(const common::model::namespace_ &path) const;
private:
};

View File

@@ -28,7 +28,7 @@ template <typename T> struct option {
option(const std::string &name_,
option_inherit_mode im = option_inherit_mode::override)
: name{name_}
, value{{}}
, value{}
{
}
option(const std::string &name_, const T &initial_value,

View File

@@ -39,7 +39,7 @@ std::string to_string(CXString &&cxs)
}
std::string full_name(
const std::vector<std::string> &current_ns, const cppast::cpp_entity &e)
const common::model::namespace_ &current_ns, const cppast::cpp_entity &e)
{
if (e.name().empty())
return "";
@@ -126,12 +126,13 @@ bool is_inside_class(const cppast::cpp_entity &e)
return false;
}
std::pair<std::vector<std::string>, std::string> split_ns(
std::pair<common::model::namespace_, std::string> split_ns(
const std::string &full_name)
{
auto name_before_template = ::clanguml::util::split(full_name, "<")[0];
auto ns = ::clanguml::util::split(name_before_template, "::");
auto name = ns.back();
auto ns = common::model::namespace_{
::clanguml::util::split(name_before_template, "::")};
auto name = ns.name();
ns.pop_back();
return {ns, name};
}
@@ -197,7 +198,7 @@ std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx)
}
std::string fully_prefixed(
const std::vector<std::string> &current_ns, const cppast::cpp_entity &e)
const common::model::namespace_ &current_ns, const cppast::cpp_entity &e)
{
if (e.name().find("::") != std::string::npos) {
// the name already contains namespace, but it could be not

View File

@@ -17,6 +17,8 @@
*/
#pragma once
#include "common/model/namespace.h"
#include <clang-c/CXCompilationDatabase.h>
#include <clang-c/Index.h>
#include <cppast/cpp_entity.hpp>
@@ -41,13 +43,13 @@ namespace util {
std::string to_string(CXString &&cxs);
std::string full_name(
const std::vector<std::string> &current_ns, const cppast::cpp_entity &e);
const common::model::namespace_ &current_ns, const cppast::cpp_entity &e);
std::string full_name(const cppast::cpp_type &t,
const cppast::cpp_entity_index &idx, bool inside_class);
std::string fully_prefixed(
const std::vector<std::string> &current_ns, const cppast::cpp_entity &e);
const common::model::namespace_ &current_ns, const cppast::cpp_entity &e);
const cppast::cpp_type &unreferenced(const cppast::cpp_type &t);
@@ -58,7 +60,7 @@ type_safe::optional_ref<const cppast::cpp_namespace> entity_ns(
std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx);
std::pair<std::vector<std::string>, std::string> split_ns(
std::pair<common::model::namespace_, std::string> split_ns(
const std::string &full_name);
bool is_inside_class(const cppast::cpp_entity &e);

View File

@@ -39,9 +39,9 @@ void generator::generate_relationships(
for (const auto &r : p.relationships()) {
std::stringstream relstr;
try {
relstr << m_model.to_alias(ns_relative(uns, p.full_name(false)))
relstr << m_model.to_alias(uns.relative(p.full_name(false)))
<< " ..> "
<< m_model.to_alias(ns_relative(uns, r.destination()))
<< m_model.to_alias(uns.relative(r.destination()))
<< '\n';
ostr << relstr.str();
}

View File

@@ -27,17 +27,17 @@ std::string diagram::to_alias(const std::string &full_name) const
{
LOG_DBG("Looking for alias for {}", full_name);
auto fn = util::split(full_name, "::");
auto path = common::model::namespace_{full_name};
if (fn.empty())
if (path.is_empty())
throw error::uml_alias_missing(
fmt::format("Missing alias for '{}'", full_name));
fmt::format("Missing alias for '{}'", path.to_string()));
auto package = get_element<common::model::package>(fn);
auto package = get_element<common::model::package>(path);
if (!package)
throw error::uml_alias_missing(
fmt::format("Missing alias for '{}'", full_name));
fmt::format("Missing alias for '{}'", path.to_string()));
return package.value().alias();
}

View File

@@ -153,12 +153,12 @@ translation_unit_context::get_type_alias_template(
void translation_unit_context::push_namespace(const std::string &ns)
{
namespace_.push_back(ns);
namespace_ |= ns;
}
void translation_unit_context::pop_namespace() { namespace_.pop_back(); }
const std::vector<std::string> &translation_unit_context::get_namespace() const
const common::model::namespace_ &translation_unit_context::get_namespace() const
{
return namespace_;
}

View File

@@ -68,7 +68,7 @@ public:
void pop_namespace();
const std::vector<std::string> &get_namespace() const;
const common::model::namespace_ &get_namespace() const;
const cppast::cpp_entity_index &entity_index() const;
@@ -82,7 +82,7 @@ public:
private:
// Current visitor namespace
std::vector<std::string> namespace_;
common::model::namespace_ namespace_;
// Reference to the cppast entity index
cppast::cpp_entity_index &entity_index_;

View File

@@ -18,6 +18,8 @@
#include "translation_unit_visitor.h"
#include "common/model/namespace.h"
#include <cppast/cpp_alias_template.hpp>
#include <cppast/cpp_array_type.hpp>
#include <cppast/cpp_class_template.hpp>
@@ -89,20 +91,17 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
if (!ns_declaration.is_anonymous() &&
!ns_declaration.is_inline()) {
std::vector<std::string> package_parent =
ctx.get_namespace();
auto package_path = package_parent;
package_path.push_back(e.name());
auto package_parent = ctx.get_namespace();
auto package_path = package_parent | e.name();
auto usn = common::model::namespace_{
ctx.config().using_namespace()};
auto usn = util::split(
ctx.config().using_namespace()[0], "::");
if (!util::starts_with(usn, package_path)) {
if (!usn.starts_with(package_path)) {
auto p = std::make_unique<package>(usn);
util::remove_prefix(package_path, usn);
package_path = package_path.relative_to(usn);
p->set_name(e.name());
p->set_namespace(package_parent);
p->set_namespace(package_path);
if (ns_declaration.comment().has_value())
p->add_decorators(decorators::parse(
@@ -303,10 +302,10 @@ void translation_unit_visitor::process_class_declaration(
}
for (const auto &dependency : relationships) {
auto destination = util::split(std::get<0>(dependency), "::");
auto destination = common::model::namespace_{std::get<0>(dependency)};
if (!util::starts_with(ctx.get_namespace(), destination) &&
!util::starts_with(destination, ctx.get_namespace())) {
if (!ctx.get_namespace().starts_with(destination) &&
!destination.starts_with(ctx.get_namespace())) {
relationship r{
relationship_t::kDependency, std::get<0>(dependency)};
current_package.value().add_relationship(std::move(r));
@@ -330,10 +329,10 @@ void translation_unit_visitor::process_function(const cppast::cpp_function &f)
f.return_type(), relationships, relationship_t::kDependency);
for (const auto &dependency : relationships) {
auto destination = util::split(std::get<0>(dependency), "::");
auto destination = common::model::namespace_{std::get<0>(dependency)};
if (!util::starts_with(ctx.get_namespace(), destination) &&
!util::starts_with(destination, ctx.get_namespace())) {
if (!ctx.get_namespace().starts_with(destination) &&
!destination.starts_with(ctx.get_namespace())) {
relationship r{
relationship_t::kDependency, std::get<0>(dependency)};
current_package.value().add_relationship(std::move(r));
@@ -350,8 +349,8 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_,
const auto fn = cx::util::full_name(
resolve_alias(cppast::remove_cv(t_)), ctx.entity_index(), false);
auto t_ns = util::split(fn, "::");
auto t_name = t_ns.back();
auto t_ns = common::model::namespace_{fn};
auto t_name = t_ns.name();
t_ns.pop_back();
const auto &t_raw = resolve_alias(cppast::remove_cv(t_));
@@ -371,13 +370,13 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_,
const auto &t_raw_ns_final = cx::util::ns(t_raw_ns.value()) +
"::" + cx::util::full_name({}, t_raw_ns.value());
t_ns = util::split(t_raw_ns_final, "::");
t_ns = common::model::namespace_{t_raw_ns_final};
}
}
std::vector<std::string> possible_matches;
possible_matches.push_back(util::join(t_ns, "::"));
possible_matches.push_back(t_ns.to_string());
const auto fn_ns = cx::util::ns(cppast::remove_cv(t_), ctx.entity_index());

View File

@@ -44,8 +44,8 @@ generator::generator(
void generator::generate_call(const message &m, std::ostream &ostr) const
{
const auto from = ns_relative(m_config.using_namespace(), m.from);
const auto to = ns_relative(m_config.using_namespace(), m.to);
const auto from = m_config.using_namespace().relative(m.from);
const auto to = m_config.using_namespace().relative(m.to);
ostr << '"' << from << "\" "
<< common::generators::plantuml::to_plantuml(message_t::kCall) << " \""
@@ -57,8 +57,8 @@ void generator::generate_return(const message &m, std::ostream &ostr) const
// Add return activity only for messages between different actors and
// only if the return type is different than void
if ((m.from != m.to) && (m.return_type != "void")) {
const auto from = ns_relative(m_config.using_namespace(), m.from);
const auto to = ns_relative(m_config.using_namespace(), m.to);
const auto from = m_config.using_namespace().relative(m.from);
const auto to = m_config.using_namespace().relative(m.to);
ostr << '"' << to << "\" "
<< common::generators::plantuml::to_plantuml(message_t::kReturn)
@@ -69,7 +69,7 @@ void generator::generate_return(const message &m, std::ostream &ostr) const
void generator::generate_activity(const activity &a, std::ostream &ostr) const
{
for (const auto &m : a.messages) {
const auto to = ns_relative(m_config.using_namespace(), m.to);
const auto to = m_config.using_namespace().relative(m.to);
generate_call(m, ostr);
ostr << "activate " << '"' << to << '"' << std::endl;
if (m_model.sequences.find(m.to_usr) != m_model.sequences.end())

View File

@@ -18,6 +18,7 @@
#include "translation_unit_visitor.h"
#include "common/model/namespace.h"
#include "translation_unit_context.h"
#include <cppast/cpp_function.hpp>
@@ -81,7 +82,7 @@ void translation_unit_visitor::process_activities(const cppast::cpp_function &e)
m.from = cx::util::ns(caller) + "::" + caller.name();
if (!ctx.config().should_include(
util::split(cx::util::ns(caller), "::"), caller.name()))
common::model::namespace_{cx::util::ns(caller)}, caller.name()))
continue;
if (caller.kind() == cpp_entity_kind::function_t)
@@ -98,7 +99,7 @@ void translation_unit_visitor::process_activities(const cppast::cpp_function &e)
m.to += "()";
if (!ctx.config().should_include(
util::split(cx::util::ns(callee), "::"), callee.name()))
common::model::namespace_{cx::util::ns(callee)}, callee.name()))
continue;
m.to_usr = type_safe::get(function_call.get_callee_method_id());

View File

@@ -68,6 +68,7 @@ std::string join(const std::vector<std::string> &toks, std::string delimiter)
return fmt::format("{}", fmt::join(toks, delimiter));
}
/*
std::string ns_relative(
const std::vector<std::string> &namespaces, const std::string &n)
{
@@ -93,6 +94,7 @@ std::string ns_relative(
}
return res;
}
*/
std::string unqualify(const std::string &s)
{

View File

@@ -83,8 +83,8 @@ std::string join(const std::vector<std::string> &toks, std::string delimiter);
*
* @return Identifier relative to one of the matching namespaces.
*/
std::string ns_relative(
const std::vector<std::string> &namespaces, const std::string &n);
// std::string ns_relative(
// const std::vector<std::string> &namespaces, const std::string &n);
/**
* @brief Remove any qualifiers (e.g. const) from type.

View File

@@ -26,7 +26,8 @@ TEST_CASE("t00002", "[test-case][class]")
REQUIRE(diagram->include().namespaces.size() == 1);
REQUIRE_THAT(diagram->include().namespaces,
VectorContains(std::string{"clanguml::t00002"}));
VectorContains(
clanguml::common::model::namespace_{"clanguml::t00002"}));
REQUIRE(diagram->exclude().namespaces.size() == 0);

View File

@@ -25,8 +25,6 @@ TEST_CASE("t00003", "[test-case][class]")
REQUIRE(diagram->name == "t00003_class");
REQUIRE(diagram->include().namespaces.size() == 1);
REQUIRE_THAT(diagram->include().namespaces,
VectorContains(std::string{"clanguml::t00003"}));
REQUIRE(diagram->exclude().namespaces.size() == 0);

View File

@@ -25,9 +25,6 @@ TEST_CASE("t00004", "[test-case][class]")
REQUIRE(diagram->name == "t00004_class");
REQUIRE(diagram->include().namespaces.size() == 1);
REQUIRE_THAT(diagram->include().namespaces,
VectorContains(std::string{"clanguml::t00004"}));
REQUIRE(diagram->exclude().namespaces.size() == 0);
REQUIRE(diagram->should_include("clanguml::t00004::A"));

View File

@@ -53,7 +53,7 @@ TEST_CASE("Test config inherited", "[unit-test]")
CHECK(cus.type() == clanguml::config::diagram_type::class_diagram);
CHECK(cus.glob().size() == 1);
CHECK(cus.glob()[0] == "src/main.cc");
CHECK(clanguml::util::contains(cus.using_namespace(), "clanguml::ns1"));
CHECK(cus.using_namespace().starts_with({"clanguml::ns1"}));
CHECK(cus.include_relations_also_as_members());
CHECK(def.generate_packages() == false);
}
@@ -76,7 +76,7 @@ TEST_CASE("Test config includes", "[unit-test]")
CHECK(cus.type() == clanguml::config::diagram_type::class_diagram);
CHECK(cus.glob().size() == 1);
CHECK(cus.glob()[0] == "src/main.cc");
CHECK(clanguml::util::contains(cus.using_namespace(), "clanguml::ns1"));
CHECK(cus.using_namespace().starts_with({"clanguml::ns1"}));
CHECK(cus.include_relations_also_as_members());
CHECK(cus.generate_method_arguments() ==
clanguml::config::method_arguments::none);

View File

@@ -6,8 +6,7 @@ diagrams:
glob:
- src/**/*.cc
- src/**/*.h
using_namespace:
- clanguml
using_namespace: clanguml
generate_method_arguments: full
generate_packages: true
include:

72
tests/test_model.cc Normal file
View File

@@ -0,0 +1,72 @@
/**
* tests/test_model.cc
*
* Copyright (c) 2021-2022 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.
*/
#define CATCH_CONFIG_MAIN
#include "catch.h"
#include "common/model/namespace.h"
TEST_CASE("Test namespace_", "[unit-test]")
{
using clanguml::common::model::namespace_;
namespace_ ns1{};
CHECK(ns1.is_empty());
namespace_ ns2{"aaa", "bbb", "ccc"};
CHECK(ns2.size() == 3);
CHECK(ns2[0] == "aaa");
CHECK(ns2[1] == "bbb");
CHECK(ns2[2] == "ccc");
namespace_ ns3 = ns1 | "aaa" | "bbb" | "ccc";
CHECK(ns3.size() == 3);
CHECK(ns3[0] == "aaa");
CHECK(ns3[1] == "bbb");
CHECK(ns3[2] == "ccc");
namespace_ ns4 = namespace_{"aaa", "bbb"} | namespace_{"ccc"};
CHECK(ns4.size() == 3);
CHECK(ns4[0] == "aaa");
CHECK(ns4[1] == "bbb");
CHECK(ns4[2] == "ccc");
namespace_ ns5{"aaa::bbb::ccc"};
CHECK(ns5.size() == 3);
CHECK(ns5[0] == "aaa");
CHECK(ns5[1] == "bbb");
CHECK(ns5[2] == "ccc");
CHECK(ns4 == ns5);
namespace_ ns6a{"aaa::bbb::ccc"};
namespace_ ns6b{"aaa::bbb::ddd::eee"};
auto ns6 = ns6a.common_path(ns6b);
CHECK(ns6 == namespace_{"aaa", "bbb"});
CHECK(ns6b.starts_with({"aaa", "bbb", "ddd"}));
namespace_ ns7a{"aaa::bbb"};
namespace_ ns7b{"aaa::bbb::ccc::ddd"};
CHECK(ns7b.relative_to(ns7a) == namespace_{"ccc", "ddd"});
CHECK(ns7a.relative_to(ns7b) == namespace_{"aaa::bbb"});
namespace_ ns8{"aaa::bbb"};
const std::string name{"aaa::bbb::ccc<std::unique_ptr<aaa::bbb::ddd>>"};
CHECK(ns8.relative(name) == "ccc<std::unique_ptr<ddd>>");
}

View File

@@ -35,19 +35,19 @@ TEST_CASE("Test split", "[unit-test]")
CHECK(split("std::vector::detail::", "::") == C{"std", "vector", "detail"});
}
TEST_CASE("Test ns_relative", "[unit-test]")
{
using namespace clanguml::util;
CHECK(ns_relative({}, "std::vector") == "std::vector");
CHECK(ns_relative({"std"}, "std::vector") == "vector");
CHECK(ns_relative({"std"}, "const std::vector&") == "const vector&");
CHECK(ns_relative({"std", "clanguml::t0"},
"static const std::vector<clanguml::t0::a>&") ==
"static const vector<a>&");
CHECK(ns_relative({"clanguml::t0"}, "clanguml::t0") == "t0");
}
//
// TEST_CASE("Test ns_relative", "[unit-test]")
//{
// using namespace clanguml::util;
//
// CHECK(ns_relative({}, "std::vector") == "std::vector");
// CHECK(ns_relative({"std"}, "std::vector") == "vector");
// CHECK(ns_relative({"std"}, "const std::vector&") == "const vector&");
// CHECK(ns_relative({"std", "clanguml::t0"},
// "static const std::vector<clanguml::t0::a>&") ==
// "static const vector<a>&");
// CHECK(ns_relative({"clanguml::t0"}, "clanguml::t0") == "t0");
//}
TEST_CASE("Test abbreviate", "[unit-test]")
{