Rename hintfmt to HintFmt

This commit is contained in:
Rebecca Turner 2024-02-03 20:35:19 -08:00
parent 149bd63afb
commit c0e7f50c1a
Signed by: rbt
SSH key fingerprint: SHA256:SiNaEWabvotTldoNb5jIKqjJ3RnpS4aRXA4KLAdW5vs
29 changed files with 460 additions and 464 deletions

View file

@ -202,7 +202,7 @@ static int main_build_remote(int argc, char * * argv)
else else
drvstr = "<unknown>"; drvstr = "<unknown>";
auto error = hintfmt(errorText); auto error = HintFmt(errorText);
error error
% drvstr % drvstr
% neededSystem % neededSystem

View file

@ -28,7 +28,7 @@ template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withTrace(PosIdx pos, const std::string_view text) EvalErrorBuilder<T> & EvalErrorBuilder<T>::withTrace(PosIdx pos, const std::string_view text)
{ {
error.err.traces.push_front( error.err.traces.push_front(
Trace{.pos = error.state.positions[pos], .hint = hintfmt(std::string(text)), .frame = false}); Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text)), .frame = false});
return *this; return *this;
} }
@ -36,7 +36,7 @@ template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrameTrace(PosIdx pos, const std::string_view text) EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrameTrace(PosIdx pos, const std::string_view text)
{ {
error.err.traces.push_front( error.err.traces.push_front(
Trace{.pos = error.state.positions[pos], .hint = hintformat(std::string(text)), .frame = true}); Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text)), .frame = true});
return *this; return *this;
} }
@ -57,13 +57,13 @@ EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrame(const Env & env, const Expr
.pos = error.state.positions[expr.getPos()], .pos = error.state.positions[expr.getPos()],
.expr = expr, .expr = expr,
.env = env, .env = env,
.hint = hintformat("Fake frame for debugging purposes"), .hint = HintFmt("Fake frame for debugging purposes"),
.isError = true}); .isError = true});
return *this; return *this;
} }
template<class T> template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::addTrace(PosIdx pos, hintformat hint, bool frame) EvalErrorBuilder<T> & EvalErrorBuilder<T>::addTrace(PosIdx pos, HintFmt hint, bool frame)
{ {
error.addTrace(error.state.positions[pos], hint, frame); error.addTrace(error.state.positions[pos], hint, frame);
return *this; return *this;
@ -75,7 +75,7 @@ EvalErrorBuilder<T> &
EvalErrorBuilder<T>::addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs) EvalErrorBuilder<T>::addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs)
{ {
addTrace(error.state.positions[pos], hintfmt(std::string(formatString), formatArgs...)); addTrace(error.state.positions[pos], HintFmt(std::string(formatString), formatArgs...));
return *this; return *this;
} }

View file

@ -89,7 +89,7 @@ public:
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrame(const Env & e, const Expr & ex); [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrame(const Env & e, const Expr & ex);
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & addTrace(PosIdx pos, hintformat hint, bool frame = false); [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & addTrace(PosIdx pos, HintFmt hint, bool frame = false);
template<typename... Args> template<typename... Args>
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & [[nodiscard, gnu::noinline]] EvalErrorBuilder<T> &

View file

@ -803,7 +803,7 @@ void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2)
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame) const void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame) const
{ {
e.addTrace(positions[pos], hintfmt(s, s2), frame); e.addTrace(positions[pos], HintFmt(s, s2), frame);
} }
template<typename... Args> template<typename... Args>
@ -819,7 +819,7 @@ static std::unique_ptr<DebugTraceStacker> makeDebugTraceStacker(
.pos = std::move(pos), .pos = std::move(pos),
.expr = expr, .expr = expr,
.env = env, .env = env,
.hint = hintfmt(formatArgs...), .hint = HintFmt(formatArgs...),
.isError = false .isError = false
}); });
} }
@ -2792,7 +2792,7 @@ std::optional<std::string> EvalState::resolveSearchPathPath(const SearchPath::Pa
res = { store->toRealPath(storePath) }; res = { store->toRealPath(storePath) };
} catch (FileTransferError & e) { } catch (FileTransferError & e) {
logWarning({ logWarning({
.msg = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value) .msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value)
}); });
} }
} }
@ -2825,7 +2825,7 @@ std::optional<std::string> EvalState::resolveSearchPathPath(const SearchPath::Pa
res = { path }; res = { path };
else { else {
logWarning({ logWarning({
.msg = hintfmt("Nix search path entry '%1%' does not exist, ignoring", value) .msg = HintFmt("Nix search path entry '%1%' does not exist, ignoring", value)
}); });
res = std::nullopt; res = std::nullopt;
} }

View file

@ -148,7 +148,7 @@ struct DebugTrace {
std::shared_ptr<Pos> pos; std::shared_ptr<Pos> pos;
const Expr & expr; const Expr & expr;
const Env & env; const Env & env;
hintfmt hint; HintFmt hint;
bool isError; bool isError;
}; };

View file

@ -155,7 +155,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
} catch (Error & e) { } catch (Error & e) {
e.addTrace( e.addTrace(
state.positions[attr.pos], state.positions[attr.pos],
hintfmt("while evaluating flake attribute '%s'", state.symbols[attr.name])); HintFmt("while evaluating flake attribute '%s'", state.symbols[attr.name]));
throw; throw;
} }
} }
@ -164,7 +164,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
try { try {
input.ref = FlakeRef::fromAttrs(attrs); input.ref = FlakeRef::fromAttrs(attrs);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(state.positions[pos], hintfmt("while evaluating flake input")); e.addTrace(state.positions[pos], HintFmt("while evaluating flake input"));
throw; throw;
} }
else { else {

View file

@ -147,7 +147,7 @@ or { return OR_KW; }
yylval->n = boost::lexical_cast<int64_t>(yytext); yylval->n = boost::lexical_cast<int64_t>(yytext);
} catch (const boost::bad_lexical_cast &) { } catch (const boost::bad_lexical_cast &) {
throw ParseError(ErrorInfo{ throw ParseError(ErrorInfo{
.msg = hintfmt("invalid integer '%1%'", yytext), .msg = HintFmt("invalid integer '%1%'", yytext),
.pos = state->positions[CUR_POS], .pos = state->positions[CUR_POS],
}); });
} }
@ -157,7 +157,7 @@ or { return OR_KW; }
yylval->nf = strtod(yytext, 0); yylval->nf = strtod(yytext, 0);
if (errno != 0) if (errno != 0)
throw ParseError(ErrorInfo{ throw ParseError(ErrorInfo{
.msg = hintfmt("invalid float '%1%'", yytext), .msg = HintFmt("invalid float '%1%'", yytext),
.pos = state->positions[CUR_POS], .pos = state->positions[CUR_POS],
}); });
return FLOAT_LIT; return FLOAT_LIT;
@ -286,7 +286,7 @@ or { return OR_KW; }
<INPATH_SLASH>{ANY} | <INPATH_SLASH>{ANY} |
<INPATH_SLASH><<EOF>> { <INPATH_SLASH><<EOF>> {
throw ParseError(ErrorInfo{ throw ParseError(ErrorInfo{
.msg = hintfmt("path has a trailing slash"), .msg = HintFmt("path has a trailing slash"),
.pos = state->positions[CUR_POS], .pos = state->positions[CUR_POS],
}); });
} }

View file

@ -64,7 +64,7 @@ struct ParserState
inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos) inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
{ {
throw ParseError({ throw ParseError({
.msg = hintfmt("attribute '%1%' already defined at %2%", .msg = HintFmt("attribute '%1%' already defined at %2%",
showAttrPath(symbols, attrPath), positions[prevPos]), showAttrPath(symbols, attrPath), positions[prevPos]),
.pos = positions[pos] .pos = positions[pos]
}); });
@ -73,7 +73,7 @@ inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, co
inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos) inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
{ {
throw ParseError({ throw ParseError({
.msg = hintfmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]), .msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
.pos = positions[pos] .pos = positions[pos]
}); });
} }
@ -154,13 +154,13 @@ inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Sym
} }
if (duplicate) if (duplicate)
throw ParseError({ throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", symbols[duplicate->first]), .msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
.pos = positions[duplicate->second] .pos = positions[duplicate->second]
}); });
if (arg && formals->has(arg)) if (arg && formals->has(arg))
throw ParseError({ throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", symbols[arg]), .msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]),
.pos = positions[pos] .pos = positions[pos]
}); });

View file

@ -65,7 +65,7 @@ using namespace nix;
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error) void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error)
{ {
throw ParseError({ throw ParseError({
.msg = hintfmt(error), .msg = HintFmt(error),
.pos = state->positions[state->at(*loc)] .pos = state->positions[state->at(*loc)]
}); });
} }
@ -154,7 +154,7 @@ expr_function
| LET binds IN_KW expr_function | LET binds IN_KW expr_function
{ if (!$2->dynamicAttrs.empty()) { if (!$2->dynamicAttrs.empty())
throw ParseError({ throw ParseError({
.msg = hintfmt("dynamic attributes not allowed in let"), .msg = HintFmt("dynamic attributes not allowed in let"),
.pos = state->positions[CUR_POS] .pos = state->positions[CUR_POS]
}); });
$$ = new ExprLet($2, $4); $$ = new ExprLet($2, $4);
@ -244,7 +244,7 @@ expr_simple
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals); static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
if (noURLLiterals) if (noURLLiterals)
throw ParseError({ throw ParseError({
.msg = hintfmt("URL literals are disabled"), .msg = HintFmt("URL literals are disabled"),
.pos = state->positions[CUR_POS] .pos = state->positions[CUR_POS]
}); });
$$ = new ExprString(std::string($1)); $$ = new ExprString(std::string($1));
@ -340,7 +340,7 @@ attrs
delete str; delete str;
} else } else
throw ParseError({ throw ParseError({
.msg = hintfmt("dynamic attributes not allowed in inherit"), .msg = HintFmt("dynamic attributes not allowed in inherit"),
.pos = state->positions[state->at(@2)] .pos = state->positions[state->at(@2)]
}); });
} }

View file

@ -754,7 +754,7 @@ static RegisterPrimOp primop_break({
if (state.debugRepl && !state.debugTraces.empty()) { if (state.debugRepl && !state.debugTraces.empty()) {
auto error = Error(ErrorInfo { auto error = Error(ErrorInfo {
.level = lvlInfo, .level = lvlInfo,
.msg = hintfmt("breakpoint reached"), .msg = HintFmt("breakpoint reached"),
.pos = state.positions[pos], .pos = state.positions[pos],
}); });
@ -765,7 +765,7 @@ static RegisterPrimOp primop_break({
// If the user elects to quit the repl, throw an exception. // If the user elects to quit the repl, throw an exception.
throw Error(ErrorInfo{ throw Error(ErrorInfo{
.level = lvlInfo, .level = lvlInfo,
.msg = hintfmt("quit the debugger"), .msg = HintFmt("quit the debugger"),
.pos = nullptr, .pos = nullptr,
}); });
} }
@ -820,7 +820,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
auto message = state.coerceToString(pos, *args[0], context, auto message = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtins.addErrorContext", "while evaluating the error message passed to builtins.addErrorContext",
false, false).toOwned(); false, false).toOwned();
e.addTrace(nullptr, hintfmt(message), true); e.addTrace(nullptr, HintFmt(message), true);
throw; throw;
} }
} }
@ -1071,7 +1071,7 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
* often results from the composition of several functions * often results from the composition of several functions
* (derivationStrict, derivation, mkDerivation, mkPythonModule, etc.) * (derivationStrict, derivation, mkDerivation, mkPythonModule, etc.)
*/ */
e.addTrace(nullptr, hintfmt( e.addTrace(nullptr, HintFmt(
"while evaluating derivation '%s'\n" "while evaluating derivation '%s'\n"
" whose name attribute is located at %s", " whose name attribute is located at %s",
drvName, pos), true); drvName, pos), true);
@ -1232,7 +1232,7 @@ drvName, Bindings * attrs, Value & v)
} catch (Error & e) { } catch (Error & e) {
e.addTrace(state.positions[i->pos], e.addTrace(state.positions[i->pos],
hintfmt("while evaluating attribute '%1%' of derivation '%2%'", key, drvName), HintFmt("while evaluating attribute '%1%' of derivation '%2%'", key, drvName),
true); true);
throw; throw;
} }

View file

@ -23,7 +23,7 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
auto rewrittenPath = makeContentAddressed(fromStore, *state.store, fromPath); auto rewrittenPath = makeContentAddressed(fromStore, *state.store, fromPath);
if (toPathMaybe && *toPathMaybe != rewrittenPath) if (toPathMaybe && *toPathMaybe != rewrittenPath)
throw Error({ throw Error({
.msg = hintfmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected", .msg = HintFmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
state.store->printStorePath(fromPath), state.store->printStorePath(fromPath),
state.store->printStorePath(rewrittenPath), state.store->printStorePath(rewrittenPath),
state.store->printStorePath(*toPathMaybe)), state.store->printStorePath(*toPathMaybe)),
@ -31,7 +31,7 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
}); });
if (!toPathMaybe) if (!toPathMaybe)
throw Error({ throw Error({
.msg = hintfmt( .msg = HintFmt(
"rewriting '%s' to content-addressed form yielded '%s'\n" "rewriting '%s' to content-addressed form yielded '%s'\n"
"Use this value for the 'toPath' attribute passed to 'fetchClosure'", "Use this value for the 'toPath' attribute passed to 'fetchClosure'",
state.store->printStorePath(fromPath), state.store->printStorePath(fromPath),
@ -50,7 +50,7 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
// We don't perform the rewriting when outPath already exists, as an optimisation. // We don't perform the rewriting when outPath already exists, as an optimisation.
// However, we can quickly detect a mistake if the toPath is input addressed. // However, we can quickly detect a mistake if the toPath is input addressed.
throw Error({ throw Error({
.msg = hintfmt( .msg = HintFmt(
"The 'toPath' value '%s' is input-addressed, so it can't possibly be the result of rewriting to a content-addressed path.\n\n" "The 'toPath' value '%s' is input-addressed, so it can't possibly be the result of rewriting to a content-addressed path.\n\n"
"Set 'toPath' to an empty string to make Nix report the correct content-addressed path.", "Set 'toPath' to an empty string to make Nix report the correct content-addressed path.",
state.store->printStorePath(toPath)), state.store->printStorePath(toPath)),
@ -73,7 +73,7 @@ static void runFetchClosureWithContentAddressedPath(EvalState & state, const Pos
if (!info->isContentAddressed(*state.store)) { if (!info->isContentAddressed(*state.store)) {
throw Error({ throw Error({
.msg = hintfmt( .msg = HintFmt(
"The 'fromPath' value '%s' is input-addressed, but 'inputAddressed' is set to 'false' (default).\n\n" "The 'fromPath' value '%s' is input-addressed, but 'inputAddressed' is set to 'false' (default).\n\n"
"If you do intend to fetch an input-addressed store path, add\n\n" "If you do intend to fetch an input-addressed store path, add\n\n"
" inputAddressed = true;\n\n" " inputAddressed = true;\n\n"
@ -99,7 +99,7 @@ static void runFetchClosureWithInputAddressedPath(EvalState & state, const PosId
if (info->isContentAddressed(*state.store)) { if (info->isContentAddressed(*state.store)) {
throw Error({ throw Error({
.msg = hintfmt( .msg = HintFmt(
"The store object referred to by 'fromPath' at '%s' is not input-addressed, but 'inputAddressed' is set to 'true'.\n\n" "The store object referred to by 'fromPath' at '%s' is not input-addressed, but 'inputAddressed' is set to 'true'.\n\n"
"Remove the 'inputAddressed' attribute (it defaults to 'false') to expect 'fromPath' to be content-addressed", "Remove the 'inputAddressed' attribute (it defaults to 'false') to expect 'fromPath' to be content-addressed",
state.store->printStorePath(fromPath)), state.store->printStorePath(fromPath)),
@ -153,14 +153,14 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
else else
throw Error({ throw Error({
.msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName), .msg = HintFmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
.pos = state.positions[pos] .pos = state.positions[pos]
}); });
} }
if (!fromPath) if (!fromPath)
throw Error({ throw Error({
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"), .msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
.pos = state.positions[pos] .pos = state.positions[pos]
}); });
@ -169,7 +169,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
if (inputAddressed) { if (inputAddressed) {
if (toPath) if (toPath)
throw Error({ throw Error({
.msg = hintfmt("attribute '%s' is set to true, but '%s' is also set. Please remove one of them", .msg = HintFmt("attribute '%s' is set to true, but '%s' is also set. Please remove one of them",
"inputAddressed", "inputAddressed",
"toPath"), "toPath"),
.pos = state.positions[pos] .pos = state.positions[pos]
@ -178,7 +178,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
if (!fromStoreUrl) if (!fromStoreUrl)
throw Error({ throw Error({
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"), .msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
.pos = state.positions[pos] .pos = state.positions[pos]
}); });
@ -188,13 +188,13 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
parsedURL.scheme != "https" && parsedURL.scheme != "https" &&
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file")) !(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
throw Error({ throw Error({
.msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"), .msg = HintFmt("'fetchClosure' only supports http:// and https:// stores"),
.pos = state.positions[pos] .pos = state.positions[pos]
}); });
if (!parsedURL.query.empty()) if (!parsedURL.query.empty())
throw Error({ throw Error({
.msg = hintfmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl), .msg = HintFmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
.pos = state.positions[pos] .pos = state.positions[pos]
}); });

View file

@ -512,7 +512,7 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer)
} }
template<> template<>
hintformat & hintformat::operator%(const ValuePrinter & value) HintFmt & HintFmt::operator%(const ValuePrinter & value)
{ {
fmt % value; fmt % value;
return *this; return *this;

View file

@ -86,6 +86,6 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
* magenta. * magenta.
*/ */
template<> template<>
hintformat & hintformat::operator%(const ValuePrinter & value); HintFmt & HintFmt::operator%(const ValuePrinter & value);
} }

View file

@ -64,7 +64,7 @@ json printValueAsJSON(EvalState & state, bool strict,
out[j] = printValueAsJSON(state, strict, *a.value, a.pos, context, copyToStore); out[j] = printValueAsJSON(state, strict, *a.value, a.pos, context, copyToStore);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(state.positions[a.pos], e.addTrace(state.positions[a.pos],
hintfmt("while evaluating attribute '%1%'", j)); HintFmt("while evaluating attribute '%1%'", j));
throw; throw;
} }
} }
@ -81,7 +81,7 @@ json printValueAsJSON(EvalState & state, bool strict,
out.push_back(printValueAsJSON(state, strict, *elem, pos, context, copyToStore)); out.push_back(printValueAsJSON(state, strict, *elem, pos, context, copyToStore));
} catch (Error & e) { } catch (Error & e) {
e.addTrace(state.positions[pos], e.addTrace(state.positions[pos],
hintfmt("while evaluating list element at index %1%", i)); HintFmt("while evaluating list element at index %1%", i));
throw; throw;
} }
i++; i++;

View file

@ -19,8 +19,8 @@ public:
: Error("") : Error("")
{ {
raw = raw_; raw = raw_;
auto hf = hintfmt(args...); auto hf = HintFmt(args...);
err.msg = hintfmt("Bad String Context element: %1%: %2%", Uncolored(hf.str()), raw); err.msg = HintFmt("Bad String Context element: %1%: %2%", Uncolored(hf.str()), raw);
} }
}; };

View file

@ -92,7 +92,7 @@ void handleDiffHook(
} catch (Error & error) { } catch (Error & error) {
ErrorInfo ei = error.info(); ErrorInfo ei = error.info();
// FIXME: wrap errors. // FIXME: wrap errors.
ei.msg = hintfmt("diff hook execution failed: %s", ei.msg.str()); ei.msg = HintFmt("diff hook execution failed: %s", ei.msg.str());
logError(ei); logError(ei);
} }
} }

View file

@ -882,12 +882,12 @@ template<typename... Args>
FileTransferError::FileTransferError(FileTransfer::Error error, std::optional<std::string> response, const Args & ... args) FileTransferError::FileTransferError(FileTransfer::Error error, std::optional<std::string> response, const Args & ... args)
: Error(args...), error(error), response(response) : Error(args...), error(error), response(response)
{ {
const auto hf = hintfmt(args...); const auto hf = HintFmt(args...);
// FIXME: Due to https://github.com/NixOS/nix/issues/3841 we don't know how // FIXME: Due to https://github.com/NixOS/nix/issues/3841 we don't know how
// to print different messages for different verbosity levels. For now // to print different messages for different verbosity levels. For now
// we add some heuristics for detecting when we want to show the response. // we add some heuristics for detecting when we want to show the response.
if (response && (response->size() < 1024 || response->find("<html>") != std::string::npos)) if (response && (response->size() < 1024 || response->find("<html>") != std::string::npos))
err.msg = hintfmt("%1%\n\nresponse body:\n\n%2%", Uncolored(hf.str()), chomp(*response)); err.msg = HintFmt("%1%\n\nresponse body:\n\n%2%", Uncolored(hf.str()), chomp(*response));
else else
err.msg = hf; err.msg = hf;
} }

View file

@ -10,11 +10,11 @@
namespace nix { namespace nix {
SQLiteError::SQLiteError(const char *path, const char *errMsg, int errNo, int extendedErrNo, int offset, hintfmt && hf) SQLiteError::SQLiteError(const char *path, const char *errMsg, int errNo, int extendedErrNo, int offset, HintFmt && hf)
: Error(""), path(path), errMsg(errMsg), errNo(errNo), extendedErrNo(extendedErrNo), offset(offset) : Error(""), path(path), errMsg(errMsg), errNo(errNo), extendedErrNo(extendedErrNo), offset(offset)
{ {
auto offsetStr = (offset == -1) ? "" : "at offset " + std::to_string(offset) + ": "; auto offsetStr = (offset == -1) ? "" : "at offset " + std::to_string(offset) + ": ";
err.msg = hintfmt("%s: %s%s, %s (in '%s')", err.msg = HintFmt("%s: %s%s, %s (in '%s')",
Uncolored(hf.str()), Uncolored(hf.str()),
offsetStr, offsetStr,
sqlite3_errstr(extendedErrNo), sqlite3_errstr(extendedErrNo),
@ -22,7 +22,7 @@ SQLiteError::SQLiteError(const char *path, const char *errMsg, int errNo, int ex
path ? path : "(in-memory)"); path ? path : "(in-memory)");
} }
[[noreturn]] void SQLiteError::throw_(sqlite3 * db, hintfmt && hf) [[noreturn]] void SQLiteError::throw_(sqlite3 * db, HintFmt && hf)
{ {
int err = sqlite3_errcode(db); int err = sqlite3_errcode(db);
int exterr = sqlite3_extended_errcode(db); int exterr = sqlite3_extended_errcode(db);
@ -33,7 +33,7 @@ SQLiteError::SQLiteError(const char *path, const char *errMsg, int errNo, int ex
if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) { if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
auto exp = SQLiteBusy(path, errMsg, err, exterr, offset, std::move(hf)); auto exp = SQLiteBusy(path, errMsg, err, exterr, offset, std::move(hf));
exp.err.msg = hintfmt( exp.err.msg = HintFmt(
err == SQLITE_PROTOCOL err == SQLITE_PROTOCOL
? "SQLite database '%s' is busy (SQLITE_PROTOCOL)" ? "SQLite database '%s' is busy (SQLITE_PROTOCOL)"
: "SQLite database '%s' is busy", : "SQLite database '%s' is busy",
@ -249,7 +249,7 @@ void handleSQLiteBusy(const SQLiteBusy & e, time_t & nextWarning)
if (now > nextWarning) { if (now > nextWarning) {
nextWarning = now + 10; nextWarning = now + 10;
logWarning({ logWarning({
.msg = hintfmt(e.what()) .msg = HintFmt(e.what())
}); });
} }

View file

@ -142,19 +142,19 @@ struct SQLiteError : Error
template<typename... Args> template<typename... Args>
[[noreturn]] static void throw_(sqlite3 * db, const std::string & fs, const Args & ... args) { [[noreturn]] static void throw_(sqlite3 * db, const std::string & fs, const Args & ... args) {
throw_(db, hintfmt(fs, args...)); throw_(db, HintFmt(fs, args...));
} }
SQLiteError(const char *path, const char *errMsg, int errNo, int extendedErrNo, int offset, hintfmt && hf); SQLiteError(const char *path, const char *errMsg, int errNo, int extendedErrNo, int offset, HintFmt && hf);
protected: protected:
template<typename... Args> template<typename... Args>
SQLiteError(const char *path, const char *errMsg, int errNo, int extendedErrNo, int offset, const std::string & fs, const Args & ... args) SQLiteError(const char *path, const char *errMsg, int errNo, int extendedErrNo, int offset, const std::string & fs, const Args & ... args)
: SQLiteError(path, errMsg, errNo, extendedErrNo, offset, hintfmt(fs, args...)) : SQLiteError(path, errMsg, errNo, extendedErrNo, offset, HintFmt(fs, args...))
{ } { }
[[noreturn]] static void throw_(sqlite3 * db, hintfmt && hf); [[noreturn]] static void throw_(sqlite3 * db, HintFmt && hf);
}; };

View file

@ -63,7 +63,7 @@ void setStackSize(rlim_t stackSize)
if (setrlimit(RLIMIT_STACK, &limit) != 0) { if (setrlimit(RLIMIT_STACK, &limit) != 0) {
logger->log( logger->log(
lvlError, lvlError,
hintfmt( HintFmt(
"Failed to increase stack size from %1% to %2% (maximum allowed stack size: %3%): %4%", "Failed to increase stack size from %1% to %2% (maximum allowed stack size: %3%): %4%",
savedStackSize, savedStackSize,
stackSize, stackSize,

View file

@ -11,7 +11,7 @@
namespace nix { namespace nix {
void BaseError::addTrace(std::shared_ptr<Pos> && e, hintfmt hint, bool frame) void BaseError::addTrace(std::shared_ptr<Pos> && e, HintFmt hint, bool frame)
{ {
err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .frame = frame }); err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .frame = frame });
} }
@ -37,7 +37,7 @@ const std::string & BaseError::calcWhat() const
std::optional<std::string> ErrorInfo::programName = std::nullopt; std::optional<std::string> ErrorInfo::programName = std::nullopt;
std::ostream & operator <<(std::ostream & os, const hintfmt & hf) std::ostream & operator <<(std::ostream & os, const HintFmt & hf)
{ {
return os << hf.str(); return os << hf.str();
} }

View file

@ -63,7 +63,7 @@ void printCodeLines(std::ostream & out,
struct Trace { struct Trace {
std::shared_ptr<Pos> pos; std::shared_ptr<Pos> pos;
hintfmt hint; HintFmt hint;
bool frame; bool frame;
}; };
@ -74,7 +74,7 @@ inline bool operator>=(const Trace& lhs, const Trace& rhs);
struct ErrorInfo { struct ErrorInfo {
Verbosity level; Verbosity level;
hintfmt msg; HintFmt msg;
std::shared_ptr<Pos> pos; std::shared_ptr<Pos> pos;
std::list<Trace> traces; std::list<Trace> traces;
@ -113,20 +113,20 @@ public:
template<typename... Args> template<typename... Args>
BaseError(unsigned int status, const Args & ... args) BaseError(unsigned int status, const Args & ... args)
: err { .level = lvlError, .msg = hintfmt(args...), .status = status } : err { .level = lvlError, .msg = HintFmt(args...), .status = status }
{ } { }
template<typename... Args> template<typename... Args>
explicit BaseError(const std::string & fs, const Args & ... args) explicit BaseError(const std::string & fs, const Args & ... args)
: err { .level = lvlError, .msg = hintfmt(fs, args...) } : err { .level = lvlError, .msg = HintFmt(fs, args...) }
{ } { }
template<typename... Args> template<typename... Args>
BaseError(const Suggestions & sug, const Args & ... args) BaseError(const Suggestions & sug, const Args & ... args)
: err { .level = lvlError, .msg = hintfmt(args...), .suggestions = sug } : err { .level = lvlError, .msg = HintFmt(args...), .suggestions = sug }
{ } { }
BaseError(hintfmt hint) BaseError(HintFmt hint)
: err { .level = lvlError, .msg = hint } : err { .level = lvlError, .msg = hint }
{ } { }
@ -159,10 +159,10 @@ public:
template<typename... Args> template<typename... Args>
void addTrace(std::shared_ptr<Pos> && e, std::string_view fs, const Args & ... args) void addTrace(std::shared_ptr<Pos> && e, std::string_view fs, const Args & ... args)
{ {
addTrace(std::move(e), hintfmt(std::string(fs), args...)); addTrace(std::move(e), HintFmt(std::string(fs), args...));
} }
void addTrace(std::shared_ptr<Pos> && e, hintfmt hint, bool frame = false); void addTrace(std::shared_ptr<Pos> && e, HintFmt hint, bool frame = false);
bool hasTrace() const { return !err.traces.empty(); } bool hasTrace() const { return !err.traces.empty(); }
@ -214,8 +214,8 @@ public:
SysError(int errNo, const Args & ... args) SysError(int errNo, const Args & ... args)
: SystemError(""), errNo(errNo) : SystemError(""), errNo(errNo)
{ {
auto hf = hintfmt(args...); auto hf = HintFmt(args...);
err.msg = hintfmt("%1%: %2%", Uncolored(hf.str()), strerror(errNo)); err.msg = HintFmt("%1%: %2%", Uncolored(hf.str()), strerror(errNo));
} }
/** /**

View file

@ -31,6 +31,17 @@ inline void formatHelper(F & f, const T & x, const Args & ... args)
// Interpolate one argument and then recurse. // Interpolate one argument and then recurse.
formatHelper(f % x, args...); formatHelper(f % x, args...);
} }
/**
* Set the correct exceptions for `fmt`.
*/
void setExceptions(boost::format & fmt)
{
fmt.exceptions(
boost::io::all_error_bits ^
boost::io::too_many_args_bit ^
boost::io::too_few_args_bit);
}
} }
/** /**
@ -74,7 +85,7 @@ template<typename... Args>
inline std::string fmt(const std::string & fs, const Args & ... args) inline std::string fmt(const std::string & fs, const Args & ... args)
{ {
boost::format f(fs); boost::format f(fs);
f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit); setExceptions(f);
formatHelper(f, args...); formatHelper(f, args...);
return f.str(); return f.str();
} }
@ -82,9 +93,9 @@ inline std::string fmt(const std::string & fs, const Args & ... args)
/** /**
* Values wrapped in this struct are printed in magenta. * Values wrapped in this struct are printed in magenta.
* *
* By default, arguments to `hintfmt` are printed in magenta. To avoid this, * By default, arguments to `HintFmt` are printed in magenta. To avoid this,
* either wrap the argument in `Uncolored` or add a specialization of * either wrap the argument in `Uncolored` or add a specialization of
* `hintfmt::operator%`. * `HintFmt::operator%`.
*/ */
template <class T> template <class T>
struct Magenta struct Magenta
@ -102,7 +113,7 @@ std::ostream & operator<<(std::ostream & out, const Magenta<T> & y)
/** /**
* Values wrapped in this class are printed without coloring. * Values wrapped in this class are printed without coloring.
* *
* By default, arguments to `hintfmt` are printed in magenta (see `Magenta`). * By default, arguments to `HintFmt` are printed in magenta (see `Magenta`).
*/ */
template <class T> template <class T>
struct Uncolored struct Uncolored
@ -121,65 +132,49 @@ std::ostream & operator<<(std::ostream & out, const Uncolored<T> & y)
* A wrapper around `boost::format` which colors interpolated arguments in * A wrapper around `boost::format` which colors interpolated arguments in
* magenta by default. * magenta by default.
*/ */
class hintfmt class HintFmt
{ {
private: private:
boost::format fmt; boost::format fmt;
public: public:
/**
* Construct a `hintfmt` from a format string, with values to be
* interpolated later with `%`.
*
* This isn't exposed as a single-argument constructor to avoid
* accidentally constructing `hintfmt`s with user-controlled strings. See
* the note on `fmt` for more information.
*/
static hintfmt interpolate(const std::string & formatString)
{
hintfmt result((boost::format(formatString)));
result.fmt.exceptions(
boost::io::all_error_bits ^
boost::io::too_many_args_bit ^
boost::io::too_few_args_bit);
return result;
}
/** /**
* Format the given string literally, without interpolating format * Format the given string literally, without interpolating format
* placeholders. * placeholders.
*/ */
hintfmt(const std::string & literal) HintFmt(const std::string & literal)
: hintfmt("%s", Uncolored(literal)) : HintFmt("%s", Uncolored(literal))
{ } { }
/** /**
* Interpolate the given arguments into the format string. * Interpolate the given arguments into the format string.
*/ */
template<typename... Args> template<typename... Args>
hintfmt(const std::string & format, const Args & ... args) HintFmt(const std::string & format, const Args & ... args)
: fmt(format) : HintFmt(boost::format(format), args...)
{ { }
formatHelper(*this, args...);
}
hintfmt(const hintfmt & hf) HintFmt(const HintFmt & hf)
: fmt(hf.fmt) : fmt(hf.fmt)
{ } { }
hintfmt(boost::format && fmt) template<typename... Args>
HintFmt(boost::format && fmt, const Args & ... args)
: fmt(std::move(fmt)) : fmt(std::move(fmt))
{ } {
setExceptions(fmt);
formatHelper(*this, args...);
}
template<class T> template<class T>
hintfmt & operator%(const T & value) HintFmt & operator%(const T & value)
{ {
fmt % Magenta(value); fmt % Magenta(value);
return *this; return *this;
} }
template<class T> template<class T>
hintfmt & operator%(const Uncolored<T> & value) HintFmt & operator%(const Uncolored<T> & value)
{ {
fmt % value.value; fmt % value.value;
return *this; return *this;
@ -191,6 +186,6 @@ public:
} }
}; };
std::ostream & operator<<(std::ostream & os, const hintfmt & hf); std::ostream & operator<<(std::ostream & os, const HintFmt & hf);
} }

View file

@ -448,7 +448,7 @@ Error readError(Source & source)
auto msg = readString(source); auto msg = readString(source);
ErrorInfo info { ErrorInfo info {
.level = level, .level = level,
.msg = hintfmt(msg), .msg = HintFmt(msg),
}; };
auto havePos = readNum<size_t>(source); auto havePos = readNum<size_t>(source);
assert(havePos == 0); assert(havePos == 0);
@ -457,7 +457,7 @@ Error readError(Source & source)
havePos = readNum<size_t>(source); havePos = readNum<size_t>(source);
assert(havePos == 0); assert(havePos == 0);
info.traces.push_back(Trace { info.traces.push_back(Trace {
.hint = hintfmt(readString(source)) .hint = HintFmt(readString(source))
}); });
} }
return Error(std::move(info)); return Error(std::move(info));

View file

@ -377,7 +377,7 @@ static void daemonLoop(std::optional<TrustedFlag> forceTrustClientOpt)
} catch (Error & error) { } catch (Error & error) {
auto ei = error.info(); auto ei = error.info();
// FIXME: add to trace? // FIXME: add to trace?
ei.msg = hintfmt("error processing connection: %1%", ei.msg.str()); ei.msg = HintFmt("error processing connection: %1%", ei.msg.str());
logError(ei); logError(ei);
} }
} }

View file

@ -98,7 +98,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
} catch (Error & e) { } catch (Error & e) {
e.addTrace( e.addTrace(
state->positions[attr.pos], state->positions[attr.pos],
hintfmt("while evaluating the attribute '%s'", name)); HintFmt("while evaluating the attribute '%s'", name));
throw; throw;
} }
} }

View file

@ -411,7 +411,7 @@ struct CmdFlakeCheck : FlakeCommand
return storePath; return storePath;
} }
} catch (Error & e) { } catch (Error & e) {
e.addTrace(resolve(pos), hintfmt("while checking the derivation '%s'", attrPath)); e.addTrace(resolve(pos), HintFmt("while checking the derivation '%s'", attrPath));
reportError(e); reportError(e);
} }
return std::nullopt; return std::nullopt;
@ -430,7 +430,7 @@ struct CmdFlakeCheck : FlakeCommand
} }
#endif #endif
} catch (Error & e) { } catch (Error & e) {
e.addTrace(resolve(pos), hintfmt("while checking the app definition '%s'", attrPath)); e.addTrace(resolve(pos), HintFmt("while checking the app definition '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
@ -454,7 +454,7 @@ struct CmdFlakeCheck : FlakeCommand
// FIXME: if we have a 'nixpkgs' input, use it to // FIXME: if we have a 'nixpkgs' input, use it to
// evaluate the overlay. // evaluate the overlay.
} catch (Error & e) { } catch (Error & e) {
e.addTrace(resolve(pos), hintfmt("while checking the overlay '%s'", attrPath)); e.addTrace(resolve(pos), HintFmt("while checking the overlay '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
@ -465,7 +465,7 @@ struct CmdFlakeCheck : FlakeCommand
fmt("checking NixOS module '%s'", attrPath)); fmt("checking NixOS module '%s'", attrPath));
state->forceValue(v, pos); state->forceValue(v, pos);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(resolve(pos), hintfmt("while checking the NixOS module '%s'", attrPath)); e.addTrace(resolve(pos), HintFmt("while checking the NixOS module '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
@ -491,7 +491,7 @@ struct CmdFlakeCheck : FlakeCommand
} }
} catch (Error & e) { } catch (Error & e) {
e.addTrace(resolve(pos), hintfmt("while checking the Hydra jobset '%s'", attrPath)); e.addTrace(resolve(pos), HintFmt("while checking the Hydra jobset '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
@ -506,7 +506,7 @@ struct CmdFlakeCheck : FlakeCommand
if (!state->isDerivation(*vToplevel)) if (!state->isDerivation(*vToplevel))
throw Error("attribute 'config.system.build.toplevel' is not a derivation"); throw Error("attribute 'config.system.build.toplevel' is not a derivation");
} catch (Error & e) { } catch (Error & e) {
e.addTrace(resolve(pos), hintfmt("while checking the NixOS configuration '%s'", attrPath)); e.addTrace(resolve(pos), HintFmt("while checking the NixOS configuration '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
@ -540,7 +540,7 @@ struct CmdFlakeCheck : FlakeCommand
throw Error("template '%s' has unsupported attribute '%s'", attrPath, name); throw Error("template '%s' has unsupported attribute '%s'", attrPath, name);
} }
} catch (Error & e) { } catch (Error & e) {
e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath)); e.addTrace(resolve(pos), HintFmt("while checking the template '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
@ -554,7 +554,7 @@ struct CmdFlakeCheck : FlakeCommand
throw Error("bundler must be a function"); throw Error("bundler must be a function");
// TODO: check types of inputs/outputs? // TODO: check types of inputs/outputs?
} catch (Error & e) { } catch (Error & e) {
e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath)); e.addTrace(resolve(pos), HintFmt("while checking the template '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
@ -774,7 +774,7 @@ struct CmdFlakeCheck : FlakeCommand
warn("unknown flake output '%s'", name); warn("unknown flake output '%s'", name);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(resolve(pos), hintfmt("while checking flake output '%s'", name)); e.addTrace(resolve(pos), HintFmt("while checking flake output '%s'", name));
reportError(e); reportError(e);
} }
}); });

File diff suppressed because it is too large Load diff

View file

@ -42,7 +42,7 @@ namespace nix {
makeJSONLogger(*logger)->logEI({ makeJSONLogger(*logger)->logEI({
.name = "error name", .name = "error name",
.msg = hintfmt("this hint has %1% templated %2%!!", .msg = HintFmt("this hint has %1% templated %2%!!",
"yellow", "yellow",
"values"), "values"),
.errPos = Pos(foFile, problem_file, 02, 13) .errPos = Pos(foFile, problem_file, 02, 13)
@ -62,7 +62,7 @@ namespace nix {
throw TestError(e.info()); throw TestError(e.info());
} catch (Error &e) { } catch (Error &e) {
ErrorInfo ei = e.info(); ErrorInfo ei = e.info();
ei.msg = hintfmt("%s; subsequent error message.", Uncolored(e.info().msg.str())); ei.msg = HintFmt("%s; subsequent error message.", Uncolored(e.info().msg.str()));
testing::internal::CaptureStderr(); testing::internal::CaptureStderr();
logger->logEI(ei); logger->logEI(ei);
@ -176,7 +176,7 @@ namespace nix {
logError({ logError({
.name = "error name", .name = "error name",
.msg = hintfmt("this hint has %1% templated %2%!!", .msg = HintFmt("this hint has %1% templated %2%!!",
"yellow", "yellow",
"values"), "values"),
.errPos = Pos(foString, problem_file, 02, 13), .errPos = Pos(foString, problem_file, 02, 13),
@ -193,7 +193,7 @@ namespace nix {
logError({ logError({
.name = "error name", .name = "error name",
.msg = hintfmt("this hint has %1% templated %2%!!", .msg = HintFmt("this hint has %1% templated %2%!!",
"yellow", "yellow",
"values"), "values"),
.errPos = Pos(foFile, problem_file, 02, 13) .errPos = Pos(foFile, problem_file, 02, 13)
@ -208,7 +208,7 @@ namespace nix {
logError({ logError({
.name = "error name", .name = "error name",
.msg = hintfmt("hint %1%", "only"), .msg = HintFmt("hint %1%", "only"),
}); });
auto str = testing::internal::GetCapturedStderr(); auto str = testing::internal::GetCapturedStderr();
@ -225,7 +225,7 @@ namespace nix {
logWarning({ logWarning({
.name = "name", .name = "name",
.msg = hintfmt("there was a %1%", "warning"), .msg = HintFmt("there was a %1%", "warning"),
}); });
auto str = testing::internal::GetCapturedStderr(); auto str = testing::internal::GetCapturedStderr();
@ -241,7 +241,7 @@ namespace nix {
logWarning({ logWarning({
.name = "warning name", .name = "warning name",
.msg = hintfmt("this hint has %1% templated %2%!!", .msg = HintFmt("this hint has %1% templated %2%!!",
"yellow", "yellow",
"values"), "values"),
.errPos = Pos(foStdin, problem_file, 2, 13), .errPos = Pos(foStdin, problem_file, 2, 13),
@ -264,7 +264,7 @@ namespace nix {
auto e = AssertionError(ErrorInfo { auto e = AssertionError(ErrorInfo {
.name = "wat", .name = "wat",
.msg = hintfmt("it has been %1% days since our last error", "zero"), .msg = HintFmt("it has been %1% days since our last error", "zero"),
.errPos = Pos(foString, problem_file, 2, 13), .errPos = Pos(foString, problem_file, 2, 13),
}); });
@ -290,7 +290,7 @@ namespace nix {
auto e = AssertionError(ErrorInfo { auto e = AssertionError(ErrorInfo {
.name = "wat", .name = "wat",
.msg = hintfmt("it has been %1% days since our last error", "zero"), .msg = HintFmt("it has been %1% days since our last error", "zero"),
.errPos = Pos(foString, problem_file, 2, 13), .errPos = Pos(foString, problem_file, 2, 13),
}); });
@ -310,39 +310,39 @@ namespace nix {
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
* hintfmt * HintFmt
* --------------------------------------------------------------------------*/ * --------------------------------------------------------------------------*/
TEST(hintfmt, percentStringWithoutArgs) { TEST(HintFmt, percentStringWithoutArgs) {
const char *teststr = "this is 100%s correct!"; const char *teststr = "this is 100%s correct!";
ASSERT_STREQ( ASSERT_STREQ(
hintfmt(teststr).str().c_str(), HintFmt(teststr).str().c_str(),
teststr); teststr);
} }
TEST(hintfmt, fmtToHintfmt) { TEST(HintFmt, fmtToHintfmt) {
ASSERT_STREQ( ASSERT_STREQ(
hintfmt(fmt("the color of this this text is %1%", "not yellow")).str().c_str(), HintFmt(fmt("the color of this this text is %1%", "not yellow")).str().c_str(),
"the color of this this text is not yellow"); "the color of this this text is not yellow");
} }
TEST(hintfmt, tooFewArguments) { TEST(HintFmt, tooFewArguments) {
ASSERT_STREQ( ASSERT_STREQ(
hintfmt("only one arg %1% %2%", "fulfilled").str().c_str(), HintFmt("only one arg %1% %2%", "fulfilled").str().c_str(),
"only one arg " ANSI_WARNING "fulfilled" ANSI_NORMAL " "); "only one arg " ANSI_WARNING "fulfilled" ANSI_NORMAL " ");
} }
TEST(hintfmt, tooManyArguments) { TEST(HintFmt, tooManyArguments) {
ASSERT_STREQ( ASSERT_STREQ(
hintfmt("what about this %1% %2%", "%3%", "one", "two").str().c_str(), HintFmt("what about this %1% %2%", "%3%", "one", "two").str().c_str(),
"what about this " ANSI_WARNING "%3%" ANSI_NORMAL " " ANSI_YELLOW "one" ANSI_NORMAL); "what about this " ANSI_WARNING "%3%" ANSI_NORMAL " " ANSI_YELLOW "one" ANSI_NORMAL);
} }