Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/build*
.vscode
.cache
CMakeUserPresets.json
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[submodule "external/fmt"]
path = external/fmt
url = https://github.com/fmtlib/fmt.git
[submodule "external/Catch2"]
path = external/Catch2
url = https://github.com/catchorg/Catch2.git
5 changes: 1 addition & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ include(cmake/libenvpp_mt_utils.cmake)
function(libenvpp_set_compiler_parameters TARGET)
set_target_properties(${TARGET} PROPERTIES
VERSION ${LIBENVPP_VERSION}
CXX_STANDARD 17
CXX_STANDARD 23
CXX_EXTENSIONS OFF
)
target_compile_options(${TARGET} PRIVATE
Expand Down Expand Up @@ -77,8 +77,6 @@ macro(fetch_content_from_submodule DEPNAME RELPATH)
endif()
endmacro()

fetch_content_from_submodule(fmt external/fmt)

if(LIBENVPP_TESTS)
fetch_content_from_submodule(Catch2 external/Catch2)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
Expand All @@ -101,7 +99,6 @@ add_library(libenvpp STATIC ${LIBENVPP_SOURCES} ${LIBENVPP_INCLUDES})
add_library(libenvpp::libenvpp ALIAS libenvpp)
libenvpp_set_compiler_parameters(libenvpp)
set_target_properties(libenvpp PROPERTIES PREFIX "")
target_link_libraries(libenvpp PUBLIC fmt::fmt)
target_include_directories(libenvpp PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
Expand Down
5 changes: 3 additions & 2 deletions examples/libenvpp_custom_environment_example.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <string>
#include <unordered_map>

#include <libenvpp/env.hpp>

Expand Down Expand Up @@ -29,5 +30,5 @@ int main()
std::cout << parsed_and_validated_pre.error_message();
}

return EXIT_SUCCESS;
return 0;
}
7 changes: 5 additions & 2 deletions examples/libenvpp_testing_example.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#include "libenvpp/detail/testing.hpp"
#include "libenvpp/env.hpp"
#include <cstdlib>
#include <filesystem>
#include <iostream>

#include <libenvpp/env.hpp>
#include <string>
#include <unordered_map>

int main()
{
const auto _ = env::scoped_test_environment({
const auto _ = env::scoped_test_environment(std::unordered_map<std::string, std::string>{
{"MYPROG_LOG_FILE_PATH", "/dev/null"},
{"MYPROG_NUM_THREADS", "8"},
});
Expand Down
1 change: 0 additions & 1 deletion external/fmt
Submodule fmt deleted from 0c9fce
8 changes: 4 additions & 4 deletions include/libenvpp/detail/check.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#include <stdexcept>
#include <string>
#include <string_view>

#include <fmt/core.h>
#include <format>
#include <print>

#ifndef LIBENVPP_STRINGIFY
#define LIBENVPP_STRINGIFY_HELPER(expression) #expression
Expand All @@ -22,7 +22,7 @@ class check_failed : public std::runtime_error {
#define LIBENVPP_CHECK(condition) \
do { \
if (!(condition)) { \
::fmt::print(stderr, "{}:{}: {}(): 'LIBENVPP_CHECK(" LIBENVPP_STRINGIFY(condition) ")' failed.", __FILE__, \
::std::print(stderr, "{}:{}: {}(): 'LIBENVPP_CHECK(" LIBENVPP_STRINGIFY(condition) ")' failed.", __FILE__, \
__LINE__, __func__); \
::std::abort(); \
} \
Expand All @@ -32,7 +32,7 @@ class check_failed : public std::runtime_error {
do { \
if (!(condition)) { \
throw ::env::detail::check_failed{ \
::fmt::format("{}:{}: {}(): 'LIBENVPP_CHECK(" LIBENVPP_STRINGIFY(condition) ")' failed.", __FILE__, \
::std::format("{}:{}: {}(): 'LIBENVPP_CHECK(" LIBENVPP_STRINGIFY(condition) ")' failed.", __FILE__, \
__LINE__, __func__)}; \
} \
} while (false)
Expand Down
33 changes: 16 additions & 17 deletions include/libenvpp/detail/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
#include <cstddef>
#include <cstdint>
#include <exception>
#include <format>
#include <sstream>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>

#include <fmt/core.h>

#include <libenvpp/detail/errors.hpp>
#include <libenvpp/detail/expected.hpp>
#include <libenvpp/detail/util.hpp>
Expand Down Expand Up @@ -79,7 +78,7 @@ inline constexpr auto is_stringstream_constructible_v = is_stringstream_construc
|| equal_case_insensitive(str, "no")) {
return false;
} else {
throw parser_error{fmt::format("Failed to parse '{}' as boolean", str)};
throw parser_error{std::format("Failed to parse '{}' as boolean", str)};
}
}

Expand All @@ -92,9 +91,9 @@ template <typename T>
} catch (const parser_error&) {
throw;
} catch (const std::exception& e) {
throw parser_error{fmt::format("String constructor failed for input '{}' with '{}'", str, e.what())};
throw parser_error{std::format("String constructor failed for input '{}' with '{}'", str, e.what())};
} catch (...) {
throw parser_error{fmt::format("String constructor failed for input '{}' with unknown error", str)};
throw parser_error{std::format("String constructor failed for input '{}' with unknown error", str)};
}
} else if constexpr (is_stringstream_constructible_v<T>) {
auto stream = std::istringstream(std::string(str));
Expand All @@ -117,15 +116,15 @@ template <typename T>
} catch (const parser_error&) {
throw;
} catch (const std::exception& e) {
throw parser_error{fmt::format("Stream operator>> failed for input '{}' with '{}'", str, e.what())};
throw parser_error{std::format("Stream operator>> failed for input '{}' with '{}'", str, e.what())};
} catch (...) {
throw parser_error{fmt::format("Stream operator>> failed for input '{}' with unknown error", str)};
throw parser_error{std::format("Stream operator>> failed for input '{}' with unknown error", str)};
}
if (stream.fail()) {
throw parser_error{fmt::format("Stream operator>> failed for input '{}'", str)};
throw parser_error{std::format("Stream operator>> failed for input '{}'", str)};
}
if (static_cast<std::size_t>(stream.tellg()) < str.size()) {
throw parser_error{fmt::format("Input '{}' was only parsed partially with remaining data '{}'", str,
throw parser_error{std::format("Input '{}' was only parsed partially with remaining data '{}'", str,
stream.str().substr(stream.tellg()))};
}
if constexpr (!std::is_same_v<T, bool> && std::is_unsigned_v<T>) {
Expand All @@ -137,10 +136,10 @@ template <typename T>
}
if (signed_stream.fail() || static_cast<std::size_t>(signed_stream.tellg()) < str.size()) {
throw parser_error{
fmt::format("Failed to validate whether '{}' was correctly parsed as '{}'", str, parsed)};
std::format("Failed to validate whether '{}' was correctly parsed as '{}'", str, parsed)};
}
if (signed_parsed < 0) {
throw parser_error{fmt::format("Cannot parse negative number '{}' as unsigned type", str)};
throw parser_error{std::format("Cannot parse negative number '{}' as unsigned type", str)};
}
}
return parsed;
Expand Down Expand Up @@ -187,19 +186,19 @@ template <typename T, typename ParserAndValidator>
try {
return expected_t{parser_and_validator(env_var_value)};
} catch (const parser_error& e) {
error_msg = fmt::format("Parser error for environment variable '{}': {}", env_var_name, e.what());
error_msg = std::format("Parser error for environment variable '{}': {}", env_var_name, e.what());
} catch (const validation_error& e) {
error_msg = fmt::format("Validation error for environment variable '{}': {}", env_var_name, e.what());
error_msg = std::format("Validation error for environment variable '{}': {}", env_var_name, e.what());
} catch (const range_error& e) {
error_msg = fmt::format("Range error for environment variable '{}': {}", env_var_name, e.what());
error_msg = std::format("Range error for environment variable '{}': {}", env_var_name, e.what());
} catch (const option_error& e) {
error_msg = fmt::format("Option error for environment variable '{}': {}", env_var_name, e.what());
error_msg = std::format("Option error for environment variable '{}': {}", env_var_name, e.what());
} catch (const std::exception& e) {
error_msg =
fmt::format("Failed to parse or validate environment variable '{}' with: {}", env_var_name, e.what());
std::format("Failed to parse or validate environment variable '{}' with: {}", env_var_name, e.what());
} catch (...) {
error_msg =
fmt::format("Failed to parse or validate environment variable '{}' with unknown error", env_var_name);
std::format("Failed to parse or validate environment variable '{}' with unknown error", env_var_name);
}
return expected_t{unexpected_t{error_msg}};
}
Expand Down
40 changes: 23 additions & 17 deletions include/libenvpp/env.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

#include <algorithm>
#include <any>
#include <cassert>
#include <cstddef>
#include <format>
#include <functional>
#include <initializer_list>
#include <iterator>
#include <numeric>
#include <optional>
#include <set>
#include <string>
Expand All @@ -13,9 +17,6 @@
#include <utility>
#include <vector>

#include <fmt/core.h>
#include <fmt/ranges.h>

#include <libenvpp/detail/edit_distance.hpp>
#include <libenvpp/detail/environment.hpp>
#include <libenvpp/detail/errors.hpp>
Expand Down Expand Up @@ -129,7 +130,7 @@ class parsed_and_validated_prefix {
const auto& value = m_prefix.m_registered_vars[var_id.m_idx].m_value;
if constexpr (IsRequired) {
if (!value.has_value()) {
throw value_error{fmt::format("Variable '{}' does not hold a value",
throw value_error{std::format("Variable '{}' does not hold a value",
m_prefix.m_registered_vars[var_id.m_idx].m_name)};
}
return std::any_cast<T>(value);
Expand Down Expand Up @@ -242,7 +243,7 @@ class parsed_and_validated_prefix {

for (auto&& unused_var : find_unused_env_vars(environment)) {
m_warnings.emplace_back(-1, unused_var,
fmt::format("Prefix environment variable '{}' specified but unused", unused_var));
std::format("Prefix environment variable '{}' specified but unused", unused_var));
}
}

Expand All @@ -254,7 +255,7 @@ class parsed_and_validated_prefix {
}
auto msg = std::string();
for (std::size_t i = 0; i < errors_or_warnings.size(); ++i) {
msg += fmt::format("{:<7}: {}\n", message_type, errors_or_warnings[i].what());
msg += std::format("{:<7}: {}\n", message_type, errors_or_warnings[i].what());
}
return msg;
}
Expand Down Expand Up @@ -389,14 +390,14 @@ class prefix {
throw_if_invalid();

if (m_registered_vars.empty()) {
return fmt::format("There are no supported environment variables for the prefix '{}'\n", m_prefix_name);
return std::format("There are no supported environment variables for the prefix '{}'\n", m_prefix_name);
}
auto msg = fmt::format("Prefix '{}' supports the following {} environment variable(s):\n", m_prefix_name,
auto msg = std::format("Prefix '{}' supports the following {} environment variable(s):\n", m_prefix_name,
m_registered_vars.size());
for (std::size_t i = 0; i < m_registered_vars.size(); ++i) {
const auto& var = m_registered_vars[i];
const auto var_name = get_full_env_var_name(i);
msg += fmt::format("\t'{}' {}\n", var_name, var.m_is_required ? "required" : "optional");
msg += std::format("\t'{}' {}\n", var_name, var.m_is_required ? "required" : "optional");
}
return msg;
}
Expand Down Expand Up @@ -442,15 +443,15 @@ class prefix {
[[nodiscard]] auto registration_range_helper(const std::string_view name, const T min, const T max)
{
if (min > max) {
throw invalid_range{fmt::format("Invalid range [{}, {}] for '{}', min must be less or equal to max", min,
throw invalid_range{std::format("Invalid range [{}, {}] for '{}', min must be less or equal to max", min,
max, get_full_env_var_name(name))};
}

const auto parser_and_validator = [min, max](const std::string_view str) {
const auto value = default_parser<T>{}(str);
default_validator<T>{}(value);
if (value < min || value > max) {
throw range_error{fmt::format("Value {} outside of range [{}, {}]", value, min, max)};
throw range_error{std::format("Value {} outside of range [{}, {}]", value, min, max)};
}
return value;
};
Expand All @@ -462,34 +463,39 @@ class prefix {
const std::vector<std::string> option_strings = {})
{
if (options.size() == 0) {
throw empty_option{fmt::format("No options provided for '{}'", get_full_env_var_name(name))};
throw empty_option{std::format("No options provided for '{}'", get_full_env_var_name(name))};
}

const auto options_set = std::set(options.begin(), options.end());
if (options_set.size() != options.size()) {
throw duplicate_option{fmt::format("Duplicate option specified for '{}'", get_full_env_var_name(name))};
throw duplicate_option{std::format("Duplicate option specified for '{}'", get_full_env_var_name(name))};
}
const auto parser_and_validator = [options = std::move(options),
strings = std::move(option_strings)](const std::string_view str) {
const auto value = [&]() {
if constexpr (SimpleParsing) {
if (strings.size() != options.size()) {
throw option_error{fmt::format("Option strings must be provided for simple option parsing")};
throw option_error{std::format("Option strings must be provided for simple option parsing")};
}
const auto it = std::find(strings.begin(), strings.end(), str);
if (it != strings.end()) {
return options.at(std::distance(strings.begin(), it));
} else {
throw option_error{fmt::format("Unrecognized option '{}', should be one of [{}]", str,
fmt::join(strings, ", "))};
assert(strings.size() != 0);
throw option_error{
std::format("Unrecognized option '{}', should be one of [{}]", str,
std::accumulate(std::next(strings.begin()), strings.end(), strings[0],
[](const std::string& buffer, const std::string& element) {
return buffer + ", " + element;
}))};
}
} else {
return default_parser<T>{}(str);
}
}();
default_validator<T>{}(value);
if (std::all_of(options.begin(), options.end(), [&value](const auto& option) { return option != value; })) {
throw option_error{fmt::format("Unrecognized option '{}'", str)};
throw option_error{std::format("Unrecognized option '{}'", str)};
}
return value;
};
Expand Down
12 changes: 8 additions & 4 deletions source/libenvpp_errors.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#include <cstddef>
#include <format>
#include <libenvpp/detail/errors.hpp>

#include <fmt/core.h>

#include <libenvpp/detail/environment.hpp>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>

namespace env::detail {

Expand All @@ -13,7 +17,7 @@ namespace env::detail {
const auto similar_var = find_similar_env_var(env_var_name, environment, edit_dist_cutoff);
if (similar_var.has_value()) {
const auto msg =
fmt::format("Unrecognized environment variable '{}' set, did you mean '{}'?", *similar_var, env_var_name);
std::format("Unrecognized environment variable '{}' set, did you mean '{}'?", *similar_var, env_var_name);
pop_from_environment(*similar_var, environment);
return {error(id, env_var_name, msg)};
}
Expand All @@ -22,7 +26,7 @@ namespace env::detail {

[[nodiscard]] error get_unset_env_var_error(const std::size_t id, const std::string_view env_var_name)
{
return error(id, env_var_name, fmt::format("Environment variable '{}' not set", env_var_name));
return error(id, env_var_name, std::format("Environment variable '{}' not set", env_var_name));
}

} // namespace env::detail
8 changes: 5 additions & 3 deletions source/libenvpp_testing.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#include <format>
#include <libenvpp/detail/testing.hpp>

#include <fmt/core.h>

#include <libenvpp/detail/errors.hpp>
#include <string>
#include <string_view>
#include <unordered_map>

namespace env {

Expand All @@ -28,7 +30,7 @@ scoped_test_environment::scoped_test_environment(const std::unordered_map<std::s
{
for (const auto& [name, value] : m_environment) {
if (const auto it = detail::g_testing_environment.find(name); it != detail::g_testing_environment.end()) {
throw test_environment_error{fmt::format("The global test environment already contains the value '{}' for "
throw test_environment_error{std::format("The global test environment already contains the value '{}' for "
"variable '{}', while trying to set it to '{}'",
it->second, name, value)};
}
Expand Down
Loading