Skip to content

Commit 7f71f58

Browse files
committed
add issue_id class with generation algorithm
- the cass is needed to create issues - the algorithm belongs at least close to the issue_id class - the algorithm does _not_ belong to the application service
1 parent dd8cf68 commit 7f71f58

8 files changed

Lines changed: 103 additions & 18 deletions

File tree

src/domain/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ set(DOMAIN_SOURCES
22
application_service.cpp
33
description.cpp
44
domain_error.cpp
5+
issue_id.cpp
56
title.cpp
67
)
78

src/domain/application_service.cpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,22 @@
11
#include "application_service.hpp"
22

3-
#include <range/v3/view.hpp>
4-
#include <range/v3/range/conversion.hpp>
53
#include <string>
64

5+
#include "description.hpp"
6+
#include "issue_id.hpp"
7+
#include "title.hpp"
8+
79
using namespace fix::domain;
810
using namespace std::literals;
9-
namespace rv = ranges::views;
1011

1112
std::string application_service::create(std::string_view title, std::string_view description) { // NOLINT
12-
(void) description;
13-
14-
// clang-format off
15-
const auto id_prefix = title
16-
| rv::split(' ')
17-
| rv::take(4)
18-
| rv::transform([](auto&& word) {
19-
return word | rv::take(3) | rv::transform([](char c){ return std::tolower(c); });
20-
})
21-
| rv::join('-')
22-
| ranges::to<std::string>;
23-
// clang-format on
13+
auto const the_title = title::create(title);
14+
if (!the_title) {
15+
return ""; // TODO: proper error handling
16+
}
2417

25-
return id_prefix + "-0000000"s;
18+
const auto issue_id = issue_id::generate(*the_title, fix::domain::description(description));
19+
return issue_id.to_string();
2620
}
2721

2822
size_t application_service::list() const { // NOLINT

src/domain/issue_id.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include "issue_id.hpp"
2+
3+
#include <range/v3/range/conversion.hpp>
4+
#include <range/v3/view.hpp>
5+
6+
using namespace fix::domain;
7+
using namespace std::literals;
8+
namespace rv = ranges::views;
9+
10+
issue_id::issue_id(std::string text) : text{std::move(text)} {}
11+
12+
issue_id issue_id::generate(title const& title, description const& description) {
13+
(void)description;
14+
// clang-format off
15+
const auto id_prefix = title.to_string()
16+
| rv::split(' ')
17+
| rv::take(4)
18+
| rv::transform([](auto&& word) {
19+
return word | rv::take(3) | rv::transform([](char c){ return std::tolower(c); });
20+
})
21+
| rv::join('-')
22+
| ranges::to<std::string>;
23+
// clang-format on
24+
return issue_id{id_prefix + "-0000000"s};
25+
}
26+
27+
std::string const& issue_id::to_string() const {
28+
return text;
29+
}

src/domain/issue_id.hpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef FIX_SRC_DOMAIN_ISSUE_ID_HPP
2+
#define FIX_SRC_DOMAIN_ISSUE_ID_HPP
3+
4+
#include "description.hpp"
5+
#include "title.hpp"
6+
7+
namespace fix::domain {
8+
9+
class issue_id {
10+
std::string text;
11+
12+
explicit issue_id(std::string text);
13+
14+
public:
15+
// move-only; rule of 5
16+
issue_id(issue_id const&) = delete;
17+
issue_id(issue_id&&) = default;
18+
issue_id& operator=(issue_id const&) = delete;
19+
issue_id& operator=(issue_id&&) = default;
20+
~issue_id() = default;
21+
22+
static issue_id generate(title const& title, description const& description);
23+
24+
// cppcheck-suppress functionStatic
25+
std::string const& to_string() const;
26+
};
27+
28+
} // namespace fix::domain
29+
30+
#endif // FIX_SRC_DOMAIN_ISSUE_ID_HPP

src/domain/title.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ expected<title> title::create(std::string_view text) {
2828
// clang-format on
2929
}
3030

31-
std::string title::to_string() const {
31+
std::string const& title::to_string() const {
3232
return text;
3333
}
3434

src/domain/title.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class title {
1717
public:
1818
static expected<title> create(std::string_view text);
1919

20-
std::string to_string() const;
20+
std::string const& to_string() const;
2121
};
2222

2323
} // namespace fix::domain

test/domain/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ set(DOMAIN_TEST_SOURCES
22
application_service_test.cpp
33
description_test.cpp
44
domain_error_test.cpp
5+
issue_id_test.cpp
56
title_test.cpp
67
)
78

test/domain/issue_id_test.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <catch2/catch.hpp>
2+
3+
#include "issue_id.hpp"
4+
5+
#include <string>
6+
7+
#include "description.hpp"
8+
#include "title.hpp"
9+
10+
using namespace fix::domain;
11+
using namespace std::literals;
12+
13+
TEST_CASE("Issue IDs can be moved, but not default-constructed or copied") {
14+
STATIC_REQUIRE(std::is_move_constructible_v<issue_id>);
15+
16+
STATIC_REQUIRE_FALSE(std::is_copy_assignable_v<issue_id>);
17+
STATIC_REQUIRE_FALSE(std::is_copy_constructible_v<issue_id>);
18+
STATIC_REQUIRE_FALSE(std::is_default_constructible_v<issue_id>);
19+
}
20+
21+
TEST_CASE("Issue ID is contains abbreviated first words of the title") {
22+
auto const& [the_title, the_description, id_prefix] = GENERATE(
23+
std::tuple(title::create("this is a new issue"), description{"some text"}, "thi-is-a-new"),
24+
std::tuple(title::create("My first issue in Fix"), description{"Dorem Fixum dolor sit amet"}, "my-fir-iss-in"));
25+
26+
REQUIRE(the_title);
27+
28+
auto const id_pattern = id_prefix + "-[0-9a-f]{7}"s;
29+
CHECK_THAT(issue_id::generate(the_title.value(), the_description).to_string(), Catch::Matches(id_pattern));
30+
}

0 commit comments

Comments
 (0)