summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/cxxformat/core.hpp10
-rw-r--r--include/cxxformat/runtime.hpp53
2 files changed, 39 insertions, 24 deletions
diff --git a/include/cxxformat/core.hpp b/include/cxxformat/core.hpp
index 4e9858b..6eec0ef 100644
--- a/include/cxxformat/core.hpp
+++ b/include/cxxformat/core.hpp
@@ -224,6 +224,14 @@ namespace format {
return {val, p};
}
+ namespace exceptions {
+ struct incomplete_specifier : std::invalid_argument {
+ using std::invalid_argument::invalid_argument;
+
+ incomplete_specifier() : std::invalid_argument{"Incomplete format specifier at end of format string"} {}
+ };
+ };
+
constexpr auto parseSpec(std::string_view fmt, std::size_t pos, ArgIndex nextArg)
{
struct Result {
@@ -236,7 +244,7 @@ namespace format {
const auto checkEos = [&pos, fmt](std::optional<std::size_t> p = std::nullopt) {
if (p.value_or(pos) >= fmt.size())
{
- throw std::invalid_argument{"Incomplete format specifier at end of format string"};
+ throw exceptions::incomplete_specifier{};
}
};
diff --git a/include/cxxformat/runtime.hpp b/include/cxxformat/runtime.hpp
index 1c881cc..7e25ee2 100644
--- a/include/cxxformat/runtime.hpp
+++ b/include/cxxformat/runtime.hpp
@@ -122,11 +122,11 @@ namespace format {
{
if (args > argCount)
{
- throw std::invalid_argument{"Too many arguments passed!"};
+ throw std::invalid_argument{"Too many arguments passed! Expected %u."_format(argCount)};
}
else if (args < argCount)
{
- throw std::invalid_argument{"Not enough arguments passed!"};
+ throw std::invalid_argument{"Not enough arguments passed! Expected %u."_format(argCount)};
}
}
};
@@ -159,33 +159,40 @@ namespace format {
break;
}
- if (nextSpec + 1 >= fmt.size())
- {
- throw std::invalid_argument{"Incomplete format specifier at end of format string"};
- }
+ try {
+ if (nextSpec + 1 >= fmt.size())
+ {
+ throw exceptions::incomplete_specifier{};
+ }
- const auto specStart = fmt[nextSpec + 1];
- if (specStart == '%')
- {
- // + 1 to include the first %
- addString(fmt.substr(pos, nextSpec - pos + 1));
- pos = nextSpec + 2;
- }
- else
- {
- const auto [newPos, spec, newNextArg, specMaxArg] = parseSpec(fmt, nextSpec + 1, nextArg);
- if (!checkValidConversion(spec.conversion))
+ const auto specStart = fmt[nextSpec + 1];
+ if (specStart == '%')
{
- throw std::invalid_argument{"Invalid conversion specifier ‘%c’ in format specifier “%s”"_format(spec.conversion, fmt.substr(nextSpec, newPos - nextSpec))};
+ // + 1 to include the first %
+ addString(fmt.substr(pos, nextSpec - pos + 1));
+ pos = nextSpec + 2;
}
+ else
+ {
+ const auto [newPos, spec, newNextArg, specMaxArg] = parseSpec(fmt, nextSpec + 1, nextArg);
+ if (!checkValidConversion(spec.conversion))
+ {
+ throw std::invalid_argument{"Invalid conversion specifier ‘%c’"_format(spec.conversion)};
+ }
- addString(fmt.substr(pos, nextSpec - pos));
+ addString(fmt.substr(pos, nextSpec - pos));
- nextArg = newNextArg;
- maxArg = std::max(maxArg, specMaxArg);
- pos = newPos;
+ nextArg = newNextArg;
+ maxArg = std::max(maxArg, specMaxArg);
+ pos = newPos;
- parts.emplace_back(spec);
+ parts.emplace_back(spec);
+ }
+ } catch(const exceptions::incomplete_specifier&) {
+ throw exceptions::incomplete_specifier{"Incomplete format specifier “%s” at end of format string"_format(fmt.substr(nextSpec))};
+ } catch(const std::exception& e) {
+ constexpr std::size_t showPartSize = 10;
+ throw std::invalid_argument{"%s when parsing format specifier “%s%s” at column %u"_format(e.what(), fmt.substr(nextSpec, showPartSize), nextSpec + showPartSize > fmt.size() ? "" : "…", nextSpec)};
}
}