Skip to content

Commit 366a308

Browse files
authored
refactor: separate format component (#88)
* refactor: separate `errors::format` and other functions to `components/format` * style(format): wrap `formatter<errors::Error>` in `fmt` namespace * refactor(format): separate implementation of `format` function to `format.ipp` * build: add `NOT_SUBPROJECT` variable * docs: add support to build docs for `format` component
1 parent b57f647 commit 366a308

File tree

12 files changed

+129
-69
lines changed

12 files changed

+129
-69
lines changed

CMakeLists.txt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@ cmake_minimum_required(VERSION 3.12)
22

33
project(errors)
44

5-
# Import dependencies
5+
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
6+
set(NOT_SUBPROJECT TRUE)
7+
endif()
8+
9+
# Initialize CPM.cmake
610
include(cmake/CPM.cmake)
7-
cpmaddpackage("gh:fmtlib/fmt#10.0.0")
811

912
# Build the main library
1013
add_library(errors src/error.cpp)
1114
target_include_directories(errors PUBLIC include)
12-
target_link_libraries(errors PUBLIC fmt)
13-
target_compile_features(errors PRIVATE cxx_std_20)
1415

1516
# Check if this project is the main project
16-
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
17+
if(NOT_SUBPROJECT)
1718
option(BUILD_DOCS "Enable documentations build" OFF)
1819

1920
# Statically analyze code by checking for warnings
@@ -37,14 +38,11 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
3738
# Append the main library properties instead of linking the library.
3839
get_target_property(errors_SOURCES errors SOURCES)
3940
get_target_property(errors_INCLUDES errors INCLUDE_DIRECTORIES)
40-
get_target_property(errors_LIBRARIES errors LINK_LIBRARIES)
41-
get_target_property(errors_FEATURES errors COMPILE_FEATURES)
4241

4342
# Build tests for the main library
4443
add_executable(errors_test test/error_test.cpp ${errors_SOURCES})
4544
target_include_directories(errors_test PRIVATE ${errors_INCLUDES})
46-
target_link_libraries(errors_test PRIVATE Catch2::Catch2WithMain ${errors_LIBRARIES})
47-
target_compile_features(errors_test PRIVATE ${errors_FEATURES})
45+
target_link_libraries(errors_test PRIVATE Catch2::Catch2WithMain)
4846

4947
# Enable support to check for test coverage
5048
if(NOT MSVC)
@@ -58,6 +56,8 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
5856
# Build XML documentation
5957
if(BUILD_DOCS)
6058
include(cmake/add_xml_docs.cmake)
61-
add_xml_docs(docs include/errors/error.hpp)
59+
add_xml_docs(errors_docs include/errors/error.hpp)
6260
endif()
6361
endif()
62+
63+
add_subdirectory(components)

components/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_subdirectory(format)

components/format/CMakeLists.txt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
cpmaddpackage("gh:fmtlib/fmt#10.0.0")
2+
3+
add_library(errors_format src/format.cpp)
4+
target_include_directories(errors_format PUBLIC include)
5+
target_link_libraries(errors_format PUBLIC errors fmt)
6+
target_compile_features(errors_format PRIVATE cxx_std_20)
7+
8+
if(NOT_SUBPROJECT)
9+
if (BUILD_TESTING)
10+
# Append the main library properties instead of linking the library.
11+
get_target_property(errors_format_SOURCES errors_format SOURCES)
12+
get_target_property(errors_format_INCLUDES errors_format INCLUDE_DIRECTORIES)
13+
get_target_property(errors_format_LIBRARIES errors_format LINK_LIBRARIES)
14+
get_target_property(errors_format_FEATURES errors_format COMPILE_FEATURES)
15+
16+
# Build tests for the main library
17+
add_executable(errors_format_test test/format_test.cpp ${errors_format_SOURCES})
18+
target_include_directories(errors_format_test PRIVATE ${errors_format_INCLUDES})
19+
target_link_libraries(errors_format_test PRIVATE Catch2::Catch2WithMain ${errors_format_LIBRARIES})
20+
target_compile_features(errors_format_test PRIVATE ${errors_format_FEATURES})
21+
22+
# Enable support to check for test coverage
23+
if(NOT MSVC)
24+
target_compile_options(errors_format_test PRIVATE --coverage -O0 -fno-exceptions)
25+
target_link_options(errors_format_test PRIVATE --coverage)
26+
endif()
27+
28+
catch_discover_tests(errors_format_test)
29+
endif()
30+
31+
# Build XML documentation
32+
if(BUILD_DOCS)
33+
add_xml_docs(errors_format_docs include/errors/format.hpp)
34+
endif()
35+
endif()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
3+
#include <errors/error.hpp>
4+
#include <fmt/core.h>
5+
6+
namespace errors {
7+
8+
/**
9+
* @brief Creates a new error object with a formatted message.
10+
* @tparam T Variadic template parameter pack for format arguments.
11+
* @param fmt A format string for the message.
12+
* @param args Format arguments.
13+
* @return A new error object.
14+
*/
15+
template <typename... T>
16+
Error format(fmt::format_string<T...> fmt, T&&... args);
17+
18+
} // namespace errors
19+
20+
namespace fmt {
21+
22+
template <>
23+
struct formatter<errors::Error> {
24+
format_parse_context::iterator parse(format_parse_context& ctx) const;
25+
format_context::iterator format(const errors::Error& err,
26+
format_context& ctx) const;
27+
};
28+
29+
} // namespace fmt
30+
31+
#include "format.ipp"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace errors {
2+
3+
template <typename... T>
4+
Error format(fmt::format_string<T...> fmt, T&&... args) {
5+
return errors::make(fmt::format(fmt, std::forward<T>(args)...));
6+
}
7+
8+
} // namespace errors

components/format/src/format.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <errors/format.hpp>
2+
3+
namespace fmt {
4+
5+
format_parse_context::iterator formatter<errors::Error>::parse(
6+
format_parse_context& ctx) const {
7+
return ctx.begin();
8+
}
9+
10+
format_context::iterator formatter<errors::Error>::format(
11+
const errors::Error& err, format_context& ctx) const {
12+
return format_to(ctx.out(), "error: {}", err.message());
13+
}
14+
15+
} // namespace fmt
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <fmt/core.h>
2+
3+
#include <catch2/catch_test_macros.hpp>
4+
#include <errors/format.hpp>
5+
6+
TEST_CASE("Error Construction With Formatting") {
7+
const errors::Error err = errors::format("HTTP error {}", 404);
8+
REQUIRE(err.message() == "HTTP error 404");
9+
}
10+
11+
TEST_CASE("Error Printing Using fmtlib") {
12+
const auto err = errors::make("unknown error");
13+
REQUIRE(fmt::format("{}", err) == "error: unknown error");
14+
}

docs/conf.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
extensions = ['breathe']
88

99
subprocess.call('cmake .. -B ../build -D BUILD_DOCS=ON', shell=True)
10-
subprocess.call('cmake --build ../build --target docs', shell=True)
10+
subprocess.call('cmake --build ../build --target errors_docs --target errors_format_docs', shell=True)
1111

12-
breathe_projects = {"errors": "../build/docs"}
13-
breathe_default_project = "errors"
12+
breathe_projects = {
13+
"errors": "../build/errors_docs",
14+
"errors_format": "../build/components/format/errors_format_docs"
15+
}
1416

1517
html_theme = 'furo'
1618
html_static_path = ['_static']

docs/index.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,15 @@ API Docs
99
--------
1010

1111
.. doxygenclass:: errors::Error
12+
:project: errors
1213
:members:
1314

15+
Format Component
16+
^^^^^^^^^^^^^^^^
17+
18+
.. doxygenfunction:: errors::format
19+
:project: errors_format
20+
1421
License
1522
-------
1623

include/errors/error.hpp

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#pragma once
22

3-
#include <fmt/core.h>
4-
53
#include <memory>
64
#include <ostream>
75
#include <string>
@@ -33,9 +31,6 @@ class Error {
3331

3432
friend Error make(const std::string& msg);
3533

36-
template <typename... T>
37-
friend Error format(fmt::format_string<T...> fmt, T&&... args);
38-
3934
/**
4035
* @brief Writes the string representation of an error object to the given
4136
* output stream.
@@ -64,23 +59,4 @@ class Error {
6459
*/
6560
Error make(const std::string& msg);
6661

67-
/**
68-
* @brief Creates a new error object with a formatted message.
69-
* @tparam T Variadic template parameter pack for format arguments.
70-
* @param fmt A format string for the message.
71-
* @param args Format arguments.
72-
* @return A new error object.
73-
*/
74-
template <typename... T>
75-
Error format(fmt::format_string<T...> fmt, T&&... args) {
76-
return errors::make(fmt::format(fmt, std::forward<T>(args)...));
77-
}
78-
7962
} // namespace error
80-
81-
template <>
82-
struct fmt::formatter<errors::Error> {
83-
format_parse_context::iterator parse(format_parse_context& ctx) const;
84-
format_context::iterator format(const errors::Error& err,
85-
format_context& ctx) const;
86-
};

src/error.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,3 @@ Error make(const std::string& msg) {
1818
}
1919

2020
} // namespace error
21-
22-
namespace fmt {
23-
24-
format_parse_context::iterator formatter<errors::Error>::parse(
25-
format_parse_context& ctx) const {
26-
return ctx.begin();
27-
}
28-
29-
format_context::iterator formatter<errors::Error>::format(
30-
const errors::Error& err, format_context& ctx) const {
31-
return format_to(ctx.out(), "error: {}", err.message());
32-
}
33-
34-
} // namespace fmt

test/error_test.cpp

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,14 @@
1-
#include <fmt/core.h>
2-
31
#include <catch2/catch_test_macros.hpp>
42
#include <errors/error.hpp>
53
#include <sstream>
6-
#include <string>
74

85
TEST_CASE("Error Construction") {
96
const errors::Error err = errors::make("unknown error");
107
REQUIRE(err.message() == "unknown error");
118
}
129

13-
TEST_CASE("Error Construction With Formatting") {
14-
const errors::Error err = errors::format("HTTP error {}", 404);
15-
REQUIRE(err.message() == "HTTP error 404");
16-
}
17-
18-
TEST_CASE("Error Printing") {
10+
TEST_CASE("Error Printing Using OStream") {
1911
const auto err = errors::make("unknown error");
20-
21-
SECTION("Using ostream") {
22-
const auto ss = std::stringstream() << err;
23-
REQUIRE(ss.str() == "error: unknown error");
24-
}
25-
26-
SECTION("Using fmtlib") {
27-
REQUIRE(fmt::format("{}", err) == "error: unknown error");
28-
}
12+
const auto ss = std::stringstream() << err;
13+
REQUIRE(ss.str() == "error: unknown error");
2914
}

0 commit comments

Comments
 (0)