aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/fmt.hh
blob: e879fd3b824715a2e07846f91487449bceeb2ae5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#pragma once

#include <boost/format.hpp>
#include <string>
#include "ansicolor.hh"


namespace nix {


/* 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...) {} };


struct FormatOrString
{
    std::string s;
    FormatOrString(std::string s) : s(std::move(s)) { };
    template<class F>
    FormatOrString(const F & f) : s(f.str()) { };
    FormatOrString(const char * s) : s(s) { };
};


/* 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). */

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)
{
    formatHelper(f % x, args...);
}

inline std::string fmt(const std::string & s)
{
    return s;
}

inline std::string fmt(const char * s)
{
    return s;
}

inline std::string fmt(const FormatOrString & fs)
{
    return fs.s;
}

template<typename... Args>
inline std::string fmt(const std::string & fs, const Args & ... args)
{
    boost::format f(fs);
    f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
    formatHelper(f, args...);
    return f.str();
}

// -----------------------------------------------------------------------------
// format function for hints in errors.  same as fmt, except templated values
// are always in yellow.

template <class T>
struct yellowtxt
{
    yellowtxt(const T &s) : value(s) {}
    const T & value;
};

template <class T>
std::ostream & operator<<(std::ostream & out, const yellowtxt<T> & y)
{
    return out << ANSI_WARNING << y.value << ANSI_NORMAL;
}

template <class T>
struct normaltxt
{
    normaltxt(const T & s) : value(s) {}
    const T & value;
};

template <class T>
std::ostream & operator<<(std::ostream & out, const normaltxt<T> & y)
{
    return out << ANSI_NORMAL << y.value;
}

class hintformat
{
public:
    hintformat(const std::string & format) : fmt(format)
    {
        fmt.exceptions(boost::io::all_error_bits ^
                       boost::io::too_many_args_bit ^
                       boost::io::too_few_args_bit);
    }

    hintformat(const hintformat & hf)
        : fmt(hf.fmt)
    { }

    hintformat(format && fmt)
        : fmt(std::move(fmt))
    { }

    template<class T>
    hintformat & operator%(const T & value)
    {
        fmt % yellowtxt(value);
        return *this;
    }

    template<class T>
    hintformat & operator%(const normaltxt<T> & value)
    {
        fmt % value.value;
        return *this;
    }

    std::string str() const
    {
        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));
}

}