diff --git a/src/common/generators/generators.cc b/src/common/generators/generators.cc index a200e993..b7b9dd1d 100644 --- a/src/common/generators/generators.cc +++ b/src/common/generators/generators.cc @@ -205,16 +205,36 @@ void generate_diagram(const std::string &od, const std::string &name, dynamic_cast(*diagram), translation_units, verbose); - auto path = std::filesystem::path{od} / fmt::format("{}.puml", name); - std::ofstream ofs; - ofs.open(path, std::ofstream::out | std::ofstream::trunc); + for (const auto generator_type : generators) { + if (generator_type == generator_type_t::plantuml) { + auto path = + std::filesystem::path{od} / fmt::format("{}.puml", name); + std::ofstream ofs; + ofs.open(path, std::ofstream::out | std::ofstream::trunc); - ofs << clanguml::include_diagram::generators::plantuml::generator( - dynamic_cast(*diagram), *model); + ofs << clanguml::include_diagram::generators::plantuml:: + generator( + dynamic_cast(*diagram), *model); - ofs.close(); + ofs.close(); - LOG_INFO("Written {} diagram to {}", name, path.string()); + LOG_INFO("Written {} diagram to {}", name, path.string()); + } + else if (generator_type == generator_type_t::json) { + auto path = + std::filesystem::path{od} / fmt::format("{}.json", name); + std::ofstream ofs; + + ofs.open(path, std::ofstream::out | std::ofstream::trunc); + ofs << clanguml::include_diagram::generators::json::generator( + dynamic_cast(*diagram), + *model); + + ofs.close(); + + LOG_INFO("Written {} diagram to {}", name, path.string()); + } + } } } diff --git a/src/common/generators/generators.h b/src/common/generators/generators.h index 5d8ea709..e5e03803 100644 --- a/src/common/generators/generators.h +++ b/src/common/generators/generators.h @@ -23,6 +23,7 @@ #include "common/generators/generators.h" #include "common/model/diagram_filter.h" #include "config/config.h" +#include "include_diagram/generators/json/include_diagram_generator.h" #include "include_diagram/generators/plantuml/include_diagram_generator.h" #include "package_diagram/generators/json/package_diagram_generator.h" #include "package_diagram/generators/plantuml/package_diagram_generator.h" diff --git a/src/common/model/source_file.cc b/src/common/model/source_file.cc index 49269d92..b5161c97 100644 --- a/src/common/model/source_file.cc +++ b/src/common/model/source_file.cc @@ -17,3 +17,21 @@ */ #include "source_file.h" + +namespace clanguml::common::model { + +std::string to_string(source_file_t sf) +{ + switch (sf) { + case source_file_t::kDirectory: + return "directory"; + case source_file_t::kHeader: + return "header"; + case source_file_t::kImplementation: + return "implementation"; + default: + assert(false); + } +} + +} // namespace clanguml::common::model diff --git a/src/common/model/source_file.h b/src/common/model/source_file.h index fbc00778..8c860504 100644 --- a/src/common/model/source_file.h +++ b/src/common/model/source_file.h @@ -36,6 +36,8 @@ namespace clanguml::common::model { enum class source_file_t { kDirectory, kHeader, kImplementation }; +std::string to_string(source_file_t sf); + struct fs_path_sep { #ifdef _WIN32 static constexpr std::string_view value = "\\"; diff --git a/src/include_diagram/generators/json/include_diagram_generator.cc b/src/include_diagram/generators/json/include_diagram_generator.cc new file mode 100644 index 00000000..ce26063b --- /dev/null +++ b/src/include_diagram/generators/json/include_diagram_generator.cc @@ -0,0 +1,107 @@ +/** + * src/include_diagram/generators/json/include_diagram_generator.cc + * + * Copyright (c) 2021-2023 Bartek Kryza + * + * 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 "include_diagram_generator.h" + +#include "util/error.h" + +namespace clanguml::include_diagram::generators::json { + +generator::generator(diagram_config &config, diagram_model &model) + : common_generator{config, model} +{ +} + +void generator::generate_relationships( + const source_file &f, nlohmann::json &parent) const +{ + LOG_DBG("Generating relationships for file {}", f.full_name(true)); + + namespace json_common = clanguml::common::generators::json; + + if (f.type() == common::model::source_file_t::kDirectory) { + util::for_each(f, [this, &parent](const auto &file) { + generate_relationships( + dynamic_cast(*file), parent); + }); + } + else { + util::for_each_if( + f.relationships(), + [this](const auto &r) { return m_model.should_include(r.type()); }, + [&f, this](const auto &r) { + nlohmann::json rel = r; + rel["source"] = std::to_string(f.id()); + json_["relationships"].push_back(std::move(rel)); + }); + } +} + +void generator::generate(const source_file &f, nlohmann::json &parent) const +{ + nlohmann::json j; + j["id"] = std::to_string(f.id()); + j["name"] = f.name(); + j["display_name"] = f.full_name(false); + + if (f.type() == common::model::source_file_t::kDirectory) { + LOG_DBG("Generating directory {}", f.name()); + + j["type"] = "folder"; + + util::for_each(f, [this, &j](const auto &file) { + generate(dynamic_cast(*file), j); + }); + + parent["elements"].push_back(std::move(j)); + } + else { + if (m_model.should_include(f)) { + LOG_DBG("Generating file {}", f.name()); + + j["type"] = "file"; + j["file_kind"] = to_string(f.type()); + + parent["elements"].push_back(std::move(j)); + } + } +} + +void generator::generate(std::ostream &ostr) const +{ + json_["name"] = m_model.name(); + json_["diagram_type"] = "include"; + + json_["elements"] = std::vector{}; + json_["relationships"] = std::vector{}; + + // Generate files and folders + util::for_each_if( + m_model, [](const auto & /*f*/) { return true; }, + [this, &ostr](const auto &f) { + generate(dynamic_cast(*f), json_); + }); + + // Process file include relationships + util::for_each(m_model, [this](const auto &f) { + generate_relationships(dynamic_cast(*f), json_); + }); + + ostr << json_; +} +} // namespace clanguml::include_diagram::generators::json diff --git a/src/include_diagram/generators/json/include_diagram_generator.h b/src/include_diagram/generators/json/include_diagram_generator.h new file mode 100644 index 00000000..b7865e8b --- /dev/null +++ b/src/include_diagram/generators/json/include_diagram_generator.h @@ -0,0 +1,69 @@ +/** + * src/include_diagram/generators/json/include_diagram_generator.h + * + * Copyright (c) 2021-2023 Bartek Kryza + * + * 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 "common/generators/json/generator.h" +#include "common/model/package.h" +#include "common/model/relationship.h" +#include "common/model/source_file.h" +#include "config/config.h" +#include "include_diagram/model/diagram.h" +#include "include_diagram/visitor/translation_unit_visitor.h" +#include "util/util.h" + +#include +#include +#include +#include + +namespace clanguml { +namespace include_diagram { +namespace generators { +namespace json { + +using diagram_config = clanguml::config::include_diagram; +using diagram_model = clanguml::include_diagram::model::diagram; + +template +using common_generator = clanguml::common::generators::json::generator; + +using clanguml::common::model::access_t; +using clanguml::common::model::package; +using clanguml::common::model::relationship_t; +using clanguml::common::model::source_file; +using namespace clanguml::util; + +class generator : public common_generator { +public: + generator(diagram_config &config, diagram_model &model); + + void generate_relationships( + const source_file &p, nlohmann::json &parent) const; + + void generate(const source_file &e, nlohmann::json &parent) const; + + void generate(std::ostream &ostr) const override; + +private: + mutable nlohmann::json json_; +}; + +} // namespace json +} // namespace generators +} // namespace include_diagram +} // namespace clanguml diff --git a/src/package_diagram/generators/json/package_diagram_generator.cc b/src/package_diagram/generators/json/package_diagram_generator.cc index e5d7b154..05c12f0c 100644 --- a/src/package_diagram/generators/json/package_diagram_generator.cc +++ b/src/package_diagram/generators/json/package_diagram_generator.cc @@ -37,7 +37,7 @@ void generator::generate_relationships( for (const auto &r : p.relationships()) { nlohmann::json rel = r; rel["source"] = std::to_string(p.id()); - parent["relationships"].push_back(std::move(rel)); + json_["relationships"].push_back(std::move(rel)); } } diff --git a/tests/t40001/test_case.h b/tests/t40001/test_case.h index 2ed7621f..70b5ec90 100644 --- a/tests/t40001/test_case.h +++ b/tests/t40001/test_case.h @@ -28,27 +28,56 @@ TEST_CASE("t40001", "[test-case][include]") REQUIRE(model->name() == "t40001_include"); - auto puml = generate_include_puml(diagram, *model); + { + auto puml = generate_include_puml(diagram, *model); - AliasMatcher _A(puml); + AliasMatcher _A(puml); - REQUIRE_THAT(puml, StartsWith("@startuml")); - REQUIRE_THAT(puml, EndsWith("@enduml\n")); + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); - REQUIRE_THAT(puml, IsFolder("lib1")); - REQUIRE_THAT(puml, IsFile("lib1.h")); - REQUIRE_THAT(puml, IsFile("t40001.cc")); - REQUIRE_THAT(puml, IsFile("t40001_include1.h")); + REQUIRE_THAT(puml, IsFolder("lib1")); + REQUIRE_THAT(puml, IsFile("lib1.h")); + REQUIRE_THAT(puml, IsFile("t40001.cc")); + REQUIRE_THAT(puml, IsFile("t40001_include1.h")); - REQUIRE_THAT(puml, IsFile("string")); - REQUIRE_THAT(puml, IsFile("yaml-cpp/yaml.h")); + REQUIRE_THAT(puml, IsFile("string")); + REQUIRE_THAT(puml, IsFile("yaml-cpp/yaml.h")); - REQUIRE_THAT(puml, IsAssociation(_A("t40001.cc"), _A("t40001_include1.h"))); - REQUIRE_THAT(puml, IsAssociation(_A("t40001_include1.h"), _A("lib1.h"))); + REQUIRE_THAT( + puml, IsAssociation(_A("t40001.cc"), _A("t40001_include1.h"))); + REQUIRE_THAT( + puml, IsAssociation(_A("t40001_include1.h"), _A("lib1.h"))); - REQUIRE_THAT(puml, IsDependency(_A("t40001_include1.h"), _A("string"))); + REQUIRE_THAT(puml, IsDependency(_A("t40001_include1.h"), _A("string"))); - REQUIRE_THAT(puml, HasComment("t40001 test diagram of type include")); + REQUIRE_THAT(puml, HasComment("t40001 test diagram of type include")); - save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); + save_puml( + config.output_directory() + "/" + diagram->name + ".puml", puml); + } + + { + auto j = generate_include_json(diagram, *model); + + using namespace json; + + REQUIRE(IsFolder(j, "include")); + REQUIRE(IsFolder(j, "include/lib1")); + REQUIRE(IsFolder(j, "src")); + + REQUIRE(IsFile(j, "include/lib1/lib1.h")); + REQUIRE(IsFile(j, "include/t40001_include1.h")); + REQUIRE(IsFile(j, "src/t40001.cc")); + REQUIRE(IsFile(j, "yaml-cpp/yaml.h")); + + REQUIRE(IsFile(j, "string")); + + REQUIRE(IsAssociation(j, "src/t40001.cc", "include/t40001_include1.h")); + REQUIRE(IsAssociation( + j, "include/t40001_include1.h", "include/lib1/lib1.h")); + REQUIRE(IsDependency(j, "include/t40001_include1.h", "string")); + + save_json(config.output_directory() + "/" + diagram->name + ".json", j); + } } diff --git a/tests/t40002/test_case.h b/tests/t40002/test_case.h index 0f6451b4..67bb22b2 100644 --- a/tests/t40002/test_case.h +++ b/tests/t40002/test_case.h @@ -28,56 +28,87 @@ TEST_CASE("t40002", "[test-case][include]") REQUIRE(model->name() == "t40002_include"); - auto puml = generate_include_puml(diagram, *model); + { + auto puml = generate_include_puml(diagram, *model); - AliasMatcher _A(puml); + AliasMatcher _A(puml); - REQUIRE_THAT(puml, StartsWith("@startuml")); - REQUIRE_THAT(puml, EndsWith("@enduml\n")); + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); - REQUIRE_THAT(puml, IsFolder("lib1")); - REQUIRE_THAT(puml, IsFolder("lib2")); - REQUIRE_THAT(puml, IsFile("lib1.h")); - REQUIRE_THAT(puml, IsFile("lib2.h")); - REQUIRE_THAT(puml, !IsFile("lib2_detail.h")); - REQUIRE_THAT(puml, IsFile("t40002.cc")); - REQUIRE_THAT(puml, IsFile("lib1.cc")); - REQUIRE_THAT(puml, IsFile("lib2.cc")); + REQUIRE_THAT(puml, IsFolder("lib1")); + REQUIRE_THAT(puml, IsFolder("lib2")); + REQUIRE_THAT(puml, IsFile("lib1.h")); + REQUIRE_THAT(puml, IsFile("lib2.h")); + REQUIRE_THAT(puml, !IsFile("lib2_detail.h")); + REQUIRE_THAT(puml, IsFile("t40002.cc")); + REQUIRE_THAT(puml, IsFile("lib1.cc")); + REQUIRE_THAT(puml, IsFile("lib2.cc")); - REQUIRE_THAT(puml, !IsFile("string")); + REQUIRE_THAT(puml, !IsFile("string")); - REQUIRE_THAT(puml, IsAssociation(_A("t40002.cc"), _A("lib1.h"))); - REQUIRE_THAT(puml, IsAssociation(_A("lib1.h"), _A("lib2.h"))); - REQUIRE_THAT(puml, IsAssociation(_A("lib1.cc"), _A("lib1.h"))); - REQUIRE_THAT(puml, IsAssociation(_A("lib2.cc"), _A("lib2.h"))); + REQUIRE_THAT(puml, IsAssociation(_A("t40002.cc"), _A("lib1.h"))); + REQUIRE_THAT(puml, IsAssociation(_A("lib1.h"), _A("lib2.h"))); + REQUIRE_THAT(puml, IsAssociation(_A("lib1.cc"), _A("lib1.h"))); + REQUIRE_THAT(puml, IsAssociation(_A("lib2.cc"), _A("lib2.h"))); - REQUIRE_THAT(puml, - HasLink(_A("t40002.cc"), - fmt::format("https://github.com/bkryza/clang-uml/blob/{}/tests/" - "t40002/src/t40002.cc#L0", - clanguml::util::get_git_commit()), - "t40002.cc")); + REQUIRE_THAT(puml, + HasLink(_A("t40002.cc"), + fmt::format("https://github.com/bkryza/clang-uml/blob/{}/tests/" + "t40002/src/t40002.cc#L0", + clanguml::util::get_git_commit()), + "t40002.cc")); - REQUIRE_THAT(puml, - HasLink(_A("lib1.cc"), - fmt::format("https://github.com/bkryza/clang-uml/blob/{}/tests/" - "t40002/src/lib1/lib1.cc#L0", - clanguml::util::get_git_commit()), - "lib1.cc")); + REQUIRE_THAT(puml, + HasLink(_A("lib1.cc"), + fmt::format("https://github.com/bkryza/clang-uml/blob/{}/tests/" + "t40002/src/lib1/lib1.cc#L0", + clanguml::util::get_git_commit()), + "lib1.cc")); - REQUIRE_THAT(puml, - HasLink(_A("lib1.h"), - fmt::format("https://github.com/bkryza/clang-uml/blob/{}/tests/" - "t40002/include/lib1/lib1.h#L0", - clanguml::util::get_git_commit()), - "lib1.h")); + REQUIRE_THAT(puml, + HasLink(_A("lib1.h"), + fmt::format("https://github.com/bkryza/clang-uml/blob/{}/tests/" + "t40002/include/lib1/lib1.h#L0", + clanguml::util::get_git_commit()), + "lib1.h")); - REQUIRE_THAT(puml, - HasLink(_A("lib2.h"), - fmt::format("https://github.com/bkryza/clang-uml/blob/{}/tests/" - "t40002/include/lib2/lib2.h#L0", - clanguml::util::get_git_commit()), - "lib2.h")); + REQUIRE_THAT(puml, + HasLink(_A("lib2.h"), + fmt::format("https://github.com/bkryza/clang-uml/blob/{}/tests/" + "t40002/include/lib2/lib2.h#L0", + clanguml::util::get_git_commit()), + "lib2.h")); - save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); + save_puml( + config.output_directory() + "/" + diagram->name + ".puml", puml); + } + + { + auto j = generate_include_json(diagram, *model); + + using namespace json; + + REQUIRE(IsFolder(j, "include")); + REQUIRE(IsFolder(j, "include/lib1")); + REQUIRE(IsFolder(j, "include/lib2")); + REQUIRE(IsFolder(j, "src")); + REQUIRE(IsFolder(j, "src/lib1")); + REQUIRE(IsFolder(j, "src/lib2")); + REQUIRE(IsFile(j, "include/lib1/lib1.h")); + REQUIRE(IsFile(j, "include/lib2/lib2.h")); + REQUIRE(!IsFile(j, "include/lib2/lib2_detail.h")); + REQUIRE(IsFile(j, "src/lib1/lib1.cc")); + REQUIRE(IsFile(j, "src/lib2/lib2.cc")); + REQUIRE(IsFile(j, "src/t40002.cc")); + + REQUIRE(!IsFile(j, "string")); + + REQUIRE(IsAssociation(j, "src/t40002.cc", "include/lib1/lib1.h")); + REQUIRE(IsAssociation(j, "include/lib1/lib1.h", "include/lib2/lib2.h")); + REQUIRE(IsAssociation(j, "src/lib1/lib1.cc", "include/lib1/lib1.h")); + REQUIRE(IsAssociation(j, "src/lib2/lib2.cc", "include/lib2/lib2.h")); + + save_json(config.output_directory() + "/" + diagram->name + ".json", j); + } } diff --git a/tests/t40003/test_case.h b/tests/t40003/test_case.h index e45cbea9..966c7fdc 100644 --- a/tests/t40003/test_case.h +++ b/tests/t40003/test_case.h @@ -28,23 +28,51 @@ TEST_CASE("t40003", "[test-case][include]") REQUIRE(model->name() == "t40003_include"); - auto puml = generate_include_puml(diagram, *model); + { + auto puml = generate_include_puml(diagram, *model); - AliasMatcher _A(puml); + AliasMatcher _A(puml); - REQUIRE_THAT(puml, StartsWith("@startuml")); - REQUIRE_THAT(puml, EndsWith("@enduml\n")); + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); - REQUIRE_THAT(puml, IsFolder("dependants")); - REQUIRE_THAT(puml, IsFolder("dependencies")); + REQUIRE_THAT(puml, IsFolder("dependants")); + REQUIRE_THAT(puml, IsFolder("dependencies")); - REQUIRE_THAT(puml, IsFile("t1.h")); - REQUIRE_THAT(puml, IsFile("t2.h")); - REQUIRE_THAT(puml, IsFile("t3.h")); + REQUIRE_THAT(puml, IsFile("t1.h")); + REQUIRE_THAT(puml, IsFile("t2.h")); + REQUIRE_THAT(puml, IsFile("t3.h")); - REQUIRE_THAT(puml, !IsFile("t4.h")); - REQUIRE_THAT(puml, IsFile("t5.h")); - REQUIRE_THAT(puml, !IsFile("t6.h")); + REQUIRE_THAT(puml, !IsFile("t4.h")); + REQUIRE_THAT(puml, IsFile("t5.h")); + REQUIRE_THAT(puml, !IsFile("t6.h")); - save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); + save_puml( + config.output_directory() + "/" + diagram->name + ".puml", puml); + } + + { + auto j = generate_include_json(diagram, *model); + + using namespace json; + + REQUIRE(IsFolder(j, "include/dependants")); + REQUIRE(IsFolder(j, "include/dependencies")); + REQUIRE(IsFolder(j, "src/dependants")); + REQUIRE(IsFolder(j, "src/dependencies")); + + REQUIRE(IsFile(j, "include/dependants/t1.h")); + REQUIRE(IsFile(j, "include/dependants/t2.h")); + REQUIRE(IsFile(j, "include/dependants/t3.h")); + REQUIRE(!IsFile(j, "include/dependants/t4.h")); + REQUIRE(IsFile(j, "src/dependants/t1.cc")); + + REQUIRE(IsFile(j, "include/dependencies/t1.h")); + REQUIRE(IsFile(j, "include/dependencies/t2.h")); + REQUIRE(IsFile(j, "include/dependencies/t3.h")); + REQUIRE(!IsFile(j, "include/dependencies/t4.h")); + REQUIRE(IsFile(j, "src/dependencies/t2.cc")); + + save_json(config.output_directory() + "/" + diagram->name + ".json", j); + } } diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 265a8608..38909c61 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -232,6 +232,22 @@ std::string generate_include_puml( return ss.str(); } +nlohmann::json generate_include_json( + std::shared_ptr config, + clanguml::include_diagram::model::diagram &model) +{ + using namespace clanguml::include_diagram::generators::json; + + std::stringstream ss; + + assert(config.get() != nullptr); + + ss << generator( + dynamic_cast(*config), model); + + return nlohmann::json::parse(ss.str()); +} + void save_puml(const std::string &path, const std::string &puml) { std::filesystem::path p{path}; diff --git a/tests/test_cases.h b/tests/test_cases.h index 3f60fc4b..944911b0 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -611,6 +611,15 @@ ContainsMatcher IsDeprecated(std::string const &str, } namespace json { +struct File { + explicit File(const std::string &f) + : file{f} + { + } + + const std::string file; +}; + std::optional get_element( const nlohmann::json &j, const std::string &name) { @@ -621,7 +630,7 @@ std::optional get_element( if (e["display_name"] == name) return {e}; - if (e["type"] == "namespace") { + if (e["type"] == "namespace" || e["type"] == "folder") { auto maybe_e = get_element(e, name); if (maybe_e) return maybe_e; @@ -714,6 +723,18 @@ bool IsPackage(const nlohmann::json &j, const std::string &name) return e && e->at("type") == "namespace"; } +bool IsFolder(const nlohmann::json &j, const std::string &name) +{ + auto e = get_element(j, name); + return e && e->at("type") == "folder"; +} + +bool IsFile(const nlohmann::json &j, const std::string &name) +{ + auto e = get_element(j, name); + return e && e->at("type") == "file"; +} + bool IsDeprecated(const nlohmann::json &j, const std::string &name) { auto e = get_element(j, expand_name(j, name)); @@ -810,13 +831,27 @@ bool IsAggregation(nlohmann::json j, const std::string &from, return true; } +namespace detail { +bool is_dependency_impl( + nlohmann::json j, const std::string &from, const std::string &to) +{ + auto rel = get_relationship(j, from, to, "dependency"); + + return rel != j["relationships"].end(); +} + +} // namespace detail + bool IsDependency( nlohmann::json j, const std::string &from, const std::string &to) { - auto rel = get_relationship( - j, expand_name(j, from), expand_name(j, to), "dependency"); + return detail::is_dependency_impl( + j, expand_name(j, from), expand_name(j, to)); +} - return rel != j["relationships"].end(); +bool IsDependency(nlohmann::json j, const File &from, const File &to) +{ + return detail::is_dependency_impl(j, from.file, to.file); } bool IsInstantiation( @@ -930,15 +965,6 @@ int find_message_impl(const nlohmann::json &j, const std::string &from, } // namespace detail -struct File { - explicit File(const std::string &f) - : file{f} - { - } - - const std::string file; -}; - int FindMessage(const nlohmann::json &j, const File &from, const File &to, const std::string &msg) {