0.5.4
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
config.h
Go to the documentation of this file.
1/**
2 * @file src/config/config.h
3 *
4 * Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18#pragma once
19
20#include "common/model/enums.h"
21#include "common/types.h"
22#include "option.h"
23#include "util/util.h"
24
25#include <spdlog/spdlog.h>
26#include <yaml-cpp/yaml.h>
27
28#include <map>
29#include <memory>
30#include <optional>
31#include <regex>
32#include <string>
33#include <variant>
34#include <vector>
35
36namespace clanguml {
37
38namespace cli {
39struct runtime_config;
40} // namespace cli
41
42/**
43 * @brief Configuration file related classes
44 *
45 * \image html config_class.svg
46 */
47namespace config {
48
49/*! Select how the method arguments should be rendered */
50enum class method_arguments {
51 full, /*! Full */
52 abbreviated, /*! Abrreviate, string between '(' and ')' is cropped */
53 none /*! Empty string between '(' and ')' */
54};
55
56std::string to_string(method_arguments ma);
57
58/*! Types of methods, which can be used in diagram filters */
59enum class method_type {
65 deleted,
67};
68
69std::string to_string(method_type mt);
70
71/*! Types of call expressions, which can be used in sequence diagram filters */
72enum class callee_type {
77 static_,
78 method,
81 lambda,
84};
85
86std::string to_string(callee_type mt);
87
88/*! How packages in diagrams should be generated */
89enum class package_type_t {
90 kNamespace, /*!< From namespaces */
91 kDirectory, /*!< From directories */
92 kModule /*!< From modules */
93};
94
95std::string to_string(package_type_t mt);
96
97/*! How class methods and members should be ordered in diagrams */
98enum class member_order_t {
99 lexical, /*! Lexical order based on entire method or member signature
100 without type */
101 as_is /*! As written in source code */
102};
103
104std::string to_string(member_order_t mt);
105
106/*! Which comment parser should be used */
108 plain, /*!< Basic string parser */
109 clang /*!< Clang's internal comment parser with structured output */
110};
111
112std::string to_string(comment_parser_t cp);
113
115 std::map<common::model::relationship_t, std::pair<std::string, std::string>>
117};
118
119enum class filter_mode_t {
120 basic, /*!< Default filter structure without logical operators */
121 advanced /*!< Advanced filter config with logical operators */
122};
123
124std::string to_string(filter_mode_t cp);
125
126/**
127 * @brief PlantUML diagram config section
128 *
129 * This configuration option can be used to add any PlantUML directives
130 * before or after the generated diagram, such as diagram name, or custom
131 * notes.
132 */
133struct plantuml {
134 /*! List of directives to add before diagram */
135 std::vector<std::string> before;
136 /*! List of directives to add before diagram */
137 std::vector<std::string> after;
138 /*! Command template to render diagram using PlantUML */
139 std::string cmd;
140 /*! Provide customized styles for various elements */
141 std::map<std::string, std::string> style;
142
143 std::optional<std::string> get_style(
144 common::model::relationship_t relationship_type) const;
145
146 std::optional<std::string> get_style(const std::string &element_type) const;
147
148 void append(const plantuml &r);
149};
150
151/**
152 * @brief MermaidJS diagram config section
153 *
154 * This configuration option can be used to add any MermaidJS directives
155 * before or after the generated diagram, such as diagram name, or custom
156 * notes.
157 */
158struct mermaid {
159 /*! List of directives to add before diagram */
160 std::vector<std::string> before;
161 /*! List of directives to add before diagram */
162 std::vector<std::string> after;
163 /*! Command template to render diagram using MermaidJS */
164 std::string cmd;
165
166 void append(const mermaid &r);
167};
168
170
171std::string to_string(context_direction_t cd);
172
175 unsigned radius{0};
177 std::vector<common::model::relationship_t> relationships;
178};
179
180/**
181 * @brief Definition of diagram template
182 */
184 /*! Template description */
185 std::string description;
186
187 /*! Diagram type */
189
190 /*! Template which will be processed by Inja to generate the actual
191 * diagram
192 */
193 std::string jinja_template;
194};
195
196struct filter {
197 std::shared_ptr<filter> anyof;
198 std::shared_ptr<filter> allof;
199
200 /*! @brief Namespaces filter
201 *
202 * Example:
203 *
204 * ```yaml
205 * include
206 * namespaces:
207 * - ns1::ns2
208 * - r: ".*detail.*"
209 * ```
210 */
211 std::vector<common::namespace_or_regex> namespaces;
212
213 /*! @brief Modules filter
214 *
215 * Example:
216 *
217 * ```yaml
218 * include
219 * modules:
220 * - app.module1
221 * - r: ".*internal.*"
222 * ```
223 */
224 std::vector<common::string_or_regex> modules;
225
226 /*! @brief Access type filter
227 *
228 * This filter allows to filter class members methods based on their access:
229 * - public
230 * - private
231 *
232 * Example:
233 *
234 * ```yaml
235 * include:
236 * module_access:
237 * - public
238 * ```
239 */
240 std::vector<common::model::module_access_t> module_access;
241
242 /*! @brief Elements filter
243 *
244 * Example:
245 *
246 * ```yaml
247 * exclude:
248 * elements:
249 * - ns1::ns2::ClassA
250 * - r: ".*Enum.*"
251 * ```
252 */
253 std::vector<common::string_or_regex> elements;
254
255 /*! @brief Element types filter
256 *
257 * This filter allows to filter diagram elements based on their type.
258 *
259 * ```yaml
260 * include:
261 * elements:
262 * - class
263 * - concept
264 * ```
265 */
266 std::vector<std::string> element_types;
267
268 /*! @brief Relationships filter
269 *
270 * This filter allows to filter relationships included in the diagram.
271 *
272 * Example:
273 *
274 * ```yaml
275 * include:
276 * relationships:
277 * - inheritance
278 * ```
279 */
280 std::vector<common::model::relationship_t> relationships;
281
282 /*! @brief Access type filter
283 *
284 * This filter allows to filter class members methods based on their access:
285 * - public
286 * - protected
287 * - private
288 *
289 * Example:
290 *
291 * ```yaml
292 * include:
293 * access:
294 * - public
295 * ```
296 */
297 std::vector<common::model::access_t> access;
298
299 /*! @brief Subclasses filter
300 *
301 * This filter allows to filter classes based on having a specified parent.
302 * This filter also matches the specified parent itself.
303 *
304 * Example:
305 *
306 * ```yaml
307 * include:
308 * subclasses:
309 * - ns1::ns2::ClassA
310 * ```
311 */
312 std::vector<common::string_or_regex> subclasses;
313
314 /*! @brief Parents filter
315 *
316 * This filter allows to filter classes based on being a base class of a
317 * specified child.
318 * This filter also matches the specified child itself.
319 *
320 * Example:
321 *
322 * ```yaml
323 * include:
324 * parents:
325 * - ns1::ns2::ChildA
326 * ```
327 */
328 std::vector<common::string_or_regex> parents;
329
330 /*! @brief Specializations filter
331 *
332 * This filter allows to filter class template specializations of a specific
333 * template.
334 * This filter also matches the specified template itself.
335 *
336 * Example:
337 *
338 * ```yaml
339 * include:
340 * specializations:
341 * - ns1::ns2::ClassA<int,T>
342 * ```
343 */
344 std::vector<common::string_or_regex> specializations;
345
346 /*! @brief Dependants filter
347 *
348 * This filter allows to filter classes based on whether they are a
349 * dependant on a specified class.
350 *
351 * Example:
352 *
353 * ```yaml
354 * include:
355 * dependants:
356 * - ns1::ns2::ClassA
357 * ```
358 */
359 std::vector<common::string_or_regex> dependants;
360
361 /*! @brief Dependencies filter
362 *
363 * This filter allows to filter classes based on whether they are a
364 * dependency of a specified class.
365 *
366 * Example:
367 *
368 * ```yaml
369 * include:
370 * dependencies:
371 * - ns1::ns2::ClassA
372 * ```
373 */
374 std::vector<common::string_or_regex> dependencies;
375
376 /*! @brief Context filter
377 *
378 * This filter allows to filter classes based on whether they have at least
379 * one direct relation to the specified class.
380 *
381 * Example:
382 *
383 * ```yaml
384 * include:
385 * context:
386 * - ns1::ns2::ClassA
387 * - r: ns1::ns2::ClassB<.*>
388 * - match:
389 * pattern: ns1::ns2::ClassA
390 * radius: 3
391 * ```
392 */
393 std::vector<context_config> context;
394
395 /*! @brief Paths filter
396 *
397 * This filter allows to filter diagram elements based on the source
398 * location of their declaration.
399 *
400 * Example:
401 *
402 * ```yaml
403 * include:
404 * context:
405 * - ns1::ns2::ClassA
406 * ```
407 */
408 std::vector<std::string> paths;
409
410 /*! @brief Method types filter
411 *
412 * This filter allows to filter class methods based on their category.
413 *
414 * @see method_type
415 *
416 * Example:
417 *
418 * ```yaml
419 * exclude:
420 * method_types:
421 * - constructor
422 * - operator
423 * - assignment
424 * ```
425 */
426 std::vector<method_type> method_types;
427
428 /*! @brief Callee types filter
429 *
430 * This filter allows to filter sequence diagram calls by callee types.
431 *
432 * @see method_type
433 *
434 * Example:
435 *
436 * ```yaml
437 * exclude:
438 * callee_types:
439 * - constructor
440 * - operator
441 * ```
442 */
443 std::vector<callee_type> callee_types;
444};
445
446enum class hint_t { up, down, left, right, together, row, column };
447
448std::string to_string(hint_t t);
449
452 std::variant<std::string, std::vector<std::string>> entity;
453};
454
455using layout_hints = std::map<std::string, std::vector<layout_hint>>;
456
458 std::map</* path */ std::string, /* pattern */ std::string> link;
459 std::map</* path */ std::string, /* pattern */ std::string> tooltip;
460
461 std::optional<std::pair<std::string, std::string>> get_link_pattern(
462 const std::string &path) const
463 {
465 }
466
467 std::optional<std::pair<std::string, std::string>> get_tooltip_pattern(
468 const std::string &path) const
469 {
471 }
472};
473
475 std::string branch;
476 std::string revision;
477 std::string commit;
478 std::string toplevel;
479};
480
482 std::map<unsigned int, common::model::relationship_t> argument_hints;
484
487 : default_hint{def}
488 {
489 }
490
491 common::model::relationship_t get(unsigned int argument_index) const
492 {
493 if (argument_hints.count(argument_index) > 0)
494 return argument_hints.at(argument_index);
495
496 return default_hint;
497 }
498};
499
500using relationship_hints_t = std::map<std::string, relationship_hint_t>;
501
502using type_aliases_t = std::map<std::string, std::string>;
503
505 bool operator()(const std::string &a, const std::string &b) const
506 {
507 if (a.size() == b.size())
508 return a > b;
509
510 return a.size() > b.size();
511 }
512};
514 std::map<std::string, std::string, type_aliases_longer_first_comparator>;
515
517
518std::string to_string(location_t cp);
519
522 std::string location;
523};
524
525/**
526 * @brief Represents subset of inheritable configuration options
527 *
528 * This class contains a subset of configuration options, which are inherited
529 * from the top level of the configuration to the diagrams.
530 *
531 * @embed{inheritable_diagram_options_context_class.svg}
532 */
534 virtual ~inheritable_diagram_options() = default;
535
536 void inherit(const inheritable_diagram_options &parent);
537
538 std::string simplify_template_type(std::string full_name) const;
539
540 /**
541 * @brief Whether the diagram element should be fully qualified in diagram
542 *
543 * This method determines whether an elements' name should include
544 * fully qualified namespace name (however relative to using_namespace), or
545 * whether it should just contain it's name. This depends on whether the
546 * diagram has packages and if they are based on namespaces or sth else.
547 *
548 * @return True, if element should include it's namespace
549 */
551
552 /**
553 * @brief Get reference to `relative_to` diagram config option
554 *
555 * This method is only to allow access to `relative_to` for loading
556 * and adjusting configuration file and for making test cases work.
557 *
558 * Instead use @see config::diagram::root_directory() method.
559 *
560 * @return Reference to `relative_to` config option.
561 */
563
568 "include_relations_also_as_members", true};
570 option<bool> include_system_headers{"include_system_headers", false};
576 "generate_method_arguments", method_arguments::full};
578 "generate_concept_requirements", true};
579 option<bool> group_methods{"group_methods", true};
581 "member_order", member_order_t::lexical};
582 option<bool> generate_packages{"generate_packages", false};
584 "package_type", package_type_t::kNamespace};
586 "generate_template_argument_dependencies", true};
588 "skip_redundant_dependencies", true};
592 // This is the absolute filesystem path to the directory containing
593 // the current .clang-uml config file - it is set automatically
595 option<bool> generate_system_headers{"generate_system_headers", false};
598 "type_aliases", option_inherit_mode::kAppend};
600 "comment_parser", comment_parser_t::plain};
602 "combine_free_functions_into_file_participants", false};
603 option<bool> inline_lambda_messages{"inline_lambda_messages", false};
604 option<bool> generate_return_types{"generate_return_types", false};
606 "generate_condition_statements", false};
608 option<bool> generate_message_comments{"generate_message_comments", false};
610 "message_comment_width", clanguml::util::kDefaultMessageCommentWidth};
611 option<bool> debug_mode{"debug_mode", false};
612 option<bool> generate_metadata{"generate_metadata", true};
613 option<bool> allow_empty_diagrams{"allow_empty_diagrams", false};
614
615protected:
616 // This is the relative path with respect to the `base_directory`,
617 // against which all matches are made, if not provided it defaults to
618 // the `base_directory`
619 //
620 // This option should not be accessed directly in code - instead use
621 // @see config::diagram::root_directory()
623
624 friend YAML::Emitter &operator<<(
625 YAML::Emitter &out, const inheritable_diagram_options &c);
626};
627
628/**
629 * @brief Common diagram configuration type
630 *
631 * This class provides common interface for diagram configuration sections
632 * of different diagram types.
633 *
634 * @embed{diagram_config_hierarchy_class.svg}
635 */
637 ~diagram() override = default;
638
639 virtual common::model::diagram_t type() const = 0;
640
641 /**
642 * @brief Filter translation units based on glob patterns
643 *
644 * @return List of translation unit paths
645 */
646 std::vector<std::string> glob_translation_units(
647 const std::vector<std::string> &compilation_database_files) const;
648
649 /**
650 * @brief Make path relative to the `relative_to` config option
651 *
652 * @param p Input path
653 * @return Relative path
654 */
655 std::filesystem::path make_path_relative(
656 const std::filesystem::path &p) const;
657
658 /**
659 * @brief Make module path relative to `using_module` configuration option
660 *
661 * @param p Input path
662 * @return Relative path
663 */
664 std::vector<std::string> make_module_relative(
665 const std::optional<std::string> &maybe_module) const;
666
667 /**
668 * @brief Returns absolute path of the `relative_to` option
669 *
670 * @return Absolute path of `relative_to`
671 */
672 std::filesystem::path root_directory() const;
673
674 std::optional<std::string> get_together_group(
675 const std::string &full_name) const;
676
677 /**
678 * @brief Initialize predefined set of C++ type aliases
679 *
680 * This method is responsible for setting up a predefined set of C++
681 * aliases to make the diagrams look nicer, for instance we want to have
682 * `std::string` instead of `std::basic_string<char>`.
683 */
685
686 std::string name;
687
689};
690
691/**
692 * @brief Class diagram configuration
693 */
694struct class_diagram : public diagram {
695 ~class_diagram() override = default;
696
697 common::model::diagram_t type() const override;
698
700};
701
702/**
703 * @brief Sequence diagram configuration
704 */
705struct sequence_diagram : public diagram {
706 ~sequence_diagram() override = default;
707
708 common::model::diagram_t type() const override;
709
711 option_with_alt_names_tag{}, "from", {"start_from"}};
714};
715
716/**
717 * @brief Package diagram configuration
718 */
719struct package_diagram : public diagram {
720 ~package_diagram() override = default;
721
722 common::model::diagram_t type() const override;
723};
724
725/**
726 * @brief Include diagram configuration
727 */
728struct include_diagram : public diagram {
729 ~include_diagram() override = default;
730
731 common::model::diagram_t type() const override;
732};
733
734/**
735 * @brief Represents entire configuration file
736 *
737 * This class contains all information loaded from the provided `clang-uml`
738 * configuration file, along with information inferred from the system
739 * such as current directory or Git context.
740 *
741 * The options in this class are not inheritable into specific diagram
742 * configurations.
743 *
744 * @embed{config_context_class.svg}
745 *
746 * The configuration file is loaded by invoking @see clanguml::config::load()
747 * function.
748 */
750 /*! Path to the directory containing compile_commands.json */
752 "compilation_database_dir", "."};
753
754 /*! List of compilation flags to be injected into the compilation
755 * commands from database
756 */
758
759 /*! List of compilation flags to be removed from the compilation
760 * commands from database
761 */
763 "remove_compile_flags"};
764
765 /*! Extract include paths by executing specified compiler driver.
766 * When the value is `'`, the command specified in compile_commands.json
767 * will be used. Otherwise the provided value will be treated as
768 * compiler executable (e.g. clang++-15) and executed.
769 */
771
772 /*! Diagrams output directory */
774
775 /*! Dictionary of user defined diagram templates, if any */
777 "diagram_templates"};
778
779 /*! Dictionary of diagrams, keys represent diagram names */
780 std::map<std::string, std::shared_ptr<diagram>> diagrams;
781
782 /**
783 * Initialize predefined diagram templates.
784 */
786
787 void inherit();
788};
789
790using config_ptr = std::unique_ptr<config>;
791
792/**
793 * @brief Load and parse `.clang-uml` configuration file
794 *
795 * This function takes the path to the configuration file and some options,
796 * parses the YAML file and creates a @see clanguml::config::config instance.
797 *
798 * @embed{load_config_sequence.svg}
799 *
800 * @param config_file Path to the configuration file
801 * @param inherit If true, common options will be propagated to diagram configs
802 * @param paths_relative_to_pwd Whether the paths in the configuration file
803 * should be relative to the parent directory of
804 * the configuration file or to the current
805 * directory (`$PWD`)
806 * @param no_metadata Whether the diagram should skip metadata at the end
807 * @param validate If true, perform schema validation
808 * @return Configuration instance
809 */
810config load(const std::string &config_file, bool inherit = true,
811 std::optional<bool> paths_relative_to_pwd = {},
812 std::optional<bool> no_metadata = {}, bool validate = true);
813
814} // namespace config
815
816namespace config {
817/** @defgroup yaml_emitters YAML serialization emitters
818 * Overloads for YAML serializations for various config types.
819 * @{
820 */
821YAML::Emitter &operator<<(YAML::Emitter &out, const config &c);
822
823YAML::Emitter &operator<<(
824 YAML::Emitter &out, const inheritable_diagram_options &c);
825
826YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f);
827
828YAML::Emitter &operator<<(YAML::Emitter &out, const plantuml &p);
829
830YAML::Emitter &operator<<(YAML::Emitter &out, const method_arguments &ma);
831
832YAML::Emitter &operator<<(YAML::Emitter &out, const generate_links_config &glc);
833
834YAML::Emitter &operator<<(YAML::Emitter &out, const git_config &gc);
835
836YAML::Emitter &operator<<(YAML::Emitter &out, const relationship_hint_t &rh);
837
838YAML::Emitter &operator<<(YAML::Emitter &out, const comment_parser_t &cp);
839
840YAML::Emitter &operator<<(YAML::Emitter &out, const hint_t &h);
841
842YAML::Emitter &operator<<(YAML::Emitter &out, const class_diagram &c);
843
844YAML::Emitter &operator<<(YAML::Emitter &out, const sequence_diagram &c);
845
846YAML::Emitter &operator<<(YAML::Emitter &out, const include_diagram &c);
847
848YAML::Emitter &operator<<(YAML::Emitter &out, const package_diagram &c);
849
850YAML::Emitter &operator<<(YAML::Emitter &out, const layout_hint &c);
851
852#ifdef _MSC_VER
853YAML::Emitter &operator<<(YAML::Emitter &out, const std::filesystem::path &p);
854
855YAML::Emitter &operator<<(
856 YAML::Emitter &out, const std::vector<std::filesystem::path> &p);
857#endif
858
859YAML::Emitter &operator<<(YAML::Emitter &out, const source_location &sc);
860
861template <typename T>
862YAML::Emitter &operator<<(YAML::Emitter &out, const option<T> &o)
863{
864 if (o.has_value) {
865 out << YAML::Key << o.name;
866 if constexpr (std::is_same_v<T, std::filesystem::path>)
867 out << YAML::Value << o.value.string();
868 else
869 out << YAML::Value << o.value;
870 }
871 return out;
872}
873/** @} */ // end of yaml_emitters
874
875} // namespace config
876
877namespace common::model {
878/** @addtogroup yaml_emitters YAML serialization emitters
879 * Overloads for YAML serializations for various model types.
880 * @{
881 */
882YAML::Emitter &operator<<(YAML::Emitter &out, const namespace_ &n);
883YAML::Emitter &operator<<(YAML::Emitter &out, const relationship_t &r);
884YAML::Emitter &operator<<(YAML::Emitter &out, const access_t &r);
885YAML::Emitter &operator<<(YAML::Emitter &out, const diagram_t &d);
886/** @} */ // end of yaml_emitters
887} // namespace common::model
888
889} // namespace clanguml