Skip to content

Commit bd4eb91

Browse files
authored
Allow querying the number of routes in URITemplateRouter (#2339)
Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent c91f056 commit bd4eb91

File tree

5 files changed

+153
-0
lines changed

5 files changed

+153
-0
lines changed

src/core/uritemplate/include/sourcemeta/core/uritemplate_router.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,14 @@ class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouter {
106106
/// Access the base path prefix
107107
[[nodiscard]] auto base_path() const noexcept -> std::string_view;
108108

109+
/// Get the number of registered routes
110+
[[nodiscard]] auto size() const noexcept -> std::size_t;
111+
109112
private:
110113
Node root_;
111114
std::string base_path_;
112115
std::vector<std::pair<Identifier, std::vector<Argument>>> arguments_;
116+
std::size_t size_{0};
113117
};
114118

115119
/// @ingroup uritemplate
@@ -145,6 +149,9 @@ class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouterView {
145149
/// Access the base path prefix
146150
[[nodiscard]] auto base_path() const noexcept -> std::string_view;
147151

152+
/// Get the number of registered routes
153+
[[nodiscard]] auto size() const noexcept -> std::size_t;
154+
148155
private:
149156
std::vector<std::uint8_t> data_;
150157
};

src/core/uritemplate/uritemplate_router.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ auto URITemplateRouter::base_path() const noexcept -> std::string_view {
109109
return this->base_path_;
110110
}
111111

112+
auto URITemplateRouter::size() const noexcept -> std::size_t {
113+
return this->size_;
114+
}
115+
112116
auto URITemplateRouter::add(const std::string_view uri_template,
113117
const Identifier identifier,
114118
const Identifier context,
@@ -141,6 +145,9 @@ auto URITemplateRouter::add(const std::string_view uri_template,
141145

142146
if (uri_template.empty()) {
143147
auto &target = current ? *current : this->root_;
148+
if (target.identifier == 0) {
149+
this->size_ += 1;
150+
}
144151
target.identifier = identifier;
145152
target.context = context;
146153
if (!arguments.empty()) {
@@ -310,6 +317,9 @@ auto URITemplateRouter::add(const std::string_view uri_template,
310317
}
311318

312319
if (!absorbed && current != nullptr) {
320+
if (current->identifier == 0) {
321+
this->size_ += 1;
322+
}
313323
current->identifier = identifier;
314324
current->context = context;
315325
if (!arguments.empty()) {

src/core/uritemplate/uritemplate_router_view.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,4 +663,34 @@ auto URITemplateRouterView::base_path() const noexcept -> std::string_view {
663663
return {string_table + header->base_path_offset, header->base_path_length};
664664
}
665665

666+
auto URITemplateRouterView::size() const noexcept -> std::size_t {
667+
if (this->data_.size() < sizeof(RouterHeader)) {
668+
return 0;
669+
}
670+
671+
const auto *header =
672+
reinterpret_cast<const RouterHeader *>(this->data_.data());
673+
if (header->magic != ROUTER_MAGIC || header->version != ROUTER_VERSION) {
674+
return 0;
675+
}
676+
677+
if (header->node_count == 0 ||
678+
header->node_count > (this->data_.size() - sizeof(RouterHeader)) /
679+
sizeof(SerializedNode)) {
680+
return 0;
681+
}
682+
683+
const auto *nodes = reinterpret_cast<const SerializedNode *>(
684+
this->data_.data() + sizeof(RouterHeader));
685+
686+
std::size_t count = 0;
687+
for (std::uint32_t index = 0; index < header->node_count; ++index) {
688+
if (nodes[index].identifier != 0) {
689+
count += 1;
690+
}
691+
}
692+
693+
return count;
694+
}
695+
666696
} // namespace sourcemeta::core

test/uritemplate/uritemplate_router_test.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,3 +1292,50 @@ TEST(URITemplateRouter, add_with_context_overwrites_previous_context) {
12921292
EXPECT_ROUTER_MATCH(router, "/users", 1, 20, captures);
12931293
EXPECT_EQ(captures.size(), 0);
12941294
}
1295+
1296+
TEST(URITemplateRouter, size_empty_router) {
1297+
const sourcemeta::core::URITemplateRouter router;
1298+
EXPECT_EQ(router.size(), 0);
1299+
}
1300+
1301+
TEST(URITemplateRouter, size_single_route) {
1302+
sourcemeta::core::URITemplateRouter router;
1303+
router.add("/users", 1);
1304+
EXPECT_EQ(router.size(), 1);
1305+
}
1306+
1307+
TEST(URITemplateRouter, size_multiple_routes) {
1308+
sourcemeta::core::URITemplateRouter router;
1309+
router.add("/users", 1);
1310+
router.add("/users/{id}", 2);
1311+
router.add("/posts", 3);
1312+
router.add("/posts/{id}", 4);
1313+
EXPECT_EQ(router.size(), 4);
1314+
}
1315+
1316+
TEST(URITemplateRouter, size_duplicate_route_does_not_increase) {
1317+
sourcemeta::core::URITemplateRouter router;
1318+
router.add("/users", 1);
1319+
router.add("/users", 2);
1320+
EXPECT_EQ(router.size(), 1);
1321+
}
1322+
1323+
TEST(URITemplateRouter, size_with_context_overwrite_does_not_increase) {
1324+
sourcemeta::core::URITemplateRouter router;
1325+
router.add("/users", 1, 10);
1326+
router.add("/users", 1, 20);
1327+
EXPECT_EQ(router.size(), 1);
1328+
}
1329+
1330+
TEST(URITemplateRouter, size_root_template) {
1331+
sourcemeta::core::URITemplateRouter router;
1332+
router.add("", 1);
1333+
EXPECT_EQ(router.size(), 1);
1334+
}
1335+
1336+
TEST(URITemplateRouter, size_with_base_path) {
1337+
sourcemeta::core::URITemplateRouter router{"/v1"};
1338+
router.add("/users", 1);
1339+
router.add("/posts", 2);
1340+
EXPECT_EQ(router.size(), 2);
1341+
}

test/uritemplate/uritemplate_router_view_test.cc

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2318,3 +2318,62 @@ TEST_F(URITemplateRouterViewTest,
23182318
EXPECT_ROUTER_MATCH(restored, "/users", 1, 20, captures);
23192319
EXPECT_EQ(captures.size(), 0);
23202320
}
2321+
2322+
TEST_F(URITemplateRouterViewTest, size_empty_router) {
2323+
{
2324+
const sourcemeta::core::URITemplateRouter router;
2325+
sourcemeta::core::URITemplateRouterView::save(router, this->path);
2326+
}
2327+
2328+
const sourcemeta::core::URITemplateRouterView restored{this->path};
2329+
EXPECT_EQ(restored.size(), 0);
2330+
}
2331+
2332+
TEST_F(URITemplateRouterViewTest, size_single_route) {
2333+
{
2334+
sourcemeta::core::URITemplateRouter router;
2335+
router.add("/users", 1);
2336+
sourcemeta::core::URITemplateRouterView::save(router, this->path);
2337+
}
2338+
2339+
const sourcemeta::core::URITemplateRouterView restored{this->path};
2340+
EXPECT_EQ(restored.size(), 1);
2341+
}
2342+
2343+
TEST_F(URITemplateRouterViewTest, size_multiple_routes) {
2344+
{
2345+
sourcemeta::core::URITemplateRouter router;
2346+
router.add("/users", 1);
2347+
router.add("/users/{id}", 2);
2348+
router.add("/posts", 3);
2349+
router.add("/posts/{id}", 4);
2350+
sourcemeta::core::URITemplateRouterView::save(router, this->path);
2351+
}
2352+
2353+
const sourcemeta::core::URITemplateRouterView restored{this->path};
2354+
EXPECT_EQ(restored.size(), 4);
2355+
}
2356+
2357+
TEST_F(URITemplateRouterViewTest, size_duplicate_route_does_not_increase) {
2358+
{
2359+
sourcemeta::core::URITemplateRouter router;
2360+
router.add("/users", 1);
2361+
router.add("/users", 2);
2362+
sourcemeta::core::URITemplateRouterView::save(router, this->path);
2363+
}
2364+
2365+
const sourcemeta::core::URITemplateRouterView restored{this->path};
2366+
EXPECT_EQ(restored.size(), 1);
2367+
}
2368+
2369+
TEST_F(URITemplateRouterViewTest, size_with_base_path) {
2370+
{
2371+
sourcemeta::core::URITemplateRouter router{"/v1"};
2372+
router.add("/users", 1);
2373+
router.add("/posts", 2);
2374+
sourcemeta::core::URITemplateRouterView::save(router, this->path);
2375+
}
2376+
2377+
const sourcemeta::core::URITemplateRouterView restored{this->path};
2378+
EXPECT_EQ(restored.size(), 2);
2379+
}

0 commit comments

Comments
 (0)