@@ -220,6 +220,12 @@ typedef struct server_object_t {
220
220
lstr_t description;
221
221
} server_object_t;
222
222
223
+ typedef struct tag_object_t {
224
+ lstr_t name;
225
+ lstr_t description;
226
+ } tag_object_t;
227
+ qvector_t(tag, tag_object_t);
228
+
223
229
qm_kvec_t(whitelist, lstr_t, bool, qhash_lstr_hash, qhash_lstr_equal);
224
230
225
231
struct iop_openapi_t {
@@ -238,6 +244,8 @@ struct iop_openapi_t {
238
244
/* security */
239
245
lstr_t security;
240
246
247
+ qv_t(tag) tags;
248
+
241
249
/* whitelist of RPCs to expose, in the format "<iface_fullname>.<rpc>" */
242
250
qm_t(whitelist) rpcs_whitelist;
243
251
@@ -381,6 +389,61 @@ get_field_help(const iop_field_attrs_t * nullable attrs,
381
389
}
382
390
}
383
391
392
+ static void
393
+ get_mod_iface_help(const iop_mod_t * nonnull mod,
394
+ const iop_iface_alias_t * nonnull alias,
395
+ const iop_help_t * nullable * nonnull help)
396
+ {
397
+ const iop_mod_iface_attrs_t *attrs;
398
+
399
+ *help = NULL;
400
+ if (!TST_BIT(&mod->flags, IOP_MOD_EXTENDED) || !mod->ifaces_attrs) {
401
+ return;
402
+ }
403
+ attrs = &mod->ifaces_attrs[alias - mod->ifaces];
404
+
405
+ for (int k = 0; k < attrs->attrs_len; k++) {
406
+ const iop_mod_iface_attr_t *attr = &attrs->attrs[k];
407
+
408
+ switch (attr->type) {
409
+ case IOP_MOD_IFACE_ATTR_HELP:
410
+ *help = attr->args[0].v.p;
411
+ return;
412
+ case IOP_MOD_IFACE_ATTR_HELP_V2:
413
+ *help = attr->args[0].v.p;
414
+ return;
415
+ default:
416
+ break;
417
+ }
418
+ }
419
+ }
420
+
421
+ static void
422
+ get_iface_help(const iop_iface_t * nonnull iface,
423
+ const iop_help_t * nullable * nonnull help)
424
+ {
425
+ *help = NULL;
426
+
427
+ if (!TST_BIT(&iface->flags, IOP_IFACE_HAS_ATTRS) || !iface->iface_attrs) {
428
+ return;
429
+ }
430
+
431
+ for (int k = 0; k < iface->iface_attrs->attrs_len; k++) {
432
+ const iop_iface_attr_t *attr = &iface->iface_attrs->attrs[k];
433
+
434
+ switch (attr->type) {
435
+ case IOP_IFACE_ATTR_HELP:
436
+ *help = attr->args[0].v.p;
437
+ return;
438
+ case IOP_IFACE_ATTR_HELP_V2:
439
+ *help = attr->args[0].v.p;
440
+ return;
441
+ default:
442
+ break;
443
+ }
444
+ }
445
+ }
446
+
384
447
/* }}} */
385
448
/* {{{ Enums */
386
449
@@ -1465,8 +1528,13 @@ t_iop_rpc_add(iop_openapi_t * nonnull oa, const iop_rpc_t * nonnull rpc,
1465
1528
1466
1529
static void
1467
1530
t_iop_iface_alias_add(iop_openapi_t * nonnull oa,
1531
+ const iop_mod_t * nonnull mod,
1468
1532
const iop_iface_alias_t * nonnull alias)
1469
1533
{
1534
+ tag_object_t *tag;
1535
+ const iop_help_t *help;
1536
+ t_SB_1k(sb);
1537
+
1470
1538
for (int j = 0; j < alias->iface->funs_len; j++) {
1471
1539
const iop_rpc_t *rpc = &alias->iface->funs[j];
1472
1540
const unsigned rpc_flags = rpc->flags;
@@ -1496,13 +1564,34 @@ t_iop_iface_alias_add(iop_openapi_t * nonnull oa,
1496
1564
}
1497
1565
}
1498
1566
}
1567
+
1568
+ /* add a tag for the iface */
1569
+ tag = qv_growlen0(&oa->tags, 1);
1570
+ tag->name = alias->name;
1571
+
1572
+ /* Get description from both the iface alias in the module, and the iface
1573
+ * itself.
1574
+ */
1575
+ get_mod_iface_help(mod, alias, &help);
1576
+ if (help) {
1577
+ sb_add_lstr(&sb, t_iop_help_to_string(help, false));
1578
+ }
1579
+
1580
+ get_iface_help(alias->iface, &help);
1581
+ if (help) {
1582
+ if (sb.len > 0) {
1583
+ sb_adds(&sb, "\n\n");
1584
+ }
1585
+ sb_add_lstr(&sb, t_iop_help_to_string(help, false));
1586
+ }
1587
+ tag->description = sb.len > 0 ? LSTR_SB_V(&sb) : LSTR_NULL_V;
1499
1588
}
1500
1589
1501
1590
static void
1502
1591
t_iop_mod_add(iop_openapi_t * nonnull oa, const iop_mod_t * nonnull mod)
1503
1592
{
1504
1593
for (int i = 0; i < mod->ifaces_len; i++) {
1505
- t_iop_iface_alias_add(oa, &mod->ifaces[i]);
1594
+ t_iop_iface_alias_add(oa, mod, &mod->ifaces[i]);
1506
1595
}
1507
1596
}
1508
1597
@@ -1607,6 +1696,25 @@ static void t_server_object_to_yaml(const server_object_t * nonnull obj,
1607
1696
}
1608
1697
}
1609
1698
1699
+ /* }}} */
1700
+ /* {{{ Tag object */
1701
+
1702
+ static void t_tag_object_to_yaml(const tag_object_t * nonnull obj,
1703
+ yaml_data_t * nonnull out)
1704
+ {
1705
+ yaml_data_t data;
1706
+
1707
+ t_yaml_data_new_obj(out, 2);
1708
+
1709
+ yaml_data_set_string(&data, obj->name);
1710
+ yaml_obj_add_field(out, LSTR("name"), data);
1711
+
1712
+ if (obj->description.s) {
1713
+ yaml_data_set_string(&data, obj->description);
1714
+ yaml_obj_add_field(out, LSTR("description"), data);
1715
+ }
1716
+ }
1717
+
1610
1718
/* }}} */
1611
1719
/* {{{ Public API */
1612
1720
@@ -1619,11 +1727,13 @@ iop_openapi_t *t_new_iop_openapi(const lstr_t title, const lstr_t version,
1619
1727
oa->info.version = t_lstr_dup(version);
1620
1728
oa->module = module;
1621
1729
1622
- oa->server.route = t_lstr_dup(route );
1730
+ t_qv_init(& oa->paths, 0 );
1623
1731
1624
1732
t_components_object_init(&oa->components);
1625
1733
1626
- t_qv_init(&oa->paths, 0);
1734
+ oa->server.route = t_lstr_dup(route);
1735
+
1736
+ t_qv_init(&oa->tags, 0);
1627
1737
1628
1738
t_qm_init(whitelist, &oa->rpcs_whitelist, 0);
1629
1739
@@ -1683,6 +1793,7 @@ int t_iop_openapi_to_yaml(iop_openapi_t *openapi, yaml_data_t *out,
1683
1793
sb_t *err)
1684
1794
{
1685
1795
yaml_data_t data;
1796
+ yaml_data_t seq;
1686
1797
bool has_unused_whitelist = false;
1687
1798
1688
1799
if (openapi->module) {
@@ -1712,14 +1823,10 @@ int t_iop_openapi_to_yaml(iop_openapi_t *openapi, yaml_data_t *out,
1712
1823
t_info_object_to_yaml(&openapi->info, &data);
1713
1824
yaml_obj_add_field(out, LSTR("info"), data);
1714
1825
1715
- {
1716
- yaml_data_t seq;
1717
-
1718
- t_yaml_data_new_seq(&seq, 1);
1719
- t_server_object_to_yaml(&openapi->server, &data);
1720
- yaml_seq_add_data(&seq, data);
1721
- yaml_obj_add_field(out, LSTR("servers"), seq);
1722
- }
1826
+ t_yaml_data_new_seq(&seq, 1);
1827
+ t_server_object_to_yaml(&openapi->server, &data);
1828
+ yaml_seq_add_data(&seq, data);
1829
+ yaml_obj_add_field(out, LSTR("servers"), seq);
1723
1830
1724
1831
t_paths_to_yaml(&openapi->paths, &data);
1725
1832
yaml_obj_add_field(out, LSTR("paths"), data);
@@ -1742,6 +1849,15 @@ int t_iop_openapi_to_yaml(iop_openapi_t *openapi, yaml_data_t *out,
1742
1849
yaml_obj_add_field(out, LSTR("security"), array);
1743
1850
}
1744
1851
1852
+ if (openapi->tags.len > 0) {
1853
+ t_yaml_data_new_seq(&seq, openapi->tags.len);
1854
+ tab_for_each_ptr(tag, &openapi->tags) {
1855
+ t_tag_object_to_yaml(tag, &data);
1856
+ yaml_seq_add_data(&seq, data);
1857
+ }
1858
+ yaml_obj_add_field(out, LSTR("tags"), seq);
1859
+ }
1860
+
1745
1861
return 0;
1746
1862
}
1747
1863
0 commit comments