0.6.2
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
jinja_context.cc
Go to the documentation of this file.
1/**
2 * @file src/common/model/jinja_context.cc
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
19#include "jinja_context.h"
20
22
24
26
27using namespace clanguml::common::model;
28
29void to_json(inja::json &ctx,
31{
32 if (const auto maybe_comment = jc.get().comment();
33 maybe_comment.has_value()) {
34 ctx["element"]["comment"] = maybe_comment.value();
35 }
36
37 if (jc.diagram_context().contains("git")) {
38 ctx["git"] = jc.diagram_context()["git"];
39 }
40
41 if (jc.diagram_context().contains("user_data")) {
42 ctx["user_data"] = jc.diagram_context()["user_data"];
43 }
44}
45
46void to_json(inja::json &ctx, const element_context<diagram_element> &jc)
47{
48 to_json(ctx, jc.as<decorated_element>());
49
50 ctx["element"]["name"] = display_name_adapter(jc.get()).name();
51 ctx["element"]["type"] = jc.get().type_name();
52 ctx["element"]["alias"] = jc.get().alias();
53 ctx["element"]["full_name"] =
55 auto maybe_doxygen_link = jc.get().doxygen_link();
56 if (maybe_doxygen_link)
57 ctx["element"]["doxygen_link"] = maybe_doxygen_link.value();
58
59 to_json(ctx, jc.as<source_location>());
60}
61
62void to_json(inja::json &ctx, const element_context<element> &jc)
63{
64 to_json(ctx, jc.as<diagram_element>());
65
66 ctx["element"]["using_namespace"] = jc.get().using_namespace().to_string();
67 ctx["element"]["namespace"] = jc.get().get_namespace().to_string();
68}
69
71 inja::json &ctx, const element_context<common::model::relationship> &jc)
72{
73 to_json(ctx, jc.as<decorated_element>());
74
75 ctx["element"]["type"] = "relationship";
76 ctx["element"]["name"] = jc.get().label();
77 ctx["element"]["relationship_type"] = to_string(jc.get().type());
78 ctx["element"]["multiplicity"]["source"] = jc.get().multiplicity_source();
79 ctx["element"]["multiplicity"]["destination"] =
80 jc.get().multiplicity_destination();
81 ctx["element"]["access"] = to_string(jc.get().access());
82
83 to_json(ctx, jc.as<source_location>());
84}
85
86void to_json(inja::json &ctx, const element_context<source_file> &jc)
87{
88 to_json(ctx, jc.as<diagram_element>());
89
90 std::filesystem::path fullNamePath{
91 ctx["element"]["full_name"].get<std::string>()};
92 fullNamePath.make_preferred();
93 ctx["element"]["full_name"] = fullNamePath.string();
94}
95
96void to_json(inja::json &ctx, const diagram_context<source_location> &jc)
97{
98 const auto &e = jc.get();
99
100 if (!e.file().empty()) {
101 const std::filesystem::path file{e.file()};
102 std::string git_relative_path = file.string();
103 if (!e.file_relative().empty()) {
104#if _MSC_VER
105 if (file.is_absolute() && ctx.contains("git")) {
106#else
107 if (file.is_absolute() && ctx.contains("git")) {
108#endif
109 git_relative_path = std::filesystem::relative(
110 weakly_canonical(file), ctx["git"]["toplevel"])
111 .string();
112 ctx["source"]["path"] = util::path_to_url(git_relative_path);
113 }
114 else {
115 ctx["source"]["path"] = e.file();
116 }
117 }
118 else {
119 git_relative_path = "";
120 ctx["source"]["path"] = e.file();
121 }
122
123 ctx["source"]["full_path"] = file.string();
124 ctx["source"]["name"] = file.filename().string();
125 ctx["source"]["line"] = e.line();
126 }
127}
128
129void to_json(inja::json &ctx, const element_context<source_location> &jc)
130{
131 const auto &e = jc.get();
132
133 if (!e.file().empty()) {
134 const std::filesystem::path file{e.file()};
135 std::string git_relative_path = file.string();
136 if (!e.file_relative().empty()) {
137#if _MSC_VER
138 if (file.is_absolute() && ctx.contains("git")) {
139#else
140 if (file.is_absolute() && ctx.contains("git")) {
141#endif
142 git_relative_path = std::filesystem::relative(
143 weakly_canonical(file), ctx["git"]["toplevel"])
144 .string();
145
146 ctx["element"]["source"]["path"] =
147 util::path_to_url(git_relative_path);
148 }
149 else {
150 ctx["element"]["source"]["path"] = e.file();
151 }
152 }
153 else {
154 git_relative_path = "";
155 ctx["element"]["source"]["path"] = e.file();
156 }
157
158 ctx["element"]["source"]["full_path"] = file.string();
159 ctx["element"]["source"]["name"] = file.filename().string();
160 ctx["element"]["source"]["line"] = e.line();
161 }
162}
163
164void to_json(inja::json &ctx,
166{
167 if (const auto maybe_comment = jc.get().comment();
168 maybe_comment.has_value()) {
169 ctx["comment"] = maybe_comment.value();
170 }
171}
172
174 inja::json &ctx, const diagram_context<common::model::diagram_element> &jc)
175{
176 to_json(ctx, jc.as<decorated_element>());
177
178 ctx["name"] = display_name_adapter(jc.get()).name();
179 ctx["type"] = jc.get().type_name();
180 ctx["alias"] = jc.get().alias();
181 ctx["full_name"] = display_name_adapter(jc.get()).full_name(false);
182 auto maybe_doxygen_link = jc.get().doxygen_link();
183 if (maybe_doxygen_link)
184 ctx["doxygen_link"] = maybe_doxygen_link.value();
185
186 to_json(ctx, jc.as<source_location>());
187}
188
189void to_json(inja::json &ctx, const diagram_context<common::model::element> &jc)
190{
191 to_json(ctx, jc.as<diagram_element>());
192
193 ctx["using_namespace"] = jc.get().using_namespace().to_string();
194 ctx["namespace"] = jc.get().get_namespace().to_string();
195 if (const auto maybe_comment = jc.get().comment();
196 maybe_comment.has_value()) {
197 ctx["comment"] = maybe_comment.value();
198 }
199}
200
201void to_json(inja::json &ctx, const diagram_context<source_file> &jc)
202{
203 to_json(ctx, jc.as<diagram_element>());
204
205 std::filesystem::path fullNamePath{ctx["full_name"].get<std::string>()};
206 fullNamePath.make_preferred();
207 ctx["full_name"] = fullNamePath.string();
208}
209
210std::optional<std::string> render_template(inja::Environment &env,
211 const inja::json &context, const std::string &jinja_template)
212{
213 std::optional<std::string> result;
214
215 if (jinja_template.empty())
216 return result;
217
218 try {
219 // Render the directive with template engine first
220 auto rendered_template =
221 env.render(std::string_view{jinja_template}, context);
222
223 result = std::move(rendered_template);
224 }
225 catch (const clanguml::error::uml_alias_missing &e) {
226 LOG_WARN("Failed to render Jinja template '{}' due to unresolvable "
227 "alias: {}",
228 jinja_template, e.what());
229 }
230 catch (const inja::json::parse_error &e) {
231 LOG_WARN("Failed to parse Jinja template: {}", jinja_template);
232 }
233 catch (const inja::json::exception &e) {
234 LOG_WARN("Failed to render Jinja template: \n{}\n due to: {}",
235 jinja_template, e.what());
236 }
237 catch (const std::regex_error &e) {
238 LOG_WARN("Failed to render Jinja template: \n{}\n due to "
239 "std::regex_error: {}",
240 jinja_template, e.what());
241 }
242 catch (const std::exception &e) {
243 LOG_WARN("Failed to render Jinja template: \n{}\n due to: {}",
244 jinja_template, e.what());
245 }
246
247 return result;
248}
249
250std::optional<std::string> render_template(
251 inja::Environment &env, const std::string &jinja_template)
252{
253 inja::json empty;
254 return render_template(env, empty, jinja_template);
255}
256
257} // namespace clanguml::common::jinja