0.6.0
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 =
110 std::filesystem::relative(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 =
143 std::filesystem::relative(file, ctx["git"]["toplevel"])
144 .string();
145 ctx["element"]["source"]["path"] =
146 util::path_to_url(git_relative_path);
147 }
148 else {
149 ctx["element"]["source"]["path"] = e.file();
150 }
151 }
152 else {
153 git_relative_path = "";
154 ctx["element"]["source"]["path"] = e.file();
155 }
156
157 ctx["element"]["source"]["full_path"] = file.string();
158 ctx["element"]["source"]["name"] = file.filename().string();
159 ctx["element"]["source"]["line"] = e.line();
160 }
161}
162
163void to_json(inja::json &ctx,
165{
166 if (const auto maybe_comment = jc.get().comment();
167 maybe_comment.has_value()) {
168 ctx["comment"] = maybe_comment.value();
169 }
170}
171
173 inja::json &ctx, const diagram_context<common::model::diagram_element> &jc)
174{
175 to_json(ctx, jc.as<decorated_element>());
176
177 ctx["name"] = display_name_adapter(jc.get()).name();
178 ctx["type"] = jc.get().type_name();
179 ctx["alias"] = jc.get().alias();
180 ctx["full_name"] = display_name_adapter(jc.get()).full_name(false);
181 auto maybe_doxygen_link = jc.get().doxygen_link();
182 if (maybe_doxygen_link)
183 ctx["doxygen_link"] = maybe_doxygen_link.value();
184
185 to_json(ctx, jc.as<source_location>());
186}
187
188void to_json(inja::json &ctx, const diagram_context<common::model::element> &jc)
189{
190 to_json(ctx, jc.as<diagram_element>());
191
192 ctx["using_namespace"] = jc.get().using_namespace().to_string();
193 ctx["namespace"] = jc.get().get_namespace().to_string();
194 if (const auto maybe_comment = jc.get().comment();
195 maybe_comment.has_value()) {
196 ctx["comment"] = maybe_comment.value();
197 }
198}
199
200void to_json(inja::json &ctx, const diagram_context<source_file> &jc)
201{
202 to_json(ctx, jc.as<diagram_element>());
203
204 std::filesystem::path fullNamePath{ctx["full_name"].get<std::string>()};
205 fullNamePath.make_preferred();
206 ctx["full_name"] = fullNamePath.string();
207}
208
209std::optional<std::string> render_template(inja::Environment &env,
210 const inja::json &context, const std::string &jinja_template)
211{
212 std::optional<std::string> result;
213
214 if (jinja_template.empty())
215 return result;
216
217 try {
218 // Render the directive with template engine first
219 auto rendered_template =
220 env.render(std::string_view{jinja_template}, context);
221
222 result = std::move(rendered_template);
223 }
224 catch (const clanguml::error::uml_alias_missing &e) {
225 LOG_WARN("Failed to render Jinja template '{}' due to unresolvable "
226 "alias: {}",
227 jinja_template, e.what());
228 }
229 catch (const inja::json::parse_error &e) {
230 LOG_WARN("Failed to parse Jinja template: {}", jinja_template);
231 }
232 catch (const inja::json::exception &e) {
233 LOG_WARN("Failed to render Jinja template: \n{}\n due to: {}",
234 jinja_template, e.what());
235 }
236 catch (const std::regex_error &e) {
237 LOG_WARN("Failed to render Jinja template: \n{}\n due to "
238 "std::regex_error: {}",
239 jinja_template, e.what());
240 }
241 catch (const std::exception &e) {
242 LOG_WARN("Failed to render Jinja template: \n{}\n due to: {}",
243 jinja_template, e.what());
244 }
245
246 return result;
247}
248
249std::optional<std::string> render_template(
250 inja::Environment &env, const std::string &jinja_template)
251{
252 inja::json empty;
253 return render_template(env, empty, jinja_template);
254}
255
256} // namespace clanguml::common::jinja