0.6.0
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-2025 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 "inja/inja.hpp"
23#include "option.h"
24#include "util/util.h"
25
26#include <miroir/miroir.hpp>
27#include <spdlog/spdlog.h>
28#include <yaml-cpp/yaml.h>
29
30#include <map>
31#include <memory>
32#include <optional>
33#include <regex>
34#include <string>
35#include <variant>
36#include <vector>
37
38namespace clanguml {
39
40namespace cli {
41struct runtime_config;
42} // namespace cli
43
44namespace error {
45class config_schema_error : public std::runtime_error {
46public:
47 config_schema_error(std::vector<miroir::Error<YAML::Node>> e)
48 : std::runtime_error("Invalid config schema")
49 , errors{std::move(e)}
50 {
51 }
52
53 std::vector<miroir::Error<YAML::Node>> errors;
54};
55
56void print(std::ostream &ostr, const config_schema_error &e,
57 logging::logger_type_t logger_type);
58} // namespace error
59
60/**
61 * @brief Configuration file related classes
62 *
63 * \image html config_class.svg
64 */
65namespace config {
66
68 enum class filtered_type {
69 any,
72 class_,
73 enum_,
74 method,
75 member,
77 package,
83 };
84
87};
88
89std::string to_string(element_filter_t::filtered_type ft);
90
91/*! Select how the method arguments should be rendered */
92enum class method_arguments {
93 full, /*! Full */
94 abbreviated, /*! Abrreviate, string between '(' and ')' is cropped */
95 none /*! Empty string between '(' and ')' */
96};
97
98std::string to_string(method_arguments ma);
99
100/*! Types of methods, which can be used in diagram filters */
101enum class method_type {
105 operator_,
106 defaulted,
107 deleted,
108 static_
109};
110
111std::string to_string(method_type mt);
112
113/*! Types of call expressions, which can be used in sequence diagram filters */
114enum class callee_type {
117 operator_,
118 defaulted,
119 static_,
120 method,
121 function,
123 lambda,
126};
127
128std::string to_string(callee_type mt);
129
130/*! How packages in diagrams should be generated */
131enum class package_type_t {
132 kNamespace, /*!< From namespaces */
133 kDirectory, /*!< From directories */
134 kModule /*!< From modules */
135};
136
137std::string to_string(package_type_t mt);
138
139/*! How class methods and members should be ordered in diagrams */
140enum class member_order_t {
141 lexical, /*! Lexical order based on entire method or member signature
142 without type */
143 as_is /*! As written in source code */
144};
145
146std::string to_string(member_order_t mt);
147
148/*! Which comment parser should be used */
150 plain, /*!< Basic string parser */
151 clang /*!< Clang's internal comment parser with structured output */
152};
153
154std::string to_string(comment_parser_t cp);
155
157 std::map<common::model::relationship_t, std::pair<std::string, std::string>>
159};
160
161enum class filter_mode_t {
162 basic, /*!< Default filter structure without logical operators */
163 advanced /*!< Advanced filter config with logical operators */
164};
165
166std::string to_string(filter_mode_t cp);
167
168/**
169 * @brief Glob config section for including and excluding TUs
170 */
171struct glob_t {
172 std::vector<common::string_or_regex> include;
173 std::vector<common::string_or_regex> exclude;
174};
175
176/**
177 * @brief PlantUML diagram config section
178 *
179 * This configuration option can be used to add any PlantUML directives
180 * before or after the generated diagram, such as diagram name, or custom
181 * notes.
182 */
183struct plantuml {
184 /*! List of directives to add before diagram */
185 std::vector<std::string> before;
186 /*! List of directives to add before diagram */
187 std::vector<std::string> after;
188 /*! Command template to render diagram using PlantUML */
189 std::string cmd;
190 /*! Provide customized styles for various elements */
191 std::map<std::string, std::string> style;
192
193 std::optional<std::string> get_style(
194 common::model::relationship_t relationship_type) const;
195
196 std::optional<std::string> get_style(const std::string &element_type) const;
197
198 void append(const plantuml &r);
199};
200
201/**
202 * @brief MermaidJS diagram config section
203 *
204 * This configuration option can be used to add any MermaidJS directives
205 * before or after the generated diagram, such as diagram name, or custom
206 * notes.
207 */
208struct mermaid {
209 /*! List of directives to add before diagram */
210 std::vector<std::string> before;
211 /*! List of directives to add before diagram */
212 std::vector<std::string> after;
213 /*! Command template to render diagram using MermaidJS */
214 std::string cmd;
215
216 void append(const mermaid &r);
217};
218
219/**
220 * @brief GraphML diagram config section
221 *
222 * This configuration option can be used to set any GraphML custom options
223 * or attach notes to diagram elements.
224 */
225struct graphml {
226 std::map<std::string, std::vector<std::string>> notes;
227
228 void append(const graphml &r);
229};
230
232
233std::string to_string(context_direction_t cd);
234
237 unsigned radius{0};
239 std::vector<common::model::relationship_t> relationships;
240};
241
242/**
243 * @brief Definition of diagram template
244 */
246 /*! Template description */
247 std::string description;
248
249 /*! Diagram type */
251
252 /*! Template which will be processed by Inja to generate the actual
253 * diagram
254 */
255 std::string jinja_template;
256};
257
258struct filter {
259 std::shared_ptr<filter> anyof;
260 std::shared_ptr<filter> allof;
261
262 /*! @brief Namespaces filter
263 *
264 * Example:
265 *
266 * ```yaml
267 * include
268 * namespaces:
269 * - ns1::ns2
270 * - r: ".*detail.*"
271 * ```
272 */
273 std::vector<common::namespace_or_regex> namespaces;
274
275 /*! @brief Modules filter
276 *
277 * Example:
278 *
279 * ```yaml
280 * include
281 * modules:
282 * - app.module1
283 * - r: ".*internal.*"
284 * ```
285 */
286 std::vector<common::string_or_regex> modules;
287
288 /*! @brief Access type filter
289 *
290 * This filter allows to filter class members methods based on their access:
291 * - public
292 * - private
293 *
294 * Example:
295 *
296 * ```yaml
297 * include:
298 * module_access:
299 * - public
300 * ```
301 */
302 std::vector<common::model::module_access_t> module_access;
303
304 /*! @brief Elements filter
305 *
306 * Example:
307 *
308 * ```yaml
309 * exclude:
310 * elements:
311 * - ns1::ns2::ClassA
312 * - r: ".*Enum.*"
313 * ```
314 */
315 std::vector<element_filter_t> elements;
316
317 /*! @brief Element types filter
318 *
319 * This filter allows to filter diagram elements based on their type.
320 *
321 * ```yaml
322 * include:
323 * elements:
324 * - class
325 * - concept
326 * ```
327 */
328 std::vector<std::string> element_types;
329
330 /*! @brief Relationships filter
331 *
332 * This filter allows to filter relationships included in the diagram.
333 *
334 * Example:
335 *
336 * ```yaml
337 * include:
338 * relationships:
339 * - inheritance
340 * ```
341 */
342 std::vector<common::model::relationship_t> relationships;
343
344 /*! @brief Access type filter
345 *
346 * This filter allows to filter class members methods based on their access:
347 * - public
348 * - protected
349 * - private
350 *
351 * Example:
352 *
353 * ```yaml
354 * include:
355 * access:
356 * - public
357 * ```
358 */
359 std::vector<common::model::access_t> access;
360
361 /*! @brief Subclasses filter
362 *
363 * This filter allows to filter classes based on having a specified parent.
364 * This filter also matches the specified parent itself.
365 *
366 * Example:
367 *
368 * ```yaml
369 * include:
370 * subclasses:
371 * - ns1::ns2::ClassA
372 * ```
373 */
374 std::vector<common::string_or_regex> subclasses;
375
376 /*! @brief Parents filter
377 *
378 * This filter allows to filter classes based on being a base class of a
379 * specified child.
380 * This filter also matches the specified child itself.
381 *
382 * Example:
383 *
384 * ```yaml
385 * include:
386 * parents:
387 * - ns1::ns2::ChildA
388 * ```
389 */
390 std::vector<common::string_or_regex> parents;
391
392 /*! @brief Specializations filter
393 *
394 * This filter allows to filter class template specializations of a specific
395 * template.
396 * This filter also matches the specified template itself.
397 *
398 * Example:
399 *
400 * ```yaml
401 * include:
402 * specializations:
403 * - ns1::ns2::ClassA<int,T>
404 * ```
405 */
406 std::vector<common::string_or_regex> specializations;
407
408 /*! @brief Dependants filter
409 *
410 * This filter allows to filter classes based on whether they are a
411 * dependant on a specified class.
412 *
413 * Example:
414 *
415 * ```yaml
416 * include:
417 * dependants:
418 * - ns1::ns2::ClassA
419 * ```
420 */
421 std::vector<common::string_or_regex> dependants;
422
423 /*! @brief Dependencies filter
424 *
425 * This filter allows to filter classes based on whether they are a
426 * dependency of a specified class.
427 *
428 * Example:
429 *
430 * ```yaml
431 * include:
432 * dependencies:
433 * - ns1::ns2::ClassA
434 * ```
435 */
436 std::vector<common::string_or_regex> dependencies;
437
438 /*! @brief Context filter
439 *
440 * This filter allows to filter classes based on whether they have at least
441 * one direct relation to the specified class.
442 *
443 * Example:
444 *
445 * ```yaml
446 * include:
447 * context:
448 * - ns1::ns2::ClassA
449 * - r: ns1::ns2::ClassB<.*>
450 * - match:
451 * pattern: ns1::ns2::ClassA
452 * radius: 3
453 * ```
454 */
455 std::vector<context_config> context;
456
457 /*! @brief Paths filter
458 *
459 * This filter allows to filter diagram elements based on the source
460 * location of their declaration.
461 *
462 * Example:
463 *
464 * ```yaml
465 * include:
466 * context:
467 * - ns1::ns2::ClassA
468 * ```
469 */
470 std::vector<std::string> paths;
471
472 /*! @brief Method types filter
473 *
474 * This filter allows to filter class methods based on their category.
475 *
476 * @see method_type
477 *
478 * Example:
479 *
480 * ```yaml
481 * exclude:
482 * method_types:
483 * - constructor
484 * - operator
485 * - assignment
486 * ```
487 */
488 std::vector<method_type> method_types;
489
490 /*! @brief Callee types filter
491 *
492 * This filter allows to filter sequence diagram calls by callee types.
493 *
494 * @see method_type
495 *
496 * Example:
497 *
498 * ```yaml
499 * exclude:
500 * callee_types:
501 * - constructor
502 * - operator
503 * ```
504 */
505 std::vector<callee_type> callee_types;
506};
507
508enum class hint_t { up, down, left, right, together, row, column };
509
510std::string to_string(hint_t t);
511
514 std::variant<std::string, std::vector<std::string>> entity;
515};
516
517using layout_hints = std::map<std::string, std::vector<layout_hint>>;
518
520 std::map</* path */ std::string, /* pattern */ std::string> link;
521 std::map</* path */ std::string, /* pattern */ std::string> tooltip;
522
523 std::optional<std::pair<std::string, std::string>> get_link_pattern(
524 const std::string &path) const
525 {
527 }
528
529 std::optional<std::pair<std::string, std::string>> get_tooltip_pattern(
530 const std::string &path) const
531 {
533 }
534};
535
537 std::string branch;
538 std::string revision;
539 std::string commit;
540 std::string toplevel;
541};
542
544 std::map<unsigned int, common::model::relationship_t> argument_hints;
546
549 : default_hint{def}
550 {
551 }
552
553 common::model::relationship_t get(unsigned int argument_index) const
554 {
555 if (argument_hints.count(argument_index) > 0)
556 return argument_hints.at(argument_index);
557
558 return default_hint;
559 }
560};
561
562using relationship_hints_t = std::map<std::string, relationship_hint_t>;
563
564using type_aliases_t = std::map<std::string, std::string>;
565
567 bool operator()(const std::string &a, const std::string &b) const
568 {
569 if (a.size() == b.size())
570 return a > b;
571
572 return a.size() > b.size();
573 }
574};
576 std::map<std::string, std::string, type_aliases_longer_first_comparator>;
577
579
580std::string to_string(location_t cp);
581
585};
586
587/**
588 * @brief Represents subset of inheritable configuration options
589 *
590 * This class contains a subset of configuration options, which are inherited
591 * from the top level of the configuration to the diagrams.
592 *
593 * @embed{inheritable_diagram_options_context_class.svg}
594 */
596 virtual ~inheritable_diagram_options() = default;
597
598 void inherit(const inheritable_diagram_options &parent);
599
600 std::string simplify_template_type(std::string full_name) const;
601
602 /**
603 * @brief Whether the diagram element should be fully qualified in diagram
604 *
605 * This method determines whether an elements' name should include
606 * fully qualified namespace name (however relative to using_namespace), or
607 * whether it should just contain it's name. This depends on whether the
608 * diagram has packages and if they are based on namespaces or sth else.
609 *
610 * @return True, if element should include it's namespace
611 */
613
614 /**
615 * @brief Returns absolute path of the `relative_to` option
616 *
617 * @return Absolute path of `relative_to`
618 */
619 std::filesystem::path root_directory() const;
620
621 /**
622 * @brief Get reference to `relative_to` diagram config option
623 *
624 * This method is only to allow access to `relative_to` for loading
625 * and adjusting configuration file and for making test cases work.
626 *
627 * Instead use @see config::diagram::root_directory() method.
628 *
629 * @return Reference to `relative_to` config option.
630 */
632
637 "include_relations_also_as_members", true};
639 option<bool> include_system_headers{"include_system_headers", false};
646 "generate_method_arguments", method_arguments::full};
648 "generate_concept_requirements", true};
649 option<bool> group_methods{"group_methods", true};
651 "member_order", member_order_t::lexical};
652 option<bool> generate_packages{"generate_packages", false};
654 "package_type", package_type_t::kNamespace};
656 "generate_template_argument_dependencies", true};
658 "skip_redundant_dependencies", true};
662 // This is the absolute filesystem path to the directory containing
663 // the current .clang-uml config file - it is set automatically
665 option<bool> generate_system_headers{"generate_system_headers", false};
668 "type_aliases", option_inherit_mode::kAppend};
670 "comment_parser", comment_parser_t::plain};
672 "combine_free_functions_into_file_participants", false};
673 option<bool> inline_lambda_messages{"inline_lambda_messages", false};
674 option<bool> generate_return_types{"generate_return_types", false};
676 "generate_condition_statements", false};
678 option<bool> generate_message_comments{"generate_message_comments", false};
679 option<bool> fold_repeated_activities{"fold_repeated_activities", false};
681 "message_comment_width", clanguml::util::kDefaultMessageCommentWidth};
682 option<bool> debug_mode{"debug_mode", false};
683 option<bool> generate_metadata{"generate_metadata", true};
684 option<bool> allow_empty_diagrams{"allow_empty_diagrams", false};
686
687protected:
688 // This is the relative path with respect to the `base_directory`,
689 // against which all matches are made, if not provided it defaults to
690 // the `base_directory`
691 //
692 // This option should not be accessed directly in code - instead use
693 // @see config::diagram::root_directory()
695
696 friend YAML::Emitter &operator<<(
697 YAML::Emitter &out, const inheritable_diagram_options &c);
698};
699
700/**
701 * @brief Common diagram configuration type
702 *
703 * This class provides common interface for diagram configuration sections
704 * of different diagram types.
705 *
706 * @embed{diagram_config_hierarchy_class.svg}
707 */
709 ~diagram() override = default;
710
711 virtual common::model::diagram_t type() const = 0;
712
713 /**
714 * @brief Filter translation units based on glob patterns
715 *
716 * @return List of translation unit paths
717 */
718 std::vector<std::string> glob_translation_units(
719 const std::vector<std::string> &compilation_database_files) const;
720
721 /**
722 * @brief Make path relative to the `relative_to` config option
723 *
724 * @param p Input path
725 * @return Relative path
726 */
727 std::filesystem::path make_path_relative(
728 const std::filesystem::path &p) const;
729
730 /**
731 * @brief Make module path relative to `using_module` configuration option
732 *
733 * @param p Input path
734 * @return Relative path
735 */
736 std::vector<std::string> make_module_relative(
737 const std::optional<std::string> &maybe_module) const;
738
739 std::optional<std::string> get_together_group(
740 const std::string &full_name) const;
741
742 /**
743 * @brief Initialize predefined set of C++ type aliases
744 *
745 * This method is responsible for setting up a predefined set of C++
746 * aliases to make the diagrams look nicer, for instance we want to have
747 * `std::string` instead of `std::basic_string<char>`.
748 */
750
751 std::string name;
752
754};
755
756/**
757 * @brief Class diagram configuration
758 */
759struct class_diagram : public diagram {
760 ~class_diagram() override = default;
761
762 common::model::diagram_t type() const override;
763
765};
766
767/**
768 * @brief Sequence diagram configuration
769 */
770struct sequence_diagram : public diagram {
771 ~sequence_diagram() override = default;
772
773 common::model::diagram_t type() const override;
774
776 option_with_alt_names_tag{}, "from", {"start_from"}};
779};
780
781/**
782 * @brief Package diagram configuration
783 */
784struct package_diagram : public diagram {
785 ~package_diagram() override = default;
786
787 common::model::diagram_t type() const override;
788};
789
790/**
791 * @brief Include diagram configuration
792 */
793struct include_diagram : public diagram {
794 ~include_diagram() override = default;
795
796 common::model::diagram_t type() const override;
797};
798
799/**
800 * @brief Represents entire configuration file
801 *
802 * This class contains all information loaded from the provided `clang-uml`
803 * configuration file, along with information inferred from the system
804 * such as current directory or Git context.
805 *
806 * The options in this class are not inheritable into specific diagram
807 * configurations.
808 *
809 * @embed{config_context_class.svg}
810 *
811 * The configuration file is loaded by invoking @see clanguml::config::load()
812 * function.
813 */
815 /*! Path to the directory containing compile_commands.json */
817 "compilation_database_dir", "."};
818
819 /*! List of compilation flags to be injected into the compilation
820 * commands from database
821 */
823
824 /*! List of compilation flags to be removed from the compilation
825 * commands from database
826 */
828 "remove_compile_flags"};
829
830 /*! Extract include paths by executing specified compiler driver.
831 * When the value is `'`, the command specified in compile_commands.json
832 * will be used. Otherwise the provided value will be treated as
833 * compiler executable (e.g. clang++-15) and executed.
834 */
836
837 /*! Diagrams output directory */
839
840 /*! Dictionary of user defined diagram templates, if any */
842 "diagram_templates"};
843
844 /*! Dictionary of diagrams, keys represent diagram names */
845 std::map<std::string, std::shared_ptr<diagram>> diagrams;
846
847 /**
848 * Initialize predefined diagram templates.
849 */
851
852 void inherit();
853};
854
855using config_ptr = std::unique_ptr<config>;
856
857/**
858 * @brief Load and parse `.clang-uml` configuration file
859 *
860 * This function takes the path to the configuration file and some options,
861 * parses the YAML file and creates a @see clanguml::config::config instance.
862 *
863 * @embed{load_config_sequence.svg}
864 *
865 * @param config_file Path to the configuration file
866 * @param inherit If true, common options will be propagated to diagram configs
867 * @param paths_relative_to_pwd Whether the paths in the configuration file
868 * should be relative to the parent directory of
869 * the configuration file or to the current
870 * directory (`$PWD`)
871 * @param no_metadata Whether the diagram should skip metadata at the end
872 * @param validate If true, perform schema validation
873 * @return Configuration instance
874 */
875config load(const std::string &config_file, bool inherit = true,
876 std::optional<bool> paths_relative_to_pwd = {},
877 std::optional<bool> no_metadata = {}, bool validate = true);
878} // namespace config
879
880namespace config {
881/** @defgroup yaml_emitters YAML serialization emitters
882 * Overloads for YAML serializations for various config types.
883 * @{
884 */
885YAML::Emitter &operator<<(YAML::Emitter &out, const config &c);
886
887YAML::Emitter &operator<<(
888 YAML::Emitter &out, const inheritable_diagram_options &c);
889
890YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f);
891
892YAML::Emitter &operator<<(YAML::Emitter &out, const plantuml &p);
893
894YAML::Emitter &operator<<(YAML::Emitter &out, const mermaid &p);
895
896YAML::Emitter &operator<<(YAML::Emitter &out, const graphml &p);
897
898YAML::Emitter &operator<<(YAML::Emitter &out, const method_arguments &ma);
899
900YAML::Emitter &operator<<(YAML::Emitter &out, const generate_links_config &glc);
901
902YAML::Emitter &operator<<(YAML::Emitter &out, const git_config &gc);
903
904YAML::Emitter &operator<<(YAML::Emitter &out, const relationship_hint_t &rh);
905
906YAML::Emitter &operator<<(YAML::Emitter &out, const comment_parser_t &cp);
907
908YAML::Emitter &operator<<(YAML::Emitter &out, const hint_t &h);
909
910YAML::Emitter &operator<<(YAML::Emitter &out, const class_diagram &c);
911
912YAML::Emitter &operator<<(YAML::Emitter &out, const sequence_diagram &c);
913
914YAML::Emitter &operator<<(YAML::Emitter &out, const include_diagram &c);
915
916YAML::Emitter &operator<<(YAML::Emitter &out, const package_diagram &c);
917
918YAML::Emitter &operator<<(YAML::Emitter &out, const layout_hint &c);
919
920YAML::Emitter &operator<<(YAML::Emitter &out, const element_filter_t &ef);
921
922YAML::Emitter &operator<<(YAML::Emitter &out, const diagram_template &dt);
923
924YAML::Emitter &operator<<(YAML::Emitter &out, const inja::json &ef);
925
926#ifdef _MSC_VER
927YAML::Emitter &operator<<(YAML::Emitter &out, const std::filesystem::path &p);
928
929YAML::Emitter &operator<<(
930 YAML::Emitter &out, const std::vector<std::filesystem::path> &p);
931#endif
932
933YAML::Emitter &operator<<(YAML::Emitter &out, const source_location &sc);
934
935template <typename T> bool is_null(const T & /*v*/) { return false; }
936
937template <> bool is_null(const std::string &v);
938
939template <> bool is_null(const glob_t &v);
940
941template <> bool is_null(const plantuml &v);
942
943template <> bool is_null(const mermaid &v);
944
945template <> bool is_null(const graphml &v);
946
947template <> bool is_null(const inja::json &v);
948
949template <typename T>
950YAML::Emitter &operator<<(YAML::Emitter &out, const option<T> &o)
951{
952 if (o.has_value && !is_null(o.value)) {
953 out << YAML::Key << o.name;
954 if constexpr (std::is_same_v<T, std::filesystem::path>)
955 out << YAML::Value << o.value.string();
956 else
957 out << YAML::Value << o.value;
958 }
959 return out;
960}
961/** @} */ // end of yaml_emitters
962
963} // namespace config
964
965namespace common::model {
966/** @addtogroup yaml_emitters YAML serialization emitters
967 * Overloads for YAML serializations for various model types.
968 * @{
969 */
970YAML::Emitter &operator<<(YAML::Emitter &out, const namespace_ &n);
971YAML::Emitter &operator<<(YAML::Emitter &out, const relationship_t &r);
972YAML::Emitter &operator<<(YAML::Emitter &out, const access_t &r);
973YAML::Emitter &operator<<(YAML::Emitter &out, const diagram_t &d);
974/** @} */ // end of yaml_emitters
975} // namespace common::model
976} // namespace clanguml
977
978namespace YAML {
979std::shared_ptr<clanguml::config::diagram> parse_diagram_config(
980 const YAML::Node &d);
981} // namespace YAML