mirror of
https://github.com/NixOS/nix
synced 2025-06-27 21:01:16 +02:00
Cleanup fmt.hh
When I started contributing to Nix, I found the mix of definitions and names in `fmt.hh` to be rather confusing, especially the small difference between `hintfmt` and `hintformat`. I've renamed many classes and added documentation to most definitions. - `formatHelper` is no longer exported. - `fmt`'s documentation is now with `fmt` rather than (misleadingly) above `formatHelper`. - `yellowtxt` is renamed to `Magenta`. `yellowtxt` wraps its value with `ANSI_WARNING`, but `ANSI_WARNING` has been equal to `ANSI_MAGENTA` for a long time. Now the name is updated. - `normaltxt` is renamed to `Uncolored`. - `hintfmt` has been merged into `hintformat` as extra constructor functions. - `hintformat` has been renamed to `hintfmt`. - The single-argument `hintformat(std::string)` constructor has been renamed to a static member `hintformat::interpolate` to avoid pitfalls with using user-generated strings as format strings.
This commit is contained in:
parent
1ba9780cf5
commit
149bd63afb
14 changed files with 135 additions and 80 deletions
|
@ -8,37 +8,53 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* Inherit some names from other namespaces for convenience.
|
||||
*/
|
||||
using boost::format;
|
||||
|
||||
|
||||
/**
|
||||
* A variadic template that does nothing. Useful to call a function
|
||||
* for all variadic arguments but ignoring the result.
|
||||
*/
|
||||
struct nop { template<typename... T> nop(T...) {} };
|
||||
|
||||
|
||||
/**
|
||||
* A helper for formatting strings. ‘fmt(format, a_0, ..., a_n)’ is
|
||||
* equivalent to ‘boost::format(format) % a_0 % ... %
|
||||
* ... a_n’. However, ‘fmt(s)’ is equivalent to ‘s’ (so no %-expansion
|
||||
* takes place).
|
||||
* A helper for writing `boost::format` expressions.
|
||||
*
|
||||
* These are equivalent:
|
||||
*
|
||||
* ```
|
||||
* formatHelper(formatter, a_0, ..., a_n)
|
||||
* formatter % a_0 % ... % a_n
|
||||
* ```
|
||||
*
|
||||
* With a single argument, `formatHelper(s)` is a no-op.
|
||||
*/
|
||||
template<class F>
|
||||
inline void formatHelper(F & f)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
template<class F, typename T, typename... Args>
|
||||
inline void formatHelper(F & f, const T & x, const Args & ... args)
|
||||
{
|
||||
// Interpolate one argument and then recurse.
|
||||
formatHelper(f % x, args...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper for writing a `boost::format` expression to a string.
|
||||
*
|
||||
* These are (roughly) equivalent:
|
||||
*
|
||||
* ```
|
||||
* fmt(formatString, a_0, ..., a_n)
|
||||
* (boost::format(formatString) % a_0 % ... % a_n).str()
|
||||
* ```
|
||||
*
|
||||
* However, when called with a single argument, the string is returned
|
||||
* unchanged.
|
||||
*
|
||||
* If you write code like this:
|
||||
*
|
||||
* ```
|
||||
* std::cout << boost::format(stringFromUserInput) << std::endl;
|
||||
* ```
|
||||
*
|
||||
* And `stringFromUserInput` contains formatting placeholders like `%s`, then
|
||||
* the code will crash at runtime. `fmt` helps you avoid this pitfall.
|
||||
*/
|
||||
inline std::string fmt(const std::string & s)
|
||||
{
|
||||
return s;
|
||||
|
@ -63,61 +79,107 @@ inline std::string fmt(const std::string & fs, const Args & ... args)
|
|||
return f.str();
|
||||
}
|
||||
|
||||
// format function for hints in errors. same as fmt, except templated values
|
||||
// are always in magenta.
|
||||
/**
|
||||
* Values wrapped in this struct are printed in magenta.
|
||||
*
|
||||
* By default, arguments to `hintfmt` are printed in magenta. To avoid this,
|
||||
* either wrap the argument in `Uncolored` or add a specialization of
|
||||
* `hintfmt::operator%`.
|
||||
*/
|
||||
template <class T>
|
||||
struct magentatxt
|
||||
struct Magenta
|
||||
{
|
||||
magentatxt(const T &s) : value(s) {}
|
||||
Magenta(const T &s) : value(s) {}
|
||||
const T & value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::ostream & operator<<(std::ostream & out, const magentatxt<T> & y)
|
||||
std::ostream & operator<<(std::ostream & out, const Magenta<T> & y)
|
||||
{
|
||||
return out << ANSI_WARNING << y.value << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Values wrapped in this class are printed without coloring.
|
||||
*
|
||||
* By default, arguments to `hintfmt` are printed in magenta (see `Magenta`).
|
||||
*/
|
||||
template <class T>
|
||||
struct normaltxt
|
||||
struct Uncolored
|
||||
{
|
||||
normaltxt(const T & s) : value(s) {}
|
||||
Uncolored(const T & s) : value(s) {}
|
||||
const T & value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::ostream & operator<<(std::ostream & out, const normaltxt<T> & y)
|
||||
std::ostream & operator<<(std::ostream & out, const Uncolored<T> & y)
|
||||
{
|
||||
return out << ANSI_NORMAL << y.value;
|
||||
}
|
||||
|
||||
class hintformat
|
||||
/**
|
||||
* A wrapper around `boost::format` which colors interpolated arguments in
|
||||
* magenta by default.
|
||||
*/
|
||||
class hintfmt
|
||||
{
|
||||
private:
|
||||
boost::format fmt;
|
||||
|
||||
public:
|
||||
hintformat(const std::string & format) : fmt(format)
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
fmt.exceptions(boost::io::all_error_bits ^
|
||||
boost::io::too_many_args_bit ^
|
||||
boost::io::too_few_args_bit);
|
||||
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;
|
||||
}
|
||||
|
||||
hintformat(const hintformat & hf)
|
||||
/**
|
||||
* Format the given string literally, without interpolating format
|
||||
* placeholders.
|
||||
*/
|
||||
hintfmt(const std::string & literal)
|
||||
: hintfmt("%s", Uncolored(literal))
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Interpolate the given arguments into the format string.
|
||||
*/
|
||||
template<typename... Args>
|
||||
hintfmt(const std::string & format, const Args & ... args)
|
||||
: fmt(format)
|
||||
{
|
||||
formatHelper(*this, args...);
|
||||
}
|
||||
|
||||
hintfmt(const hintfmt & hf)
|
||||
: fmt(hf.fmt)
|
||||
{ }
|
||||
|
||||
hintformat(format && fmt)
|
||||
hintfmt(boost::format && fmt)
|
||||
: fmt(std::move(fmt))
|
||||
{ }
|
||||
|
||||
template<class T>
|
||||
hintformat & operator%(const T & value)
|
||||
hintfmt & operator%(const T & value)
|
||||
{
|
||||
fmt % magentatxt(value);
|
||||
fmt % Magenta(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
hintformat & operator%(const normaltxt<T> & value)
|
||||
hintfmt & operator%(const Uncolored<T> & value)
|
||||
{
|
||||
fmt % value.value;
|
||||
return *this;
|
||||
|
@ -127,25 +189,8 @@ public:
|
|||
{
|
||||
return fmt.str();
|
||||
}
|
||||
|
||||
private:
|
||||
format fmt;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const hintformat & hf);
|
||||
|
||||
template<typename... Args>
|
||||
inline hintformat hintfmt(const std::string & fs, const Args & ... args)
|
||||
{
|
||||
hintformat f(fs);
|
||||
formatHelper(f, args...);
|
||||
return f;
|
||||
}
|
||||
|
||||
inline hintformat hintfmt(const std::string & plain_string)
|
||||
{
|
||||
// we won't be receiving any args in this case, so just print the original string
|
||||
return hintfmt("%s", normaltxt(plain_string));
|
||||
}
|
||||
std::ostream & operator<<(std::ostream & os, const hintfmt & hf);
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue