Implement CLI options for adding diagrams to config from templates
This commit is contained in:
@@ -553,12 +553,20 @@ template <> struct convert<diagram_template> {
|
|||||||
if (node.Type() == NodeType::Scalar) {
|
if (node.Type() == NodeType::Scalar) {
|
||||||
// Check that the template provided as string is at least valid YAML
|
// Check that the template provided as string is at least valid YAML
|
||||||
const auto yaml_node = Load(node.as<std::string>());
|
const auto yaml_node = Load(node.as<std::string>());
|
||||||
const auto diagram_type = yaml_node["type"].as<std::string>();
|
const auto template_root_it = yaml_node.begin();
|
||||||
|
const auto diagram_name_template =
|
||||||
|
template_root_it->first.as<std::string>();
|
||||||
|
const auto diagram_type =
|
||||||
|
template_root_it->second["type"].as<std::string>();
|
||||||
rhs.type = clanguml::common::model::from_string(diagram_type);
|
rhs.type = clanguml::common::model::from_string(diagram_type);
|
||||||
rhs.jinja_template = Dump(yaml_node);
|
rhs.jinja_template = Dump(yaml_node);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const auto diagram_type = node["type"].as<std::string>();
|
const auto template_root_it = node.begin();
|
||||||
|
const auto diagram_name_template =
|
||||||
|
template_root_it->first.as<std::string>();
|
||||||
|
const auto diagram_type =
|
||||||
|
template_root_it->second["type"].as<std::string>();
|
||||||
rhs.type = clanguml::common::model::from_string(diagram_type);
|
rhs.type = clanguml::common::model::from_string(diagram_type);
|
||||||
rhs.jinja_template = Dump(node);
|
rhs.jinja_template = Dump(node);
|
||||||
}
|
}
|
||||||
|
|||||||
119
src/main.cc
119
src/main.cc
@@ -62,6 +62,14 @@ void print_version();
|
|||||||
*/
|
*/
|
||||||
void print_diagrams_list(const clanguml::config::config &cfg);
|
void print_diagrams_list(const clanguml::config::config &cfg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print list of available diagram templates, including their names
|
||||||
|
* and types.
|
||||||
|
*
|
||||||
|
* @param cfg
|
||||||
|
*/
|
||||||
|
void print_diagram_templates(const clanguml::config::config &cfg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print effective config after loading and setting default values.
|
* Print effective config after loading and setting default values.
|
||||||
*
|
*
|
||||||
@@ -87,6 +95,18 @@ int create_config_file();
|
|||||||
int add_config_diagram(clanguml::common::model::diagram_t type,
|
int add_config_diagram(clanguml::common::model::diagram_t type,
|
||||||
const std::string &config_file_path, const std::string &name);
|
const std::string &config_file_path, const std::string &name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add diagram based on template
|
||||||
|
* @param config_file_path
|
||||||
|
* @param cfg
|
||||||
|
* @param template_name
|
||||||
|
* @param template_variables
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int add_config_diagram_from_template(const std::string &config_file_path,
|
||||||
|
const config::config &cfg, const std::string &template_name,
|
||||||
|
const std::vector<std::string> &template_variables);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if diagram output directory exists, if not create it
|
* Check if diagram output directory exists, if not create it
|
||||||
*
|
*
|
||||||
@@ -172,8 +192,11 @@ int main(int argc, const char *argv[])
|
|||||||
std::optional<std::string> add_sequence_diagram;
|
std::optional<std::string> add_sequence_diagram;
|
||||||
std::optional<std::string> add_package_diagram;
|
std::optional<std::string> add_package_diagram;
|
||||||
std::optional<std::string> add_include_diagram;
|
std::optional<std::string> add_include_diagram;
|
||||||
|
std::optional<std::string> add_diagram_from_template;
|
||||||
bool dump_config{false};
|
bool dump_config{false};
|
||||||
std::optional<bool> paths_relative_to_pwd{};
|
std::optional<bool> paths_relative_to_pwd{};
|
||||||
|
std::vector<std::string> template_variables{};
|
||||||
|
bool list_templates{false};
|
||||||
|
|
||||||
app.add_option("-c,--config", config_path,
|
app.add_option("-c,--config", config_path,
|
||||||
"Location of configuration file, when '-' read from stdin");
|
"Location of configuration file, when '-' read from stdin");
|
||||||
@@ -200,6 +223,12 @@ int main(int argc, const char *argv[])
|
|||||||
"Add package diagram config");
|
"Add package diagram config");
|
||||||
app.add_option("--add-include-diagram", add_include_diagram,
|
app.add_option("--add-include-diagram", add_include_diagram,
|
||||||
"Add include diagram config");
|
"Add include diagram config");
|
||||||
|
app.add_option("--add-diagram-from-template", add_diagram_from_template,
|
||||||
|
"Add diagram config based on diagram template");
|
||||||
|
app.add_option("--template-variable", template_variables,
|
||||||
|
"Specify a value for a template variable");
|
||||||
|
app.add_flag("--list-templates", list_templates,
|
||||||
|
"List all available diagram templates");
|
||||||
app.add_flag(
|
app.add_flag(
|
||||||
"--dump-config", dump_config, "Print effective config to stdout");
|
"--dump-config", dump_config, "Print effective config to stdout");
|
||||||
app.add_flag("--paths-relative-to-pwd", paths_relative_to_pwd,
|
app.add_flag("--paths-relative-to-pwd", paths_relative_to_pwd,
|
||||||
@@ -221,8 +250,8 @@ int main(int argc, const char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((config_path == "-") &&
|
if ((config_path == "-") &&
|
||||||
(initialize || add_class_diagram.has_value() ||
|
(initialize || add_diagram_from_template ||
|
||||||
add_sequence_diagram.has_value() ||
|
add_class_diagram.has_value() || add_sequence_diagram.has_value() ||
|
||||||
add_package_diagram.has_value() ||
|
add_package_diagram.has_value() ||
|
||||||
add_include_diagram.has_value())) {
|
add_include_diagram.has_value())) {
|
||||||
|
|
||||||
@@ -276,6 +305,16 @@ int main(int argc, const char *argv[])
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (list_templates) {
|
||||||
|
print_diagram_templates(config);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_path != "-" && add_diagram_from_template) {
|
||||||
|
return add_config_diagram_from_template(config_path, config,
|
||||||
|
add_diagram_from_template.value(), template_variables);
|
||||||
|
}
|
||||||
|
|
||||||
LOG_INFO("Loaded clang-uml config from {}", config_path);
|
LOG_INFO("Loaded clang-uml config from {}", config_path);
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -539,6 +578,23 @@ void print_diagrams_list(const clanguml::config::config &cfg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void print_diagram_templates(const clanguml::config::config &cfg)
|
||||||
|
{
|
||||||
|
using std::cout;
|
||||||
|
|
||||||
|
if (!cfg.diagram_templates) {
|
||||||
|
cout << "No diagram templates are defined in the config file\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "The following diagram templates are available:\n";
|
||||||
|
for (const auto &[name, diagram_template] : cfg.diagram_templates()) {
|
||||||
|
cout << " - " << name << " [" << to_string(diagram_template.type)
|
||||||
|
<< "]";
|
||||||
|
cout << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int create_config_file()
|
int create_config_file()
|
||||||
{
|
{
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
@@ -669,6 +725,65 @@ int add_config_diagram(clanguml::common::model::diagram_t type,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int add_config_diagram_from_template(const std::string &config_file_path,
|
||||||
|
const config::config &cfg, const std::string &template_name,
|
||||||
|
const std::vector<std::string> &template_variables)
|
||||||
|
{
|
||||||
|
if (!cfg.diagram_templates ||
|
||||||
|
!(cfg.diagram_templates().find(template_name) !=
|
||||||
|
cfg.diagram_templates().end())) {
|
||||||
|
std::cerr << "ERROR: No such diagram template: " << template_name
|
||||||
|
<< "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, try to render the template using inja and create a YAML node from
|
||||||
|
// it
|
||||||
|
inja::json ctx;
|
||||||
|
for (const auto &tv : template_variables) {
|
||||||
|
const auto var = util::split(tv, "=");
|
||||||
|
if (var.size() != 2) {
|
||||||
|
std::cerr << "ERROR: Invalid template variable " << tv << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx[var.at(0)] = var.at(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto diagram_template_str =
|
||||||
|
cfg.diagram_templates().at(template_name).jinja_template;
|
||||||
|
|
||||||
|
auto diagram_str = inja::render(diagram_template_str, ctx);
|
||||||
|
|
||||||
|
auto diagram_node = YAML::Load(diagram_str);
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
fs::path config_file{config_file_path};
|
||||||
|
|
||||||
|
if (!fs::exists(config_file)) {
|
||||||
|
std::cerr << "ERROR: " << config_file_path << " file doesn't exists\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
YAML::Node doc = YAML::LoadFile(config_file.string());
|
||||||
|
|
||||||
|
const auto diagram_name = diagram_node.begin()->first.as<std::string>();
|
||||||
|
doc["diagrams"][diagram_name] = diagram_node.begin()->second;
|
||||||
|
|
||||||
|
YAML::Emitter out;
|
||||||
|
out.SetIndent(2);
|
||||||
|
|
||||||
|
out << doc;
|
||||||
|
out << YAML::Newline;
|
||||||
|
|
||||||
|
std::ofstream ofs(config_file);
|
||||||
|
ofs << out.c_str();
|
||||||
|
ofs.close();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void print_config(const clanguml::config::config &cfg)
|
void print_config(const clanguml::config::config &cfg)
|
||||||
{
|
{
|
||||||
YAML::Emitter out;
|
YAML::Emitter out;
|
||||||
|
|||||||
@@ -270,34 +270,34 @@ TEST_CASE("Test config diagram_templates", "[unit-test]")
|
|||||||
REQUIRE(cfg.diagram_templates()["bases_hierarchy_tmpl"].type ==
|
REQUIRE(cfg.diagram_templates()["bases_hierarchy_tmpl"].type ==
|
||||||
clanguml::common::model::diagram_t::kClass);
|
clanguml::common::model::diagram_t::kClass);
|
||||||
REQUIRE(cfg.diagram_templates()["bases_hierarchy_tmpl"].jinja_template ==
|
REQUIRE(cfg.diagram_templates()["bases_hierarchy_tmpl"].jinja_template ==
|
||||||
R"(name: "{{ class_name }}_parents_hierarchy"
|
R"("{{ class_name }}_parents_hierarchy":
|
||||||
type: class
|
type: class
|
||||||
include:
|
include:
|
||||||
parents: "{{ class_name }}"
|
parents: "{{ class_name }}"
|
||||||
namespaces: "{{ namespace_name }}"
|
namespaces: "{{ namespace_name }}"
|
||||||
relationships:
|
relationships:
|
||||||
- inheritance
|
- inheritance
|
||||||
exclude:
|
exclude:
|
||||||
access: [public, protected, private]
|
access: [public, protected, private]
|
||||||
plantuml:
|
plantuml:
|
||||||
before:
|
before:
|
||||||
- left to right direction)");
|
- left to right direction)");
|
||||||
|
|
||||||
REQUIRE(cfg.diagram_templates()["children_hierarchy_tmpl"].type ==
|
REQUIRE(cfg.diagram_templates()["children_hierarchy_tmpl"].type ==
|
||||||
clanguml::common::model::diagram_t::kClass);
|
clanguml::common::model::diagram_t::kClass);
|
||||||
REQUIRE(cfg.diagram_templates()["children_hierarchy_tmpl"].jinja_template ==
|
REQUIRE(cfg.diagram_templates()["children_hierarchy_tmpl"].jinja_template ==
|
||||||
R"(name: "{{ class_name }}_children_hierarchy"
|
R"("{{ class_name }}_children_hierarchy":
|
||||||
type: class
|
type: class
|
||||||
include:
|
include:
|
||||||
subclasses: "{{ class_name }}"
|
subclasses: "{{ class_name }}"
|
||||||
namespaces: "{{ namespace_name }}"
|
namespaces: "{{ namespace_name }}"
|
||||||
relationships:
|
relationships:
|
||||||
- inheritance
|
- inheritance
|
||||||
exclude:
|
exclude:
|
||||||
access: [public, protected, private]
|
access: [public, protected, private]
|
||||||
plantuml:
|
plantuml:
|
||||||
before:
|
before:
|
||||||
- left to right direction)");
|
- left to right direction)");
|
||||||
|
|
||||||
REQUIRE(cfg.diagram_templates()["main_sequence_tmpl"].type ==
|
REQUIRE(cfg.diagram_templates()["main_sequence_tmpl"].type ==
|
||||||
clanguml::common::model::diagram_t::kSequence);
|
clanguml::common::model::diagram_t::kSequence);
|
||||||
|
|||||||
@@ -3,37 +3,37 @@ output_directory: output
|
|||||||
|
|
||||||
diagram_templates:
|
diagram_templates:
|
||||||
bases_hierarchy_tmpl:
|
bases_hierarchy_tmpl:
|
||||||
name: '{{ class_name }}_parents_hierarchy'
|
'{{ class_name }}_parents_hierarchy':
|
||||||
type: class
|
type: class
|
||||||
include:
|
include:
|
||||||
parents: '{{ class_name }}'
|
parents: '{{ class_name }}'
|
||||||
namespaces: '{{ namespace_name }}'
|
namespaces: '{{ namespace_name }}'
|
||||||
relationships:
|
relationships:
|
||||||
- inheritance
|
- inheritance
|
||||||
exclude:
|
exclude:
|
||||||
access: [public, protected, private]
|
access: [public, protected, private]
|
||||||
plantuml:
|
plantuml:
|
||||||
before:
|
before:
|
||||||
- left to right direction
|
- left to right direction
|
||||||
children_hierarchy_tmpl: |
|
children_hierarchy_tmpl: |
|
||||||
name: '{{ class_name }}_children_hierarchy'
|
'{{ class_name }}_children_hierarchy':
|
||||||
type: class
|
type: class
|
||||||
include:
|
include:
|
||||||
subclasses: '{{ class_name }}'
|
subclasses: '{{ class_name }}'
|
||||||
namespaces: '{{ namespace_name }}'
|
namespaces: '{{ namespace_name }}'
|
||||||
relationships:
|
relationships:
|
||||||
- inheritance
|
- inheritance
|
||||||
exclude:
|
exclude:
|
||||||
access: [public, protected, private]
|
access: [public, protected, private]
|
||||||
plantuml:
|
plantuml:
|
||||||
before:
|
before:
|
||||||
- left to right direction
|
- left to right direction
|
||||||
main_sequence_tmpl: |
|
main_sequence_tmpl: |
|
||||||
name: main_sequence_diargam
|
main_sequence_diagram:
|
||||||
type: sequence
|
type: sequence
|
||||||
glob: [ {{ }} ]
|
glob: [ {{ glob }} ]
|
||||||
start_from:
|
start_from:
|
||||||
- function: 'main(int,const char**)'
|
- function: 'main(int,const char**)'
|
||||||
diagrams:
|
diagrams:
|
||||||
diagram1:
|
diagram1:
|
||||||
type: class
|
type: class
|
||||||
Reference in New Issue
Block a user