Rename SongTagMap to TagVector and allow it to store regular string chunks
[ncmpcpp.git] / src / format_impl.h
blob7655919d3b276fbfe49cc5428957d7bb111d7f38
1 /***************************************************************************
2 * Copyright (C) 2008-2017 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #ifndef NCMPCPP_HAVE_FORMAT_IMPL_H
22 #define NCMPCPP_HAVE_FORMAT_IMPL_H
24 #include <boost/variant.hpp>
26 #include "curses/menu.h"
27 #include "curses/strbuffer.h"
28 #include "format.h"
29 #include "song.h"
30 #include "utility/functional.h"
31 #include "utility/wide_string.h"
33 namespace Format {
35 // Commutative binary operation such that:
36 // - Empty + Empty = Empty
37 // - Empty + Missing = Missing
38 // - Empty + Ok = Ok
39 // - Missing + Missing = Missing
40 // - Missing + Ok = Missing
41 // - Ok + Ok = Ok
42 inline Result &operator+=(Result &base, Result result)
44 if (base == Result::Missing || result == Result::Missing)
45 base = Result::Missing;
46 else if (base == Result::Ok || result == Result::Ok)
47 base = Result::Ok;
48 return base;
51 /*inline std::ostream &operator<<(std::ostream &os, Result r)
53 switch (r)
55 case Result::Empty:
56 os << "empty";
57 break;
58 case Result::Missing:
59 os << "missing";
60 break;
61 case Result::Ok:
62 os << "ok";
63 break;
65 return os;
66 }*/
68 template <typename CharT, typename OutputT, typename SecondOutputT = OutputT>
69 struct Printer: boost::static_visitor<Result>
71 typedef std::basic_string<CharT> StringT;
73 Printer(OutputT &os, const MPD::Song *song, SecondOutputT *second_os, const unsigned flags)
74 : m_output(os)
75 , m_song(song)
76 , m_output_switched(false)
77 , m_second_os(second_os)
78 , m_no_output(0)
79 , m_flags(flags)
80 { }
82 Result operator()(const StringT &s)
84 if (!s.empty())
86 output(s);
87 return Result::Ok;
89 else
90 return Result::Empty;
93 Result operator()(const NC::Color &c)
95 if (m_flags & Flags::Color)
96 output(c);
97 return Result::Empty;
100 Result operator()(NC::Format fmt)
102 if (m_flags & Flags::Format)
103 output(fmt);
104 return Result::Empty;
107 Result operator()(OutputSwitch)
109 if (!m_no_output)
110 m_output_switched = true;
111 return Result::Ok;
114 Result operator()(const SongTag &st)
116 StringT tags;
117 if (m_flags & Flags::Tag && m_song != nullptr)
119 tags = convertString<CharT, char>::apply(
120 m_song->getTags(st.function())
123 if (!tags.empty())
125 if (st.delimiter() > 0)
127 // shorten date/length by simple truncation
128 if (st.function() == &MPD::Song::getDate
129 || st.function() == &MPD::Song::getLength)
130 tags.resize(st.delimiter());
131 else
132 tags = wideShorten(tags, st.delimiter());
134 output(tags, &st);
135 return Result::Ok;
137 else
138 return Result::Missing;
141 // If all Empty -> Empty, if any Ok -> continue with Ok, if any Missing ->
142 // stop with Empty.
143 Result operator()(const Group<CharT> &group)
145 auto visit = [this, &group] {
146 Result result = Result::Empty;
147 for (const auto &ex : group.base())
149 result += boost::apply_visitor(*this, ex);
150 if (result == Result::Missing)
152 result = Result::Empty;
153 break;
156 return result;
159 ++m_no_output;
160 Result result = visit();
161 --m_no_output;
162 if (!m_no_output && result == Result::Ok)
163 visit();
164 return result;
167 // If all Empty or Missing -> Empty, if any Ok -> stop with Ok.
168 Result operator()(const FirstOf<CharT> &first_of)
170 for (const auto &ex : first_of.base())
172 if (boost::apply_visitor(*this, ex) == Result::Ok)
173 return Result::Ok;
175 return Result::Empty;
178 private:
179 // generic version for streams (buffers, menus)
180 template <typename ValueT, typename OutputStreamT>
181 struct output_ {
182 static void exec(OutputStreamT &os, const ValueT &value, const SongTag *) {
183 os << value;
187 // specialization for strings (input/output)
188 template <typename SomeCharT, typename OtherCharT>
189 struct output_<std::basic_string<SomeCharT>, std::basic_string<OtherCharT>> {
190 typedef std::basic_string<SomeCharT> SomeString;
191 typedef std::basic_string<OtherCharT> OtherString;
193 // compile only if string types are the same
194 static typename std::enable_if<
195 std::is_same<SomeString, OtherString>::value,
196 void
197 >::type exec(SomeString &result, const OtherString &s, const SongTag *) {
198 result += s;
201 // When writing to a string, we should ignore all other properties. If this
202 // code is reached, throw an exception.
203 template <typename ValueT, typename SomeCharT>
204 struct output_<ValueT, std::basic_string<SomeCharT>> {
205 static void exec(std::basic_string<CharT> &, const ValueT &, const SongTag *) {
206 throw std::logic_error("non-string property can't be appended to the string");
210 // Specialization for TagVector.
211 template <typename SomeCharT, typename OtherCharT>
212 struct output_<std::basic_string<SomeCharT>, TagVector<OtherCharT> > {
213 // Compile only if string types are the same.
214 static typename std::enable_if<
215 std::is_same<SomeCharT, OtherCharT>::value,
216 void
217 >::type exec(TagVector<OtherCharT> &acc,
218 const std::basic_string<SomeCharT> &s, const SongTag *st) {
219 if (st != nullptr)
220 acc.emplace_back(*st, s);
221 else
222 acc.emplace_back(boost::none, s);
225 // When extracting tags from a song all the other properties should be
226 // ignored. If that's not the case, throw an exception.
227 template <typename ValueT, typename SomeCharT>
228 struct output_<ValueT, TagVector<SomeCharT> > {
229 static void exec(TagVector<SomeCharT> &, const ValueT &, const SongTag *) {
230 throw std::logic_error("Non-string property can't be inserted into the TagVector");
234 template <typename ValueT>
235 void output(const ValueT &value, const SongTag *st = nullptr) const
237 if (!m_no_output)
239 if (m_output_switched && m_second_os != nullptr)
240 output_<ValueT, SecondOutputT>::exec(*m_second_os, value, st);
241 else
242 output_<ValueT, OutputT>::exec(m_output, value, st);
246 OutputT &m_output;
247 const MPD::Song *m_song;
249 bool m_output_switched;
250 SecondOutputT *m_second_os;
252 unsigned m_no_output;
253 const unsigned m_flags;
256 template <typename CharT, typename VisitorT>
257 void visit(VisitorT &visitor, const AST<CharT> &ast)
259 for (const auto &ex : ast.base())
260 boost::apply_visitor(visitor, ex);
263 template <typename CharT, typename ItemT>
264 void print(const AST<CharT> &ast, NC::Menu<ItemT> &menu, const MPD::Song *song,
265 NC::BasicBuffer<CharT> *buffer, const unsigned flags)
267 Printer<CharT, NC::Menu<ItemT>, NC::Buffer> printer(menu, song, buffer, flags);
268 visit(printer, ast);
271 template <typename CharT>
272 void print(const AST<CharT> &ast, NC::BasicBuffer<CharT> &buffer,
273 const MPD::Song *song, const unsigned flags)
275 Printer<CharT, NC::BasicBuffer<CharT>> printer(buffer, song, &buffer, flags);
276 visit(printer, ast);
279 template <typename CharT>
280 std::basic_string<CharT> stringify(const AST<CharT> &ast, const MPD::Song *song)
282 std::basic_string<CharT> result;
283 Printer<CharT, std::basic_string<CharT>> printer(result, song, &result, Flags::Tag);
284 visit(printer, ast);
285 return result;
288 template <typename CharT>
289 TagVector<CharT> flatten(const AST<CharT> &ast, const MPD::Song &song)
291 TagVector<CharT> result;
292 Printer<CharT, TagVector<CharT>> printer(result, &song, &result, Flags::Tag);
293 visit(printer, ast);
294 return result;
299 #endif // NCMPCPP_HAVE_FORMAT__IMPL_H