97 deployment_(deployment),
98 resolution_(resolution),
100 filter_(params.filter),
101 view_(deployment, resolution, filter_, params.omit_disconnected),
102 indexer_(deployment.indexer)
120 std::vector<std::string> classes;
124 std::string id(
const std::string& parent,
const std::string& name)
126 auto key = parent +
"\n" + name;
127 auto it = ids_.find(key);
128 if (it != ids_.end())
131 static const std::set<std::string> keywords{
132 "label",
"style",
"class",
"classes",
"shape",
133 "icon",
"near",
"width",
"height",
"tooltip",
134 "link",
"direction",
"vars",
"constraint",
"grid-rows",
135 "grid-columns",
"top",
"left",
"source-arrowhead",
"target-arrowhead",
136 "layers",
"scenarios",
"steps",
"filled",
"_"};
138 auto shortened = name.substr(0, name.find(
'<'));
139 auto scope = shortened.rfind(
"::");
140 if (scope != std::string::npos)
141 shortened = shortened.substr(scope + 2);
144 for (
unsigned char c : shortened)
146 if (std::isalnum(c) || c ==
'_')
148 else if (base.empty() || base.back() !=
'_')
151 while (!base.empty() && base.back() ==
'_') base.pop_back();
152 while (!base.empty() && base.front() ==
'_') base.erase(0, 1);
153 if (base.empty() || keywords.count(base))
156 std::string unique = base;
157 for (
int n = 2; used_[parent].count(unique); ++n) unique = base +
"_" + std::to_string(n);
158 used_[parent].insert(unique);
159 return ids_[key] = unique;
163 std::string path(
const Endpoint& at,
const std::string& thread)
165 auto app = app_path(at);
168 threads_[app].insert(thread);
169 return app +
"." + id(app, thread);
172 std::string app_path(
const Endpoint& at)
174 std::string p = id(
"", at.platform->name);
175 if (at.platform->has_modules())
176 p +=
"." + id(p, at.module->name);
177 return p +
"." + id(p, at.application->name);
181 std::string placeholder_id(
const std::string& app,
bool no_publisher)
184 auto key = no_publisher ?
"\x01no publisher" :
"\x01no subscriber";
185 auto placeholder = id(app, key);
186 placeholders_[app][placeholder] = no_publisher ?
"no publisher" :
"no subscriber";
190 std::vector<std::string> edge_classes(
const PubSubEntry& sub,
bool dynamic)
192 std::vector<std::string> classes{
layer_name(sub.layer)};
194 classes.push_back(
"required");
196 classes.push_back(
"recommended");
198 classes.push_back(
"regex");
200 classes.push_back(
"dynamic");
206 for (
const auto& c : resolution_.connections)
210 edges_.push_back({path(c.pub_at, c.pub.thread), path(c.sub_at, c.sub.thread), c.pub,
211 edge_classes(c.sub, c.pub.dynamic || c.sub.dynamic || c.loose)});
214 for (
const auto& u : resolution_.unsubscribed)
218 auto app = app_path(u.at);
219 auto placeholder = placeholder_id(app,
false);
220 std::vector<std::string> classes{
layer_name(u.entry.layer)};
222 classes.push_back(
"dynamic");
224 {path(u.at, u.entry.thread), app +
"." + placeholder, u.entry, classes});
227 for (
const auto& u : resolution_.unpublished)
232 auto app = app_path(u.at);
233 auto placeholder = placeholder_id(app,
true);
234 PubSubEntry placeholder_pub = u.entry;
235 placeholder_pub.publish_index = indexer_.
next(u.entry.layer);
236 auto classes = edge_classes(u.entry, u.entry.dynamic);
238 classes.push_back(
"unmet");
240 {app +
"." + placeholder, path(u.at, u.entry.thread), placeholder_pub, classes});
243 for (
const auto& e : edges_)
246 pubs_by_thread_[
e.from].emplace(
e.pub.layer,
e.pub.publish_index,
e.pub);
253 os_ <<
"vars: { d2-config: { layout-engine: " << params_.
layout_engine <<
" } }\n";
254 os_ <<
"direction: right\n";
255 os_ <<
"classes: {\n";
256 auto container = [&](
const std::string& name,
const std::string& color)
258 os_ <<
" " << name <<
": { style.stroke: \"" << color <<
"\"; style.font-color: \""
259 << color <<
"\"; style.stroke-width: 2; style.fill: transparent }\n";
266 os_ <<
" placeholder: { shape: text; style.font-color: \"#888888\" }\n";
270 <<
"\"; style.font-size: 10 }\n";
271 os_ <<
" required: { style.stroke-width: 3 }\n";
272 os_ <<
" recommended: { style.stroke-dash: 6 }\n";
273 os_ <<
" regex: { style.stroke-dash: 2 }\n";
274 os_ <<
" dynamic: { style.stroke-dash: 4 }\n";
275 os_ <<
" unmet: { style.stroke: red; style.stroke-width: 3 }\n";
279 void label(
const std::string& indent,
const std::string& key,
const std::string& name)
282 os_ << indent <<
"label: " <<
d2_quoted(name) <<
"\n";
285 void write_containers()
287 for (
const auto& platform : deployment_.platforms)
291 auto platform_id = id(
"", platform.name);
292 os_ << platform_id <<
": {\n class: platform\n";
293 label(
" ", platform_id,
294 platform.has_modules()
296 : view_.container_label(platform.name, platform.modules.front()));
298 for (
const auto& module : platform.modules)
300 std::string indent =
" ";
301 std::string parent = platform_id;
302 if (platform.has_modules())
306 auto module_id = id(platform_id, module.name);
307 os_ << indent << module_id <<
": {\n" << indent <<
" class: module\n";
308 label(indent +
" ", module_id, view_.
container_label(module.name, module));
309 parent +=
"." + module_id;
313 for (
const auto& app_p : module.applications)
315 const auto& app = app_p.second;
318 auto app_id = id(parent, app.name);
319 auto app_path = parent +
"." + app_id;
320 os_ << indent << app_id <<
": {\n" << indent <<
" class: application\n";
321 label(indent +
" ", app_id, app.name);
324 auto tooltip = pubs_tooltip(app_path);
325 if (!tooltip.empty())
326 os_ << indent <<
" tooltip: " <<
d2_tooltip(tooltip) <<
"\n";
331 std::map<std::string, bool> threads;
332 for (
const auto& thread_p : app.threads)
334 const auto& thread = *thread_p.second;
336 threads[thread.most_derived_name()] = thread.known;
338 for (
const auto& thread : threads_[app_path]) threads.emplace(thread, true);
340 for (
const auto& thread_p : threads)
341 write_thread(indent +
" ", app_path, thread_p.first, thread_p.second);
342 for (
const auto& placeholder : placeholders_[app_path])
343 os_ << indent <<
" " << placeholder.first
344 <<
": { class: placeholder; label: " <<
d2_quoted(placeholder.second)
346 os_ << indent <<
"}\n";
349 if (platform.has_modules())
356 void write_thread(
const std::string& indent,
const std::string& app_path,
357 const std::string& thread,
bool known)
361 os_ << indent <<
thread_id <<
": { class: thread";
363 os_ <<
"; label: " <<
d2_quoted(display);
365 os_ <<
"; style.stroke-dash: 3";
367 auto tooltip = pubs_tooltip(app_path +
"." +
thread_id);
368 if (display != thread)
369 tooltip = thread + (tooltip.empty() ?
"" :
"\n") + tooltip;
370 if (!tooltip.empty())
376 std::string pubs_tooltip(
const std::string& path)
379 for (
const auto& p : pubs_by_thread_[path])
381 const auto& pub = std::get<2>(p);
382 tooltip += (tooltip.empty() ?
"" :
"\n") + pub.publish_index_str() +
": " +
383 pub.display_group() +
" | " + pub.scheme +
" | " + pub.type;
390 for (
const auto& e : edges_)
392 auto pub_label =
e.pub.publish_index_str();
393 os_ <<
e.from <<
" -> " <<
e.to <<
": "
396 if (
e.classes.size() == 1)
397 os_ <<
e.classes.front();
401 for (std::size_t i = 0; i <
e.classes.size(); ++i)
402 os_ << (i ?
"; " :
"") <<
e.classes[i];
406 <<
d2_tooltip(pub_label +
": " +
e.pub.display_group() +
" | " +
e.pub.scheme +
414 const Deployment& deployment_;
415 const Resolution& resolution_;
416 const D2Params& params_;
419 PublishIndexer indexer_;
421 std::map<std::string, std::string> ids_;
422 std::map<std::string, std::set<std::string>> used_;
423 std::vector<Edge> edges_;
425 std::map<std::string, std::set<std::string>> threads_;
427 std::map<std::string, std::map<std::string, std::string>> placeholders_;
429 std::map<std::string, std::set<std::tuple<Layer, int, PubSubEntry>>> pubs_by_thread_;