diff --git a/src/class_diagram/generators/mermaid/class_diagram_generator.cc b/src/class_diagram/generators/mermaid/class_diagram_generator.cc index 64fc15aa6..5e92a9751 100644 --- a/src/class_diagram/generators/mermaid/class_diagram_generator.cc +++ b/src/class_diagram/generators/mermaid/class_diagram_generator.cc @@ -25,6 +25,7 @@ namespace clanguml::class_diagram::generators::mermaid { using clanguml::common::generators::mermaid::indent; +using clanguml::common::generators::mermaid::render_name; generator::generator(diagram_config &config, diagram_model &model) : common_generator{config, model} @@ -32,19 +33,6 @@ generator::generator(diagram_config &config, diagram_model &model) { } -std::string generator::render_name(std::string name) const -{ - util::replace_all(name, "<", "<"); - util::replace_all(name, ">", ">"); - util::replace_all(name, "(", "("); - util::replace_all(name, ")", ")"); - util::replace_all(name, "##", "::"); - util::replace_all(name, "{", "{"); - util::replace_all(name, "}", "}"); - - return name; -} - void generator::generate_alias( const common::model::element &c, std::ostream &ostr) const { diff --git a/src/class_diagram/generators/mermaid/class_diagram_generator.h b/src/class_diagram/generators/mermaid/class_diagram_generator.h index 5da861a7a..4a8bd1183 100644 --- a/src/class_diagram/generators/mermaid/class_diagram_generator.h +++ b/src/class_diagram/generators/mermaid/class_diagram_generator.h @@ -246,8 +246,6 @@ class generator : public common_generator { const std::vector method_groups_{ "constructors", "assignment", "operators", "other"}; - std::string render_name(std::string name) const; - template void sort_class_elements(std::vector &elements) const { diff --git a/src/common/generators/generators.h b/src/common/generators/generators.h index 639a5fe6d..8c3d29d13 100644 --- a/src/common/generators/generators.h +++ b/src/common/generators/generators.h @@ -31,6 +31,7 @@ #include "package_diagram/generators/json/package_diagram_generator.h" #include "package_diagram/generators/plantuml/package_diagram_generator.h" #include "sequence_diagram/generators/json/sequence_diagram_generator.h" +#include "sequence_diagram/generators/mermaid/sequence_diagram_generator.h" #include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h" #include "util/util.h" #include "version.h" @@ -168,11 +169,11 @@ struct diagram_generator_t { using type = clanguml::class_diagram::generators::mermaid::generator; }; -// template <> -// struct diagram_generator_t { -// using type = clanguml::sequence_diagram::generators::mermaid::generator; -// }; +template <> +struct diagram_generator_t { + using type = clanguml::sequence_diagram::generators::mermaid::generator; +}; // template <> // struct diagram_generator_t { diff --git a/src/common/generators/mermaid/generator.cc b/src/common/generators/mermaid/generator.cc index 740ac3cb6..9000cedd4 100644 --- a/src/common/generators/mermaid/generator.cc +++ b/src/common/generators/mermaid/generator.cc @@ -64,9 +64,9 @@ std::string to_mermaid(message_t r) { switch (r) { case message_t::kCall: - return "->"; + return "->>"; case message_t::kReturn: - return "-->"; + return "-->>"; default: return ""; } @@ -78,4 +78,16 @@ std::string indent(const unsigned level) return std::string(level * kIndentWidth, ' '); } +std::string render_name(std::string name) +{ + util::replace_all(name, "<", "<"); + util::replace_all(name, ">", ">"); + util::replace_all(name, "(", "("); + util::replace_all(name, ")", ")"); + util::replace_all(name, "##", "::"); + util::replace_all(name, "{", "{"); + util::replace_all(name, "}", "}"); + + return name; +} } // namespace clanguml::common::generators::mermaid diff --git a/src/common/generators/mermaid/generator.h b/src/common/generators/mermaid/generator.h index 7eee83c26..bff4bfedc 100644 --- a/src/common/generators/mermaid/generator.h +++ b/src/common/generators/mermaid/generator.h @@ -44,6 +44,8 @@ std::string to_mermaid(message_t r); std::string indent(const unsigned level); +std::string render_name(std::string name); + /** * @brief Base class for diagram generators * diff --git a/src/sequence_diagram/generators/mermaid/sequence_diagram_generator.cc b/src/sequence_diagram/generators/mermaid/sequence_diagram_generator.cc new file mode 100644 index 000000000..de9e33083 --- /dev/null +++ b/src/sequence_diagram/generators/mermaid/sequence_diagram_generator.cc @@ -0,0 +1,580 @@ +/** + * @file src/sequence_diagram/generators/mermaid/sequence_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 "sequence_diagram_generator.h" + +namespace clanguml::sequence_diagram::generators::mermaid { + +using clanguml::common::model::message_t; +using clanguml::config::location_t; +using clanguml::sequence_diagram::model::activity; +using clanguml::sequence_diagram::model::message; +using namespace clanguml::util; + +using clanguml::common::generators::mermaid::indent; +using clanguml::common::generators::mermaid::render_name; + +std::string render_participant_name(std::string name) +{ + util::replace_all(name, "##", "::"); + + return name; +} + +std::string render_message_text(std::string text) +{ + util::replace_all(text, ";", "#59;"); + + return text; +} + +generator::generator( + clanguml::config::sequence_diagram &config, diagram_model &model) + : common_generator{config, model} +{ +} + +void generator::generate_call(const message &m, std::ostream &ostr) const +{ + const auto &from = model().get_participant(m.from()); + const auto &to = model().get_participant(m.to()); + + if (!from || !to) { + LOG_DBG("Skipping empty call from '{}' to '{}'", m.from(), m.to()); + return; + } + + generate_participant(ostr, m.from()); + generate_participant(ostr, m.to()); + + std::string message; + + model::function::message_render_mode render_mode = + select_method_arguments_render_mode(); + + if (to.value().type_name() == "method") { + const auto &f = dynamic_cast(to.value()); + const std::string_view style = f.is_static() ? "" : ""; + message = + fmt::format("{}{}{}", style, f.message_name(render_mode), style); + } + else if (config().combine_free_functions_into_file_participants()) { + if (to.value().type_name() == "function") { + message = dynamic_cast(to.value()) + .message_name(render_mode); + } + else if (to.value().type_name() == "function_template") { + message = dynamic_cast(to.value()) + .message_name(render_mode); + } + } + + const std::string from_alias = generate_alias(from.value()); + const std::string to_alias = generate_alias(to.value()); + + print_debug(m, ostr); + + ostr << indent(1) << from_alias << " " + << common::generators::mermaid::to_mermaid(message_t::kCall) << " "; + + ostr << indent(1) << to_alias; + + ostr << " : "; + + if (m.message_scope() == common::model::message_scope_t::kCondition) + ostr << "["; + + ostr << message; + + if (m.message_scope() == common::model::message_scope_t::kCondition) + ostr << "]"; + + ostr << '\n'; + + LOG_DBG("Generated call '{}' from {} [{}] to {} [{}]", message, from, + m.from(), to, m.to()); +} + +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 + const auto &from = model().get_participant(m.from()); + const auto &to = model().get_participant(m.to()); + if ((m.from() != m.to()) && !to.value().is_void()) { + const std::string from_alias = generate_alias(from.value()); + + const std::string to_alias = generate_alias(to.value()); + + ostr << indent(1) << to_alias << " " + << common::generators::mermaid::to_mermaid(message_t::kReturn) + << " " << from_alias << " : "; + + if (config().generate_return_types()) { + ostr << m.return_type(); + } + + ostr << '\n'; + } +} + +void generator::generate_activity(const activity &a, std::ostream &ostr, + std::vector &visited) const +{ + for (const auto &m : a.messages()) { + if (m.type() == message_t::kCall) { + const auto &to = + model().get_participant(m.to()); + + visited.push_back(m.from()); + + LOG_DBG("Generating message [{}] --> [{}]", m.from(), m.to()); + + generate_call(m, ostr); + + std::string to_alias = generate_alias(to.value()); + + ostr << indent(1) << "activate " << to_alias << std::endl; + + if (model().sequences().find(m.to()) != model().sequences().end()) { + if (std::find(visited.begin(), visited.end(), m.to()) == + visited + .end()) { // break infinite recursion on recursive calls + LOG_DBG("Creating activity {} --> {} - missing sequence {}", + m.from(), m.to(), m.to()); + generate_activity( + model().get_activity(m.to()), ostr, visited); + } + } + else + LOG_DBG("Skipping activity {} --> {} - missing sequence {}", + m.from(), m.to(), m.to()); + + generate_return(m, ostr); + + ostr << indent(1) << "deactivate " << to_alias << std::endl; + + visited.pop_back(); + } + else if (m.type() == message_t::kIf) { + print_debug(m, ostr); + ostr << indent(1) << "alt"; + if (const auto &text = m.condition_text(); text.has_value()) + ostr << " " << render_message_text(text.value()); + ostr << '\n'; + } + else if (m.type() == message_t::kElseIf) { + print_debug(m, ostr); + ostr << indent(1) << "else"; + if (const auto &text = m.condition_text(); text.has_value()) + ostr << " " << render_message_text(text.value()); + ostr << '\n'; + } + else if (m.type() == message_t::kElse) { + print_debug(m, ostr); + ostr << indent(1) << "else\n"; + } + else if (m.type() == message_t::kIfEnd) { + ostr << indent(1) << "end\n"; + } + else if (m.type() == message_t::kWhile) { + print_debug(m, ostr); + ostr << indent(1) << "loop"; + if (const auto &text = m.condition_text(); text.has_value()) + ostr << " " << render_message_text(text.value()); + ostr << '\n'; + } + else if (m.type() == message_t::kWhileEnd) { + ostr << indent(1) << "end\n"; + } + else if (m.type() == message_t::kFor) { + print_debug(m, ostr); + ostr << indent(1) << "loop"; + if (const auto &text = m.condition_text(); text.has_value()) + ostr << " " << render_message_text(text.value()); + ostr << '\n'; + } + else if (m.type() == message_t::kForEnd) { + ostr << "end\n"; + } + else if (m.type() == message_t::kDo) { + print_debug(m, ostr); + ostr << indent(1) << "loop"; + if (const auto &text = m.condition_text(); text.has_value()) + ostr << " " << render_message_text(text.value()); + ostr << '\n'; + } + else if (m.type() == message_t::kDoEnd) { + ostr << indent(1) << "end\n"; + } + else if (m.type() == message_t::kTry) { + print_debug(m, ostr); + ostr << indent(1) << "critical\n"; + } + else if (m.type() == message_t::kCatch) { + print_debug(m, ostr); + ostr << indent(1) << "option " + << render_message_text(m.message_name()) << '\n'; + } + else if (m.type() == message_t::kTryEnd) { + print_debug(m, ostr); + ostr << indent(1) << "end\n"; + } + else if (m.type() == message_t::kSwitch) { + print_debug(m, ostr); + ostr << indent(1) << "alt\n"; + } + else if (m.type() == message_t::kCase) { + print_debug(m, ostr); + ostr << indent(1) << "else " + << render_message_text(m.message_name()) << '\n'; + } + else if (m.type() == message_t::kSwitchEnd) { + ostr << indent(1) << "end\n"; + } + else if (m.type() == message_t::kConditional) { + print_debug(m, ostr); + ostr << indent(1) << "alt"; + if (const auto &text = m.condition_text(); text.has_value()) + ostr << " " << render_message_text(text.value()); + ostr << '\n'; + } + else if (m.type() == message_t::kConditionalElse) { + print_debug(m, ostr); + ostr << indent(1) << "else\n"; + } + else if (m.type() == message_t::kConditionalEnd) { + ostr << indent(1) << "end\n"; + } + } +} + +void generator::generate_participant( + std::ostream &ostr, const std::string &name) const +{ + auto p = model().get(name); + + if (!p.has_value()) { + LOG_WARN("Cannot find participant {} from `participants_order` option", + name); + return; + } + + generate_participant(ostr, p.value().id(), true); +} + +void generator::generate_participant( + std::ostream &ostr, common::id_t id, bool force) const +{ + common::id_t participant_id{0}; + + if (!force) { + for (const auto pid : model().active_participants()) { + if (pid == id) { + participant_id = pid; + break; + } + } + } + else + participant_id = id; + + if (participant_id == 0) + return; + + if (is_participant_generated(participant_id)) + return; + + const auto &participant = + model().get_participant(participant_id).value(); + + if (participant.type_name() == "method") { + const auto class_id = + model() + .get_participant(participant_id) + .value() + .class_id(); + + if (is_participant_generated(class_id)) + return; + + const auto &class_participant = + model().get_participant(class_id).value(); + + print_debug(class_participant, ostr); + + ostr << indent(1) << "participant " << class_participant.alias() + << " as " + << render_participant_name(config().using_namespace().relative( + class_participant.full_name(false))); + + ostr << '\n'; + + generated_participants_.emplace(class_id); + } + else if ((participant.type_name() == "function" || + participant.type_name() == "function_template") && + config().combine_free_functions_into_file_participants()) { + // Create a single participant for all functions declared in a + // single file + const auto &file_path = + model() + .get_participant(participant_id) + .value() + .file(); + + assert(!file_path.empty()); + + const auto file_id = common::to_id(file_path); + + if (is_participant_generated(file_id)) + return; + + auto participant_name = util::path_to_url(std::filesystem::relative( + std::filesystem::path{file_path}, config().root_directory()) + .string()); + + ostr << indent(1) << "participant " << fmt::format("C_{:022}", file_id) + << " as " << render_participant_name(participant_name); + ostr << '\n'; + + generated_participants_.emplace(file_id); + } + else { + print_debug(participant, ostr); + + ostr << indent(1) << "participant " << participant.alias() << " as " + << config().using_namespace().relative( + participant.full_name(false)); + ostr << '\n'; + + generated_participants_.emplace(participant_id); + } +} + +bool generator::is_participant_generated(common::id_t id) const +{ + return std::find(generated_participants_.begin(), + generated_participants_.end(), + id) != generated_participants_.end(); +} + +std::string generator::generate_alias( + const model::participant &participant) const +{ + if ((participant.type_name() == "function" || + participant.type_name() == "function_template") && + config().combine_free_functions_into_file_participants()) { + const auto file_id = common::to_id(participant.file()); + + return fmt::format("C_{:022}", file_id); + } + + return participant.alias(); +} + +void generator::generate_diagram(std::ostream &ostr) const +{ + model().print(); + + ostr << "sequenceDiagram\n"; + + if (config().participants_order.has_value) { + for (const auto &p : config().participants_order()) { + LOG_DBG("Pregenerating participant {}", p); + generate_participant(ostr, p); + } + } + + bool star_participant_generated{false}; + + for (const auto &ft : config().from_to()) { + // First, find the sequence of activities from 'from' location + // to 'to' location + assert(ft.size() == 2); + + const auto &from_location = ft.front(); + const auto &to_location = ft.back(); + + auto from_activity_id = model().get_from_activity_id(from_location); + auto to_activity_id = model().get_to_activity_id(to_location); + + if (from_activity_id == 0 || to_activity_id == 0) + continue; + + auto message_chains_unique = model().get_all_from_to_message_chains( + from_activity_id, to_activity_id); + + for (const auto &mc : message_chains_unique) { + const auto &from = + model().get_participant(from_activity_id); + + if (from.value().type_name() == "method" || + config().combine_free_functions_into_file_participants()) { + if (!star_participant_generated) { + ostr << indent(1) << "participant *\n"; + star_participant_generated = true; + } + generate_participant(ostr, from_activity_id); + ostr << indent(1) << "* " + << common::generators::mermaid::to_mermaid( + message_t::kCall) + << " " << generate_alias(from.value()) << " : " + << from.value().message_name( + select_method_arguments_render_mode()) + << std::endl; + } + + for (const auto &m : mc) { + generate_call(m, ostr); + } + } + } + + for (const auto &to_location : config().to()) { + auto to_activity_id = model().get_to_activity_id(to_location); + + if (to_activity_id == 0) + continue; + + auto message_chains_unique = + model().get_all_from_to_message_chains(0, to_activity_id); + + for (const auto &mc : message_chains_unique) { + const auto from_activity_id = mc.front().from(); + + const auto &from = + model().get_participant(from_activity_id); + + if (from.value().type_name() == "method" || + config().combine_free_functions_into_file_participants()) { + generate_participant(ostr, from_activity_id); + ostr << indent(1) << "* " + << common::generators::mermaid::to_mermaid( + message_t::kCall) + << " " << generate_alias(from.value()) << " : " + << from.value().message_name( + select_method_arguments_render_mode()) + << std::endl; + } + + for (const auto &m : mc) { + generate_call(m, ostr); + } + } + } + + for (const auto &sf : config().from()) { + if (sf.location_type == location_t::function) { + common::model::diagram_element::id_t start_from{0}; + for (const auto &[k, v] : model().sequences()) { + const auto &caller = *model().participants().at(v.from()); + std::string vfrom = caller.full_name(false); + if (vfrom == sf.location) { + LOG_DBG("Found sequence diagram start point: {}", k); + start_from = k; + break; + } + } + + if (start_from == 0) { + LOG_WARN("Failed to find participant with {} for start_from " + "condition", + sf.location); + continue; + } + + // Use this to break out of recurrent loops + std::vector + visited_participants; + + const auto &from = + model().get_participant(start_from); + + if (!from.has_value()) { + LOG_WARN("Failed to find participant {} for start_from " + "condition", + sf.location); + continue; + } + + generate_participant(ostr, start_from); + + std::string from_alias = generate_alias(from.value()); + + model::function::message_render_mode render_mode = + select_method_arguments_render_mode(); + + // For methods or functions in diagrams where they are combined into + // file participants, we need to add an 'entry' point call to know + // which method relates to the first activity for this 'start_from' + // condition + if (from.value().type_name() == "method" || + config().combine_free_functions_into_file_participants()) { + ostr << indent(1) << "* " + << common::generators::mermaid::to_mermaid( + message_t::kCall) + << " " << from_alias << " : " + << from.value().message_name(render_mode) << std::endl; + } + + ostr << indent(1) << "activate " << from_alias << std::endl; + + generate_activity( + model().get_activity(start_from), ostr, visited_participants); + + if (from.value().type_name() == "method" || + config().combine_free_functions_into_file_participants()) { + + if (!from.value().is_void()) { + ostr << indent(1) << from_alias << " " + << common::generators::mermaid::to_mermaid( + message_t::kReturn) + << " *" + << " : "; + + if (config().generate_return_types()) + ostr << from.value().return_type(); + + ostr << '\n'; + } + } + + ostr << indent(1) << "deactivate " << from_alias << std::endl; + } + else { + // TODO: Add support for other sequence start location types + continue; + } + } +} + +model::function::message_render_mode +generator::select_method_arguments_render_mode() const +{ + if (config().generate_method_arguments() == + config::method_arguments::abbreviated) + return model::function::message_render_mode::abbreviated; + + if (config().generate_method_arguments() == config::method_arguments::none) + return model::function::message_render_mode::no_arguments; + + return model::function::message_render_mode::full; +} + +} // namespace clanguml::sequence_diagram::generators::mermaid diff --git a/src/sequence_diagram/generators/mermaid/sequence_diagram_generator.h b/src/sequence_diagram/generators/mermaid/sequence_diagram_generator.h new file mode 100644 index 000000000..823f729f2 --- /dev/null +++ b/src/sequence_diagram/generators/mermaid/sequence_diagram_generator.h @@ -0,0 +1,141 @@ +/** + * @file src/sequence_diagram/generators/mermaid/sequence_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/mermaid/generator.h" +#include "config/config.h" +#include "sequence_diagram/model/diagram.h" +#include "sequence_diagram/visitor/translation_unit_visitor.h" +#include "util/util.h" + +#include + +#include +#include +#include +#include + +namespace clanguml { +namespace sequence_diagram { +namespace generators { +namespace mermaid { + +using diagram_config = clanguml::config::sequence_diagram; +using diagram_model = clanguml::sequence_diagram::model::diagram; + +template +using common_generator = clanguml::common::generators::mermaid::generator; + +/** + * @brief Sequence diagram MermaidJS generator + */ +class generator : public common_generator { +public: + generator(diagram_config &config, diagram_model &model); + + using common_generator::generate; + + /** + * @brief Main generator method. + * + * This method is called first and coordinates the entire diagram + * generation. + * + * @param ostr Output stream. + */ + void generate_diagram(std::ostream &ostr) const override; + + /** + * @brief Generate sequence diagram message. + * + * @param m Message model + * @param ostr Output stream + */ + void generate_call(const clanguml::sequence_diagram::model::message &m, + std::ostream &ostr) const; + + /** + * @brief Generate sequence diagram return message + * + * @param m Message model + * @param ostr Output stream + */ + void generate_return(const clanguml::sequence_diagram::model::message &m, + std::ostream &ostr) const; + + /** + * @brief Generate sequence diagram participant + * + * @param ostr Output stream + * @param id Participant id + * @param force If true, generate the participant even if its not in + * the set of active participants + * @return Id of the generated participant + */ + void generate_participant( + std::ostream &ostr, common::id_t id, bool force = false) const; + + /** + * @brief Generate sequence diagram participant by name + * + * This is convenience wrapper over `generate_participant()` by id. + * + * @param ostr Output stream + * @param name Full participant name + */ + void generate_participant( + std::ostream &ostr, const std::string &name) const; + + /** + * @brief Generate sequence diagram activity. + * + * @param a Activity model + * @param ostr Output stream + * @param visited List of already visited participants, this is necessary + * for breaking infinite recursion on recursive calls + */ + void generate_activity(const clanguml::sequence_diagram::model::activity &a, + std::ostream &ostr, + std::vector &visited) const; + +private: + /** + * @brief Check if specified participant has already been generated. + * + * @param id Participant id. + * @return True, if participant has already been generated. + */ + bool is_participant_generated(common::id_t id) const; + + /** + * @brief Generate MermaidJS alias for participant + * + * @param participant Sequence diagram participant model + * @return Particpant alias + */ + std::string generate_alias(const model::participant &participant) const; + + mutable std::set generated_participants_; + model::function::message_render_mode + select_method_arguments_render_mode() const; +}; + +} // namespace mermaid +} // namespace generators +} // namespace sequence_diagram +} // namespace clanguml diff --git a/tests/t20001/test_case.h b/tests/t20001/test_case.h index 2b02bab19..f8706b2dc 100644 --- a/tests/t20001/test_case.h +++ b/tests/t20001/test_case.h @@ -66,4 +66,9 @@ TEST_CASE("t20001", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } diff --git a/tests/t20002/test_case.h b/tests/t20002/test_case.h index 8b281240e..fdfd40810 100644 --- a/tests/t20002/test_case.h +++ b/tests/t20002/test_case.h @@ -60,4 +60,10 @@ TEST_CASE("t20002", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } diff --git a/tests/t20003/test_case.h b/tests/t20003/test_case.h index d5bbbbc4e..f27ced2e0 100644 --- a/tests/t20003/test_case.h +++ b/tests/t20003/test_case.h @@ -55,4 +55,10 @@ TEST_CASE("t20003", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } diff --git a/tests/t20004/test_case.h b/tests/t20004/test_case.h index 82f483c58..eb6ac7477 100644 --- a/tests/t20004/test_case.h +++ b/tests/t20004/test_case.h @@ -82,4 +82,10 @@ TEST_CASE("t20004", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20005/test_case.h b/tests/t20005/test_case.h index e94360772..49f0babe3 100644 --- a/tests/t20005/test_case.h +++ b/tests/t20005/test_case.h @@ -56,4 +56,10 @@ TEST_CASE("t20005", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20007/test_case.h b/tests/t20007/test_case.h index c995b9f54..c16b95d60 100644 --- a/tests/t20007/test_case.h +++ b/tests/t20007/test_case.h @@ -66,4 +66,10 @@ TEST_CASE("t20007", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20008/test_case.h b/tests/t20008/test_case.h index 577796620..47f8a529f 100644 --- a/tests/t20008/test_case.h +++ b/tests/t20008/test_case.h @@ -76,4 +76,10 @@ TEST_CASE("t20008", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20009/test_case.h b/tests/t20009/test_case.h index e36f41fe0..b11a4597b 100644 --- a/tests/t20009/test_case.h +++ b/tests/t20009/test_case.h @@ -68,4 +68,10 @@ TEST_CASE("t20009", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20010/test_case.h b/tests/t20010/test_case.h index 5a5cc9f07..600784b08 100644 --- a/tests/t20010/test_case.h +++ b/tests/t20010/test_case.h @@ -70,4 +70,10 @@ TEST_CASE("t20010", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20011/test_case.h b/tests/t20011/test_case.h index 8d44803cc..26cea6eff 100644 --- a/tests/t20011/test_case.h +++ b/tests/t20011/test_case.h @@ -63,4 +63,10 @@ TEST_CASE("t20011", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20013/test_case.h b/tests/t20013/test_case.h index e3b826a7d..b885fe88a 100644 --- a/tests/t20013/test_case.h +++ b/tests/t20013/test_case.h @@ -67,4 +67,10 @@ TEST_CASE("t20013", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20014/test_case.h b/tests/t20014/test_case.h index 00d194a48..e5910ec70 100644 --- a/tests/t20014/test_case.h +++ b/tests/t20014/test_case.h @@ -66,4 +66,10 @@ TEST_CASE("t20014", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20015/test_case.h b/tests/t20015/test_case.h index 0a05b4a6d..c56f24997 100644 --- a/tests/t20015/test_case.h +++ b/tests/t20015/test_case.h @@ -61,4 +61,10 @@ TEST_CASE("t20015", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20016/test_case.h b/tests/t20016/test_case.h index 15e35993e..92ef295f3 100644 --- a/tests/t20016/test_case.h +++ b/tests/t20016/test_case.h @@ -59,4 +59,10 @@ TEST_CASE("t20016", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20017/test_case.h b/tests/t20017/test_case.h index 8f5e26fb4..e617b4a85 100644 --- a/tests/t20017/test_case.h +++ b/tests/t20017/test_case.h @@ -73,4 +73,10 @@ TEST_CASE("t20017", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20018/test_case.h b/tests/t20018/test_case.h index 1eea53f77..cb3d8d69f 100644 --- a/tests/t20018/test_case.h +++ b/tests/t20018/test_case.h @@ -76,4 +76,10 @@ TEST_CASE("t20018", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20019/test_case.h b/tests/t20019/test_case.h index 903efedaa..68de2fbd8 100644 --- a/tests/t20019/test_case.h +++ b/tests/t20019/test_case.h @@ -59,4 +59,10 @@ TEST_CASE("t20019", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20020/test_case.h b/tests/t20020/test_case.h index 5314b9ef0..63538cbf0 100644 --- a/tests/t20020/test_case.h +++ b/tests/t20020/test_case.h @@ -79,4 +79,10 @@ TEST_CASE("t20020", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20021/test_case.h b/tests/t20021/test_case.h index f2a980006..c4016afbb 100644 --- a/tests/t20021/test_case.h +++ b/tests/t20021/test_case.h @@ -85,4 +85,10 @@ TEST_CASE("t20021", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20022/test_case.h b/tests/t20022/test_case.h index 026930a4c..a3a2bbf02 100644 --- a/tests/t20022/test_case.h +++ b/tests/t20022/test_case.h @@ -53,4 +53,10 @@ TEST_CASE("t20022", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20023/test_case.h b/tests/t20023/test_case.h index 4f17bd6bd..087e2b937 100644 --- a/tests/t20023/test_case.h +++ b/tests/t20023/test_case.h @@ -57,4 +57,10 @@ TEST_CASE("t20023", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20024/test_case.h b/tests/t20024/test_case.h index 57d13c91c..9f5768b0c 100644 --- a/tests/t20024/test_case.h +++ b/tests/t20024/test_case.h @@ -63,4 +63,10 @@ TEST_CASE("t20024", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20025/test_case.h b/tests/t20025/test_case.h index b468ad25d..b46e02458 100644 --- a/tests/t20025/test_case.h +++ b/tests/t20025/test_case.h @@ -57,4 +57,10 @@ TEST_CASE("t20025", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20026/test_case.h b/tests/t20026/test_case.h index cdd561562..da5dd6017 100644 --- a/tests/t20026/test_case.h +++ b/tests/t20026/test_case.h @@ -51,4 +51,10 @@ TEST_CASE("t20026", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20027/test_case.h b/tests/t20027/test_case.h index 4562a71d1..5476d0fec 100644 --- a/tests/t20027/test_case.h +++ b/tests/t20027/test_case.h @@ -53,4 +53,10 @@ TEST_CASE("t20027", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20028/test_case.h b/tests/t20028/test_case.h index b596fa7da..01174a4de 100644 --- a/tests/t20028/test_case.h +++ b/tests/t20028/test_case.h @@ -59,4 +59,10 @@ TEST_CASE("t20028", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20029/test_case.h b/tests/t20029/test_case.h index 6438d32f6..17900066f 100644 --- a/tests/t20029/test_case.h +++ b/tests/t20029/test_case.h @@ -83,4 +83,10 @@ TEST_CASE("t20029", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20030/test_case.h b/tests/t20030/test_case.h index aced91278..167e5ebb4 100644 --- a/tests/t20030/test_case.h +++ b/tests/t20030/test_case.h @@ -62,4 +62,10 @@ TEST_CASE("t20030", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20031/test_case.h b/tests/t20031/test_case.h index 707a91461..01a08bee4 100644 --- a/tests/t20031/test_case.h +++ b/tests/t20031/test_case.h @@ -65,4 +65,10 @@ TEST_CASE("t20031", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20032/test_case.h b/tests/t20032/test_case.h index 9f5d42422..6003423a6 100644 --- a/tests/t20032/test_case.h +++ b/tests/t20032/test_case.h @@ -75,4 +75,10 @@ TEST_CASE("t20032", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20033/test_case.h b/tests/t20033/test_case.h index 22eaff7b9..41703b185 100644 --- a/tests/t20033/test_case.h +++ b/tests/t20033/test_case.h @@ -54,4 +54,10 @@ TEST_CASE("t20033", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20034/test_case.h b/tests/t20034/test_case.h index d184d1ac9..3ad62327f 100644 --- a/tests/t20034/test_case.h +++ b/tests/t20034/test_case.h @@ -73,4 +73,10 @@ TEST_CASE("t20034", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20035/test_case.h b/tests/t20035/test_case.h index 67b3614fa..b73c22f1a 100644 --- a/tests/t20035/test_case.h +++ b/tests/t20035/test_case.h @@ -53,4 +53,10 @@ TEST_CASE("t20035", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/t20036/test_case.h b/tests/t20036/test_case.h index 5f9ca877b..e3e6c548f 100644 --- a/tests/t20036/test_case.h +++ b/tests/t20036/test_case.h @@ -69,4 +69,10 @@ TEST_CASE("t20036", "[test-case][sequence]") save_json(config.output_directory(), diagram->name + ".json", j); } + + { + auto mmd = generate_sequence_mermaid(diagram, *model); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd); + } } \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index e49adbf06..e760bbab8 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -235,6 +235,14 @@ std::string generate_class_mermaid( config, model); } +std::string generate_sequence_mermaid( + std::shared_ptr config, + clanguml::sequence_diagram::model::diagram &model) +{ + return detail::generate_diagram_mermaid( + config, model); +} + template void save_diagram(const std::filesystem::path &path, const T &diagram) { diff --git a/util/generate_mermaid.py b/util/generate_mermaid.py index a7cccce08..51fa120b0 100644 --- a/util/generate_mermaid.py +++ b/util/generate_mermaid.py @@ -38,7 +38,7 @@ def print_usage(): for f in files: try: print(f'Generating Mermaid diagram from {f}') - f_svg = Path(f).with_suffix('.png') + f_svg = Path(f).with_suffix('.svg') subprocess.check_call(['mmdc', '-i', f, '-o', f_svg]) except subprocess.CalledProcessError: ok = 1