Skip to content

Commit

Permalink
add issue_id class with generation algorithm
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
arnemertz committed Sep 8, 2021
1 parent dd8cf68 commit 7f71f58
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/domain/CMakeLists.txt
Expand Up @@ -2,6 +2,7 @@ set(DOMAIN_SOURCES
application_service.cpp
description.cpp
domain_error.cpp
issue_id.cpp
title.cpp
)

Expand Down
26 changes: 10 additions & 16 deletions src/domain/application_service.cpp
@@ -1,28 +1,22 @@
#include "application_service.hpp"

#include <range/v3/view.hpp>
#include <range/v3/range/conversion.hpp>
#include <string>

#include "description.hpp"
#include "issue_id.hpp"
#include "title.hpp"

using namespace fix::domain;
using namespace std::literals;
namespace rv = ranges::views;

std::string application_service::create(std::string_view title, std::string_view description) { // NOLINT
(void) description;

// clang-format off
const auto id_prefix = title
| rv::split(' ')
| rv::take(4)
| rv::transform([](auto&& word) {
return word | rv::take(3) | rv::transform([](char c){ return std::tolower(c); });
})
| rv::join('-')
| ranges::to<std::string>;
// clang-format on
auto const the_title = title::create(title);
if (!the_title) {
return ""; // TODO: proper error handling
}

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

size_t application_service::list() const { // NOLINT
Expand Down
29 changes: 29 additions & 0 deletions src/domain/issue_id.cpp
@@ -0,0 +1,29 @@
#include "issue_id.hpp"

#include <range/v3/range/conversion.hpp>
#include <range/v3/view.hpp>

using namespace fix::domain;
using namespace std::literals;
namespace rv = ranges::views;

issue_id::issue_id(std::string text) : text{std::move(text)} {}

issue_id issue_id::generate(title const& title, description const& description) {
(void)description;
// clang-format off
const auto id_prefix = title.to_string()
| rv::split(' ')
| rv::take(4)
| rv::transform([](auto&& word) {
return word | rv::take(3) | rv::transform([](char c){ return std::tolower(c); });
})
| rv::join('-')
| ranges::to<std::string>;
// clang-format on
return issue_id{id_prefix + "-0000000"s};
}

std::string const& issue_id::to_string() const {
return text;
}
30 changes: 30 additions & 0 deletions src/domain/issue_id.hpp
@@ -0,0 +1,30 @@
#ifndef FIX_SRC_DOMAIN_ISSUE_ID_HPP
#define FIX_SRC_DOMAIN_ISSUE_ID_HPP

#include "description.hpp"
#include "title.hpp"

namespace fix::domain {

class issue_id {
std::string text;

explicit issue_id(std::string text);

public:
// move-only; rule of 5
issue_id(issue_id const&) = delete;
issue_id(issue_id&&) = default;
issue_id& operator=(issue_id const&) = delete;
issue_id& operator=(issue_id&&) = default;
~issue_id() = default;

static issue_id generate(title const& title, description const& description);

// cppcheck-suppress functionStatic
std::string const& to_string() const;
};

} // namespace fix::domain

#endif // FIX_SRC_DOMAIN_ISSUE_ID_HPP
2 changes: 1 addition & 1 deletion src/domain/title.cpp
Expand Up @@ -28,7 +28,7 @@ expected<title> title::create(std::string_view text) {
// clang-format on
}

std::string title::to_string() const {
std::string const& title::to_string() const {
return text;
}

Expand Down
2 changes: 1 addition & 1 deletion src/domain/title.hpp
Expand Up @@ -17,7 +17,7 @@ class title {
public:
static expected<title> create(std::string_view text);

std::string to_string() const;
std::string const& to_string() const;
};

} // namespace fix::domain
Expand Down
1 change: 1 addition & 0 deletions test/domain/CMakeLists.txt
Expand Up @@ -2,6 +2,7 @@ set(DOMAIN_TEST_SOURCES
application_service_test.cpp
description_test.cpp
domain_error_test.cpp
issue_id_test.cpp
title_test.cpp
)

Expand Down
30 changes: 30 additions & 0 deletions test/domain/issue_id_test.cpp
@@ -0,0 +1,30 @@
#include <catch2/catch.hpp>

#include "issue_id.hpp"

#include <string>

#include "description.hpp"
#include "title.hpp"

using namespace fix::domain;
using namespace std::literals;

TEST_CASE("Issue IDs can be moved, but not default-constructed or copied") {
STATIC_REQUIRE(std::is_move_constructible_v<issue_id>);

STATIC_REQUIRE_FALSE(std::is_copy_assignable_v<issue_id>);
STATIC_REQUIRE_FALSE(std::is_copy_constructible_v<issue_id>);
STATIC_REQUIRE_FALSE(std::is_default_constructible_v<issue_id>);
}

TEST_CASE("Issue ID is contains abbreviated first words of the title") {
auto const& [the_title, the_description, id_prefix] = GENERATE(
std::tuple(title::create("this is a new issue"), description{"some text"}, "thi-is-a-new"),
std::tuple(title::create("My first issue in Fix"), description{"Dorem Fixum dolor sit amet"}, "my-fir-iss-in"));

REQUIRE(the_title);

auto const id_pattern = id_prefix + "-[0-9a-f]{7}"s;
CHECK_THAT(issue_id::generate(the_title.value(), the_description).to_string(), Catch::Matches(id_pattern));
}

0 comments on commit 7f71f58

Please sign in to comment.