Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate Format Components #88

Merged
merged 5 commits into from
Dec 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ cmake_minimum_required(VERSION 3.12)

project(errors)

# Import dependencies
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(NOT_SUBPROJECT TRUE)
endif()

# Initialize CPM.cmake
include(cmake/CPM.cmake)
cpmaddpackage("gh:fmtlib/fmt#10.0.0")

# Build the main library
add_library(errors src/error.cpp)
target_include_directories(errors PUBLIC include)
target_link_libraries(errors PUBLIC fmt)
target_compile_features(errors PRIVATE cxx_std_20)

# Check if this project is the main project
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
if(NOT_SUBPROJECT)
option(BUILD_DOCS "Enable documentations build" OFF)

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

# Build tests for the main library
add_executable(errors_test test/error_test.cpp ${errors_SOURCES})
target_include_directories(errors_test PRIVATE ${errors_INCLUDES})
target_link_libraries(errors_test PRIVATE Catch2::Catch2WithMain ${errors_LIBRARIES})
target_compile_features(errors_test PRIVATE ${errors_FEATURES})
target_link_libraries(errors_test PRIVATE Catch2::Catch2WithMain)

# Enable support to check for test coverage
if(NOT MSVC)
Expand All @@ -58,6 +56,8 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
# Build XML documentation
if(BUILD_DOCS)
include(cmake/add_xml_docs.cmake)
add_xml_docs(docs include/errors/error.hpp)
add_xml_docs(errors_docs include/errors/error.hpp)
endif()
endif()

add_subdirectory(components)
1 change: 1 addition & 0 deletions components/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(format)
35 changes: 35 additions & 0 deletions components/format/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
cpmaddpackage("gh:fmtlib/fmt#10.0.0")

add_library(errors_format src/format.cpp)
target_include_directories(errors_format PUBLIC include)
target_link_libraries(errors_format PUBLIC errors fmt)
target_compile_features(errors_format PRIVATE cxx_std_20)

if(NOT_SUBPROJECT)
if (BUILD_TESTING)
# Append the main library properties instead of linking the library.
get_target_property(errors_format_SOURCES errors_format SOURCES)
get_target_property(errors_format_INCLUDES errors_format INCLUDE_DIRECTORIES)
get_target_property(errors_format_LIBRARIES errors_format LINK_LIBRARIES)
get_target_property(errors_format_FEATURES errors_format COMPILE_FEATURES)

# Build tests for the main library
add_executable(errors_format_test test/format_test.cpp ${errors_format_SOURCES})
target_include_directories(errors_format_test PRIVATE ${errors_format_INCLUDES})
target_link_libraries(errors_format_test PRIVATE Catch2::Catch2WithMain ${errors_format_LIBRARIES})
target_compile_features(errors_format_test PRIVATE ${errors_format_FEATURES})

# Enable support to check for test coverage
if(NOT MSVC)
target_compile_options(errors_format_test PRIVATE --coverage -O0 -fno-exceptions)
target_link_options(errors_format_test PRIVATE --coverage)
endif()

catch_discover_tests(errors_format_test)
endif()

# Build XML documentation
if(BUILD_DOCS)
add_xml_docs(errors_format_docs include/errors/format.hpp)
endif()
endif()
31 changes: 31 additions & 0 deletions components/format/include/errors/format.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <errors/error.hpp>
#include <fmt/core.h>

namespace errors {

/**
* @brief Creates a new error object with a formatted message.
* @tparam T Variadic template parameter pack for format arguments.
* @param fmt A format string for the message.
* @param args Format arguments.
* @return A new error object.
*/
template <typename... T>
Error format(fmt::format_string<T...> fmt, T&&... args);

} // namespace errors

namespace fmt {

template <>
struct formatter<errors::Error> {
format_parse_context::iterator parse(format_parse_context& ctx) const;
format_context::iterator format(const errors::Error& err,
format_context& ctx) const;
};

} // namespace fmt

#include "format.ipp"
8 changes: 8 additions & 0 deletions components/format/include/errors/format.ipp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace errors {

template <typename... T>
Error format(fmt::format_string<T...> fmt, T&&... args) {
return errors::make(fmt::format(fmt, std::forward<T>(args)...));
}

} // namespace errors
15 changes: 15 additions & 0 deletions components/format/src/format.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <errors/format.hpp>

namespace fmt {

format_parse_context::iterator formatter<errors::Error>::parse(
format_parse_context& ctx) const {
return ctx.begin();
}

format_context::iterator formatter<errors::Error>::format(
const errors::Error& err, format_context& ctx) const {
return format_to(ctx.out(), "error: {}", err.message());
}

} // namespace fmt
14 changes: 14 additions & 0 deletions components/format/test/format_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include <fmt/core.h>

#include <catch2/catch_test_macros.hpp>
#include <errors/format.hpp>

TEST_CASE("Error Construction With Formatting") {
const errors::Error err = errors::format("HTTP error {}", 404);
REQUIRE(err.message() == "HTTP error 404");
}

TEST_CASE("Error Printing Using fmtlib") {
const auto err = errors::make("unknown error");
REQUIRE(fmt::format("{}", err) == "error: unknown error");
}
8 changes: 5 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
extensions = ['breathe']

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

breathe_projects = {"errors": "../build/docs"}
breathe_default_project = "errors"
breathe_projects = {
"errors": "../build/errors_docs",
"errors_format": "../build/components/format/errors_format_docs"
}

html_theme = 'furo'
html_static_path = ['_static']
7 changes: 7 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ API Docs
--------

.. doxygenclass:: errors::Error
:project: errors
:members:

Format Component
^^^^^^^^^^^^^^^^

.. doxygenfunction:: errors::format
:project: errors_format

License
-------

Expand Down
24 changes: 0 additions & 24 deletions include/errors/error.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#pragma once

#include <fmt/core.h>

#include <memory>
#include <ostream>
#include <string>
Expand Down Expand Up @@ -33,9 +31,6 @@ class Error {

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

template <typename... T>
friend Error format(fmt::format_string<T...> fmt, T&&... args);

/**
* @brief Writes the string representation of an error object to the given
* output stream.
Expand Down Expand Up @@ -64,23 +59,4 @@ class Error {
*/
Error make(const std::string& msg);

/**
* @brief Creates a new error object with a formatted message.
* @tparam T Variadic template parameter pack for format arguments.
* @param fmt A format string for the message.
* @param args Format arguments.
* @return A new error object.
*/
template <typename... T>
Error format(fmt::format_string<T...> fmt, T&&... args) {
return errors::make(fmt::format(fmt, std::forward<T>(args)...));
}

} // namespace error

template <>
struct fmt::formatter<errors::Error> {
format_parse_context::iterator parse(format_parse_context& ctx) const;
format_context::iterator format(const errors::Error& err,
format_context& ctx) const;
};
14 changes: 0 additions & 14 deletions src/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,3 @@ Error make(const std::string& msg) {
}

} // namespace error

namespace fmt {

format_parse_context::iterator formatter<errors::Error>::parse(
format_parse_context& ctx) const {
return ctx.begin();
}

format_context::iterator formatter<errors::Error>::format(
const errors::Error& err, format_context& ctx) const {
return format_to(ctx.out(), "error: {}", err.message());
}

} // namespace fmt
21 changes: 3 additions & 18 deletions test/error_test.cpp
Original file line number Diff line number Diff line change
@@ -1,29 +1,14 @@
#include <fmt/core.h>

#include <catch2/catch_test_macros.hpp>
#include <errors/error.hpp>
#include <sstream>
#include <string>

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

TEST_CASE("Error Construction With Formatting") {
const errors::Error err = errors::format("HTTP error {}", 404);
REQUIRE(err.message() == "HTTP error 404");
}

TEST_CASE("Error Printing") {
TEST_CASE("Error Printing Using OStream") {
const auto err = errors::make("unknown error");

SECTION("Using ostream") {
const auto ss = std::stringstream() << err;
REQUIRE(ss.str() == "error: unknown error");
}

SECTION("Using fmtlib") {
REQUIRE(fmt::format("{}", err) == "error: unknown error");
}
const auto ss = std::stringstream() << err;
REQUIRE(ss.str() == "error: unknown error");
}