Simplify option_parser

This commit is contained in:
Andrzej Rybczak
2016-11-19 14:41:32 +01:00
parent 6d313a282b
commit 180c2d26fc
9 changed files with 480 additions and 690 deletions

View File

@@ -390,6 +390,8 @@
## ##
#lastfm_preferred_language = en #lastfm_preferred_language = en
# #
#space_add_mode = add_remove
#
#show_hidden_files_in_local_browser = no #show_hidden_files_in_local_browser = no
# #
## ##

View File

@@ -257,7 +257,7 @@ If enabled, each time song changes lyrics fetcher will be automatically run in b
If enabled, lyrics will be saved in song's directory, otherwise in ~/.lyrics. Note that it needs properly set mpd_music_dir. If enabled, lyrics will be saved in song's directory, otherwise in ~/.lyrics. Note that it needs properly set mpd_music_dir.
.TP .TP
.B generate_win32_compatible_filenames = yes/no .B generate_win32_compatible_filenames = yes/no
If set to yes, filenames generated by ncmpcpp (with tag editor, for lyrics, artists etc.) will not contain the following characters: \\?*:|"<> - otherwise only slash (/) will not be used. If set to yes, filenames generated by ncmpcpp (with tag editor, for lyrics, artists etc.) will not contain the following characters: \\?*:|\"<> - otherwise only slash (/) will not be used.
.TP .TP
.B allow_for_physical_item_deletion = yes/no .B allow_for_physical_item_deletion = yes/no
If set to yes, it will be possible to physically delete files and directories from the disk in the browser. If set to yes, it will be possible to physically delete files and directories from the disk in the browser.
@@ -265,6 +265,9 @@ If set to yes, it will be possible to physically delete files and directories fr
.B lastfm_preferred_language = ISO 639 alpha-2 language code .B lastfm_preferred_language = ISO 639 alpha-2 language code
If set, ncmpcpp will try to get info from last.fm in language you set and if it fails, it will fall back to english. Otherwise it will use english the first time. If set, ncmpcpp will try to get info from last.fm in language you set and if it fails, it will fall back to english. Otherwise it will use english the first time.
.TP .TP
.B space_add_mode = add_remove/always_add
If set to add_remove, attepting to add files that are already in playlist will remove them. Otherwise they can be added multiple times.
.TP
.B show_hidden_files_in_local_browser = yes/no .B show_hidden_files_in_local_browser = yes/no
Trigger for displaying in local browser files and directories that begin with '.' Trigger for displaying in local browser files and directories that begin with '.'
.TP .TP

View File

@@ -36,27 +36,31 @@
#include "utility/html.h" #include "utility/html.h"
#include "utility/string.h" #include "utility/string.h"
std::unique_ptr<LyricsFetcher> toLyricsFetcher(const std::string &s) std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
{ {
std::string s;
is >> s;
if (s == "lyricwiki") if (s == "lyricwiki")
return std::make_unique<LyricwikiFetcher>(); fetcher = std::make_unique<LyricwikiFetcher>();
else if (s == "azlyrics") else if (s == "azlyrics")
return std::make_unique<AzLyricsFetcher>(); fetcher = std::make_unique<AzLyricsFetcher>();
else if (s == "genius") else if (s == "genius")
return std::make_unique<GeniusFetcher>(); fetcher = std::make_unique<GeniusFetcher>();
else if (s == "sing365") else if (s == "sing365")
return std::make_unique<Sing365Fetcher>(); fetcher = std::make_unique<Sing365Fetcher>();
else if (s == "lyricsmania") else if (s == "lyricsmania")
return std::make_unique<LyricsmaniaFetcher>(); fetcher = std::make_unique<LyricsmaniaFetcher>();
else if (s == "metrolyrics") else if (s == "metrolyrics")
return std::make_unique<MetrolyricsFetcher>(); fetcher = std::make_unique<MetrolyricsFetcher>();
else if (s == "justsomelyrics") else if (s == "justsomelyrics")
return std::make_unique<JustSomeLyricsFetcher>(); fetcher = std::make_unique<JustSomeLyricsFetcher>();
else if (s == "tekstowo") else if (s == "tekstowo")
return std::make_unique<TekstowoFetcher>(); fetcher = std::make_unique<TekstowoFetcher>();
else if (s == "internet") else if (s == "internet")
return std::make_unique<InternetLyricsFetcher>(); fetcher = std::make_unique<InternetLyricsFetcher>();
throw std::runtime_error("no lyrics fetcher named '" + s + "'"); else
is.setstate(std::ios::failbit);
return is;
} }
const char LyricsFetcher::msgNotFound[] = "Not found"; const char LyricsFetcher::msgNotFound[] = "Not found";

View File

@@ -47,9 +47,11 @@ protected:
static const char msgNotFound[]; static const char msgNotFound[];
}; };
typedef std::vector<std::unique_ptr<LyricsFetcher>> LyricsFetchers; typedef std::unique_ptr<LyricsFetcher> LyricsFetcher_;
std::unique_ptr<LyricsFetcher> toLyricsFetcher(const std::string &s); typedef std::vector<LyricsFetcher_> LyricsFetchers;
std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher);
/**********************************************************************/ /**********************************************************************/

View File

@@ -19,6 +19,7 @@
***************************************************************************/ ***************************************************************************/
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#include <boost/tokenizer.hpp>
#include <fstream> #include <fstream>
#include <stdexcept> #include <stdexcept>
@@ -166,567 +167,346 @@ std::string adjust_path(std::string s)
return s; return s;
} }
// parser worker for buffer NC::Buffer buffer(const std::string &v)
template <typename ValueT, typename TransformT>
option_parser::worker buffer(NC::Buffer &arg, ValueT &&value, TransformT &&map)
{ {
return option_parser::worker(assign<std::string>(arg, [&arg, map](std::string s) { NC::Buffer result;
NC::Buffer result; Format::print(
auto ast = Format::parse(s, Format::Flags::Color | Format::Flags::Format); Format::parse(v, Format::Flags::Color | Format::Flags::Format),
Format::print(ast, result, nullptr); result,
return map(std::move(result)); nullptr);
}), defaults_to(arg, map(std::forward<ValueT>(value)))); return result;
} }
option_parser::worker border(NC::Border &arg, NC::Border value) void deprecated(const char *option, double version_removal)
{ {
return option_parser::worker(assign<std::string>(arg, [&arg](std::string s) { std::cerr << "WARNING: " << option
NC::Border result; << " is deprecated and will be removed in " << version_removal << "\n";
if (!s.empty())
{
try {
result = boost::lexical_cast<NC::Color>(s);
} catch (boost::bad_lexical_cast &) {
throw std::runtime_error("invalid border: " + s);
}
}
return result;
}), defaults_to(arg, std::move(value)));
} }
option_parser::worker deprecated(const char *option, double version_removal)
{
return option_parser::worker([option, version_removal](std::string) {
std::cerr << "WARNING: " << option << " is deprecated and will be removed in " << version_removal << ".\n";
}, [] { }
);
}
} }
bool Configuration::read(const std::vector<std::string> &config_paths, bool ignore_errors) bool Configuration::read(const std::vector<std::string> &config_paths, bool ignore_errors)
{ {
std::string mpd_host;
unsigned mpd_port;
std::string columns_format;
option_parser p; option_parser p;
// deprecation warnings // deprecation warnings
p.add("default_space_mode", deprecated("default_space_mode", 0.8)); p.add<void>("default_space_mode", nullptr, "", [](std::string) {
deprecated("default_space_mode", 0.8);
});
// keep the same order of variables as in configuration file // keep the same order of variables as in configuration file
p.add("ncmpcpp_directory", assign_default<std::string>( p.add("ncmpcpp_directory", &ncmpcpp_directory, "~/.ncmpcpp/", adjust_directory);
ncmpcpp_directory, "~/.ncmpcpp/", adjust_directory p.add("lyrics_directory", &lyrics_directory, "~/.lyrics/", adjust_directory);
)); p.add<void>("mpd_host", nullptr, "localhost", [](std::string host) {
p.add("lyrics_directory", assign_default<std::string>(
lyrics_directory, "~/.lyrics/", adjust_directory
));
p.add("mpd_host", assign_default<std::string>(
mpd_host, "localhost", [](std::string host) {
// host can be a path to ipc socket, relative to home directory
expand_home(host); expand_home(host);
Mpd.SetHostname(host); Mpd.SetHostname(host);
return host; });
})); p.add<void>("mpd_port", nullptr, "6600", [](std::string port) {
p.add("mpd_port", assign_default<unsigned>( Mpd.SetPort(verbose_lexical_cast<unsigned>(port));
mpd_port, 6600, [](unsigned port) { });
Mpd.SetPort(port); p.add("mpd_music_dir", &mpd_music_dir, "~/music", adjust_directory);
return port; p.add("mpd_connection_timeout", &mpd_connection_timeout, "5");
})); p.add("mpd_crossfade_time", &crossfade_time, "5");
p.add("mpd_music_dir", assign_default<std::string>( p.add("visualizer_fifo_path", &visualizer_fifo_path, "/tmp/mpd.fifo", adjust_path);
mpd_music_dir, "~/music", adjust_directory p.add("visualizer_output_name", &visualizer_output_name, "Visualizer feed");
)); p.add("visualizer_in_stereo", &visualizer_in_stereo, "yes", yes_no);
p.add("mpd_connection_timeout", assign_default( p.add("visualizer_sample_multiplier", &visualizer_sample_multiplier, "1",
mpd_connection_timeout, 5 [](std::string v) {
)); double multiplier = verbose_lexical_cast<double>(v);
p.add("mpd_crossfade_time", assign_default( lowerBoundCheck(multiplier, 0.0);
crossfade_time, 5 return multiplier;
)); });
p.add("visualizer_fifo_path", assign_default<std::string>( p.add("visualizer_sync_interval", &visualizer_sync_interval, "30",
visualizer_fifo_path, "/tmp/mpd.fifo", adjust_path [](std::string v) {
)); unsigned sync_interval = verbose_lexical_cast<unsigned>(v);
p.add("visualizer_output_name", assign_default( lowerBoundCheck<unsigned>(sync_interval, 10);
visualizer_output_name, "Visualizer feed" return boost::posix_time::seconds(sync_interval);
)); });
p.add("visualizer_in_stereo", yes_no( p.add("visualizer_type", &visualizer_type, "wave");
visualizer_in_stereo, true p.add("visualizer_look", &visualizer_chars, "●▮", [](std::string s) {
));
p.add("visualizer_sample_multiplier", assign_default<double>(
visualizer_sample_multiplier, 1.0, [](double v) {
lowerBoundCheck(v, 0.0);
return v;
}));
p.add("visualizer_sync_interval", assign_default<unsigned>(
visualizer_sync_interval, 30, [](unsigned v) {
lowerBoundCheck(v, 10u);
return boost::posix_time::seconds(v);
}));
p.add("visualizer_type", assign_default(
visualizer_type, VisualizerType::Wave
));
p.add("visualizer_look", assign_default<std::string>(
visualizer_chars, "●▮", [](std::string s) {
auto result = ToWString(std::move(s)); auto result = ToWString(std::move(s));
typedef std::wstring::size_type size_type; boundsCheck<std::wstring::size_type>(result.size(), 2, 2);
boundsCheck(result.size(), size_type(2), size_type(2));
return result; return result;
})); });
p.add("visualizer_color", option_parser::worker([this](std::string v) { p.add("visualizer_color", &visualizer_colors,
boost::sregex_token_iterator color(v.begin(), v.end(), boost::regex("\\w+")), end; "blue, cyan, green, yellow, magenta, red", list_of<NC::Color>);
for (; color != end; ++color) p.add("system_encoding", &system_encoding, "", [](std::string encoding) {
{ #ifdef HAVE_LANGINFO_H
try {
visualizer_colors.push_back(boost::lexical_cast<NC::Color>(*color));
} catch (boost::bad_lexical_cast &) {
throw std::runtime_error("invalid color: " + *color);
}
}
if (visualizer_colors.empty())
throw std::runtime_error("empty list");
}, [this] {
visualizer_colors = { NC::Color::Blue, NC::Color::Cyan, NC::Color::Green, NC::Color::Yellow, NC::Color::Magenta, NC::Color::Red };
}));
p.add("system_encoding", assign_default<std::string>(
system_encoding, "", [](std::string enc) {
# ifdef HAVE_LANGINFO_H
// try to autodetect system encoding // try to autodetect system encoding
if (enc.empty()) if (encoding.empty())
{ {
enc = nl_langinfo(CODESET); encoding = nl_langinfo(CODESET);
if (enc == "UTF-8") // mpd uses utf-8 by default so no need to convert if (encoding == "UTF-8") // mpd uses utf-8 by default so no need to convert
enc.clear(); encoding.clear();
} }
# endif // HAVE_LANGINFO_H #endif // HAVE_LANGINFO_H
return enc; return encoding;
})); });
p.add("playlist_disable_highlight_delay", assign_default<unsigned>( p.add("playlist_disable_highlight_delay", &playlist_disable_highlight_delay,
playlist_disable_highlight_delay, 5, [](unsigned v) { "5", [](std::string v) {
return boost::posix_time::seconds(v); return boost::posix_time::seconds(verbose_lexical_cast<unsigned>(v));
})); });
p.add("message_delay_time", assign_default( p.add("message_delay_time", &message_delay_time, "5");
message_delay_time, 5 p.add("song_list_format", &song_list_format,
)); "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", [](std::string v) {
p.add("song_list_format", assign_default<std::string>( return Format::parse(v);
song_list_format, "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", [](std::string v) { });
return Format::parse(v); p.add("song_status_format", &song_status_format,
})); "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) {
p.add("song_status_format", assign_default<std::string>( auto flags = Format::Flags::All ^ Format::Flags::OutputSwitch;
song_status_format, "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) { // precompute wide format for status display
const unsigned flags = Format::Flags::All ^ Format::Flags::OutputSwitch; song_status_wformat = Format::parse(ToWString(v), flags);
// precompute wide format for status display return Format::parse(v, flags);
song_status_wformat = Format::parse(ToWString(v), flags); });
return Format::parse(v, flags); p.add("song_library_format", &song_library_format,
})); "{%n - }{%t}|{%f}", [](std::string v) {
p.add("song_library_format", assign_default<std::string>( return Format::parse(v);
song_library_format, "{%n - }{%t}|{%f}", [](std::string v) { });
return Format::parse(v); p.add("alternative_header_first_line_format", &new_header_first_line,
})); "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [](std::string v) {
p.add("alternative_header_first_line_format", assign_default<std::string>( return Format::parse(ToWString(std::move(v)),
new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [](std::string v) { Format::Flags::All ^ Format::Flags::OutputSwitch);
return Format::parse(ToWString(std::move(v)), });
Format::Flags::All ^ Format::Flags::OutputSwitch p.add("alternative_header_second_line_format", &new_header_second_line,
); "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [](std::string v) {
})); return Format::parse(ToWString(std::move(v)),
p.add("alternative_header_second_line_format", assign_default<std::string>( Format::Flags::All ^ Format::Flags::OutputSwitch);
new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [](std::string v) { });
return Format::parse(ToWString(std::move(v)), p.add("now_playing_prefix", &now_playing_prefix,
Format::Flags::All ^ Format::Flags::OutputSwitch "$b", [this](std::string v) {
); NC::Buffer result = buffer(v);
})); now_playing_prefix_length = wideLength(ToWString(result.str()));
p.add("now_playing_prefix", buffer( return result;
now_playing_prefix, NC::Buffer::init(NC::Format::Bold), [this](NC::Buffer buf) { });
now_playing_prefix_length = wideLength(ToWString(buf.str())); p.add("now_playing_suffix", &now_playing_suffix,
return buf; "$/b", [this](std::string v) {
})); NC::Buffer result = buffer(v);
p.add("now_playing_suffix", buffer( now_playing_suffix_length = wideLength(ToWString(result.str()));
now_playing_suffix, NC::Buffer::init(NC::Format::NoBold), [this](NC::Buffer buf) { return result;
now_playing_suffix_length = wideLength(ToWString(buf.str())); });
return buf; p.add("browser_playlist_prefix", &browser_playlist_prefix, "$2playlist$9 ", buffer);
})); p.add("selected_item_prefix", &selected_item_prefix,
p.add("browser_playlist_prefix", buffer( "$6", [this](std::string v) {
browser_playlist_prefix, NC::Buffer::init(NC::Color::Red, "playlist", NC::Color::End, ' '), id_() NC::Buffer result = buffer(v);
)); selected_item_prefix_length = wideLength(ToWString(result.str()));
p.add("selected_item_prefix", buffer( return result;
selected_item_prefix, NC::Buffer::init(NC::Color::Magenta), [this](NC::Buffer buf) { });
selected_item_prefix_length = wideLength(ToWString(buf.str())); p.add("selected_item_suffix", &selected_item_suffix,
return buf; "$9", [this](std::string v) {
})); NC::Buffer result = buffer(v);
p.add("selected_item_suffix", buffer( selected_item_suffix_length = wideLength(ToWString(result.str()));
selected_item_suffix, NC::Buffer::init(NC::Color::End), [this](NC::Buffer buf) { return result;
selected_item_suffix_length = wideLength(ToWString(buf.str())); });
return buf; p.add("modified_item_prefix", &modified_item_prefix, "$3>$9 ", buffer);
})); p.add("song_window_title_format", &song_window_title_format,
p.add("modified_item_prefix", buffer( "{%a - }{%t}|{%f}", [](std::string v) {
modified_item_prefix, NC::Buffer::init(NC::Color::Green, "> ", NC::Color::End), id_() return Format::parse(v, Format::Flags::Tag);
)); });
p.add("browser_sort_mode", assign_default( p.add("browser_sort_mode", &browser_sort_mode, "name");
browser_sort_mode, SortMode::Name p.add("browser_sort_format", &browser_sort_format,
)); "{%a - }{%t}|{%f} {(%l)}", [](std::string v) {
p.add("browser_sort_format", assign_default<std::string>( return Format::parse(v, Format::Flags::Tag);
browser_sort_format, "{%a - }{%t}|{%f} {(%l)}", [](std::string v) { });
return Format::parse(v, Format::Flags::Tag); p.add("song_columns_list_format", &song_columns_mode_format,
})); "(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}",
p.add("song_window_title_format", assign_default<std::string>( [this](std::string v) {
song_window_title_format, "{%a - }{%t}|{%f}", [](std::string v) { columns = generate_columns(v);
return Format::parse(v, Format::Flags::Tag); return columns_to_format(columns);
})); });
p.add("song_columns_list_format", assign_default<std::string>( p.add("execute_on_song_change", &execute_on_song_change, "", adjust_path);
columns_format, "(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}", p.add("execute_on_player_state_change", &execute_on_player_state_change,
[this](std::string v) { "", adjust_path);
columns = generate_columns(v); p.add("playlist_show_mpd_host", &playlist_show_mpd_host, "no", yes_no);
song_columns_mode_format = columns_to_format(columns); p.add("playlist_show_remaining_time", &playlist_show_remaining_time, "no", yes_no);
return v; p.add("playlist_shorten_total_times", &playlist_shorten_total_times, "no", yes_no);
})); p.add("playlist_separate_albums", &playlist_separate_albums, "no", yes_no);
p.add("execute_on_song_change", assign_default<std::string>( p.add("playlist_display_mode", &playlist_display_mode, "columns");
execute_on_song_change, "", adjust_path p.add("browser_display_mode", &browser_display_mode, "classic");
)); p.add("search_engine_display_mode", &search_engine_display_mode, "classic");
p.add("execute_on_player_state_change", assign_default<std::string>( p.add("playlist_editor_display_mode", &playlist_editor_display_mode, "classic");
execute_on_player_state_change, "", adjust_path p.add("discard_colors_if_item_is_selected", &discard_colors_if_item_is_selected,
)); "yes", yes_no);
p.add("playlist_show_mpd_host", yes_no( p.add("show_duplicate_tags", &MPD::Song::ShowDuplicateTags, "yes", yes_no);
playlist_show_mpd_host, false p.add("incremental_seeking", &incremental_seeking, "yes", yes_no);
)); p.add("seek_time", &seek_time, "1");
p.add("playlist_show_remaining_time", yes_no( p.add("volume_change_step", &volume_change_step, "2");
playlist_show_remaining_time, false p.add("autocenter_mode", &autocenter_mode, "no", yes_no);
)); p.add("centered_cursor", &centered_cursor, "no", yes_no);
p.add("playlist_shorten_total_times", yes_no( p.add("progressbar_look", &progressbar, "=>", [](std::string v) {
playlist_shorten_total_times, false auto result = ToWString(std::move(v));
)); boundsCheck<std::wstring::size_type>(result.size(), 2, 3);
p.add("playlist_separate_albums", yes_no( // If two characters were specified, fill \0 as the third one.
playlist_separate_albums, false
));
p.add("playlist_display_mode", assign_default(
playlist_display_mode, DisplayMode::Columns
));
p.add("browser_display_mode", assign_default(
browser_display_mode, DisplayMode::Classic
));
p.add("search_engine_display_mode", assign_default(
search_engine_display_mode, DisplayMode::Classic
));
p.add("playlist_editor_display_mode", assign_default(
playlist_editor_display_mode, DisplayMode::Classic
));
p.add("discard_colors_if_item_is_selected", yes_no(
discard_colors_if_item_is_selected, true
));
p.add("show_duplicate_tags", yes_no(
MPD::Song::ShowDuplicateTags, true
));
p.add("incremental_seeking", yes_no(
incremental_seeking, true
));
p.add("seek_time", assign_default(
seek_time, 1
));
p.add("volume_change_step", assign_default(
volume_change_step, 2
));
p.add("autocenter_mode", yes_no(
autocenter_mode, false
));
p.add("centered_cursor", yes_no(
centered_cursor, false
));
p.add("progressbar_look", assign_default<std::string>(
progressbar, "=>", [](std::string s) {
auto result = ToWString(std::move(s));
typedef std::wstring::size_type size_type;
boundsCheck(result.size(), size_type(2), size_type(3));
// if two characters were specified, add third one (null)
result.resize(3); result.resize(3);
return result; return result;
})); });
p.add("progressbar_boldness", yes_no( p.add("progressbar_boldness", &progressbar_boldness, "yes", yes_no);
progressbar_boldness, true p.add("default_place_to_search_in", &search_in_db, "database", [](std::string v) {
)); if (v == "database")
p.add("default_place_to_search_in", option_parser::worker([this](std::string v) { return true;
if (v == "database") else if (v == "playlist")
search_in_db = true; return false;
else if (v == "playlist") else
search_in_db = true; invalid_value(v);
else });
throw std::runtime_error("invalid argument: " + v); p.add("user_interface", &design, "classic");
}, defaults_to(search_in_db, true) p.add("data_fetching_delay", &data_fetching_delay, "yes", yes_no);
)); p.add("media_library_primary_tag", &media_lib_primary_tag, "artist", [](std::string v) {
p.add("user_interface", assign_default( if (v == "artist")
design, Design::Classic return MPD_TAG_ARTIST;
)); else if (v == "album_artist")
p.add("data_fetching_delay", yes_no( return MPD_TAG_ALBUM_ARTIST;
data_fetching_delay, true else if (v == "date")
)); return MPD_TAG_DATE;
p.add("media_library_primary_tag", option_parser::worker([this](std::string v) { else if (v == "genre")
if (v == "artist") return MPD_TAG_GENRE;
media_lib_primary_tag = MPD_TAG_ARTIST; else if (v == "composer")
else if (v == "album_artist") return MPD_TAG_COMPOSER;
media_lib_primary_tag = MPD_TAG_ALBUM_ARTIST; else if (v == "performer")
else if (v == "date") return MPD_TAG_PERFORMER;
media_lib_primary_tag = MPD_TAG_DATE; else
else if (v == "genre") invalid_value(v);
media_lib_primary_tag = MPD_TAG_GENRE; });
else if (v == "composer") p.add("default_find_mode", &wrapped_search, "wrapped", [](std::string v) {
media_lib_primary_tag = MPD_TAG_COMPOSER; if (v == "wrapped")
else if (v == "performer") return true;
media_lib_primary_tag = MPD_TAG_PERFORMER; else if (v == "normal")
else return false;
throw std::runtime_error("invalid argument: " + v); else
}, defaults_to(media_lib_primary_tag, MPD_TAG_ARTIST) invalid_value(v);
)); });
p.add("default_find_mode", option_parser::worker([this](std::string v) { p.add("default_tag_editor_pattern", &pattern, "%n - %t");
if (v == "wrapped") p.add("header_visibility", &header_visibility, "yes", yes_no);
wrapped_search = true; p.add("statusbar_visibility", &statusbar_visibility, "yes", yes_no);
else if (v == "normal") p.add("titles_visibility", &titles_visibility, "yes", yes_no);
wrapped_search = false; p.add("header_text_scrolling", &header_text_scrolling, "yes", yes_no);
else p.add("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no);
throw std::runtime_error("invalid argument: " + v); p.add("lines_scrolled", &lines_scrolled, "2");
}, defaults_to(wrapped_search, true) p.add("lyrics_fetchers", &lyrics_fetchers,
)); "lyricwiki, azlyrics, genius, sing365, lyricsmania, metrolyrics, justsomelyrics, tekstowo, internet",
p.add("default_tag_editor_pattern", assign_default( list_of<LyricsFetcher_>);
pattern, "%n - %t" p.add("follow_now_playing_lyrics", &now_playing_lyrics, "no", yes_no);
)); p.add("fetch_lyrics_for_current_song_in_background", &fetch_lyrics_in_background,
p.add("header_visibility", yes_no( "no", yes_no);
header_visibility, true p.add("store_lyrics_in_song_dir", &store_lyrics_in_song_dir, "no", yes_no);
)); p.add("generate_win32_compatible_filenames", &generate_win32_compatible_filenames,
p.add("statusbar_visibility", yes_no( "yes", yes_no);
statusbar_visibility, true p.add("allow_for_physical_item_deletion", &allow_for_physical_item_deletion,
)); "no", yes_no);
p.add("titles_visibility", yes_no( p.add("lastfm_preferred_language", &lastfm_preferred_language, "en");
titles_visibility, true p.add("space_add_mode", &space_add_mode, "add_remove");
)); p.add("show_hidden_files_in_local_browser", &local_browser_show_hidden_files,
p.add("header_text_scrolling", yes_no( "no", yes_no);
header_text_scrolling, true p.add<void>(
)); "screen_switcher_mode", nullptr, "playlist, browser",
p.add("cyclic_scrolling", yes_no( [this](std::string v) {
use_cyclic_scrolling, false if (v == "previous")
)); screen_switcher_previous = true;
p.add("lines_scrolled", assign_default( else
lines_scrolled, 2
));
p.add("lyrics_fetchers", option_parser::worker([this](std::string v) {
boost::sregex_token_iterator fetcher(v.begin(), v.end(), boost::regex("\\w+")), end;
for (; fetcher != end; ++fetcher)
lyrics_fetchers.push_back(toLyricsFetcher(*fetcher));
if (lyrics_fetchers.empty())
throw std::runtime_error("empty list");
}, [this] {
lyrics_fetchers.push_back(std::make_unique<LyricwikiFetcher>());
lyrics_fetchers.push_back(std::make_unique<AzLyricsFetcher>());
lyrics_fetchers.push_back(std::make_unique<GeniusFetcher>());
lyrics_fetchers.push_back(std::make_unique<Sing365Fetcher>());
lyrics_fetchers.push_back(std::make_unique<LyricsmaniaFetcher>());
lyrics_fetchers.push_back(std::make_unique<MetrolyricsFetcher>());
lyrics_fetchers.push_back(std::make_unique<JustSomeLyricsFetcher>());
lyrics_fetchers.push_back(std::make_unique<TekstowoFetcher>());
lyrics_fetchers.push_back(std::make_unique<InternetLyricsFetcher>());
}));
p.add("follow_now_playing_lyrics", yes_no(
now_playing_lyrics, false
));
p.add("fetch_lyrics_for_current_song_in_background", yes_no(
fetch_lyrics_in_background, false
));
p.add("store_lyrics_in_song_dir", yes_no(
store_lyrics_in_song_dir, false
));
p.add("generate_win32_compatible_filenames", yes_no(
generate_win32_compatible_filenames, true
));
p.add("allow_for_physical_item_deletion", yes_no(
allow_for_physical_item_deletion, false
));
p.add("lastfm_preferred_language", assign_default(
lastfm_preferred_language, "en"
));
p.add("space_add_mode", assign_default(
space_add_mode, SpaceAddMode::AlwaysAdd
));
p.add("show_hidden_files_in_local_browser", yes_no(
local_browser_show_hidden_files, false
));
p.add("screen_switcher_mode", option_parser::worker([this](std::string v) {
if (v == "previous")
screen_switcher_previous = true;
else
{
screen_switcher_previous = false;
boost::sregex_token_iterator i(v.begin(), v.end(), boost::regex("\\w+")), j;
for (; i != j; ++i)
{ {
auto screen = stringtoStartupScreenType(*i); screen_switcher_previous = false;
if (screen != ScreenType::Unknown) screen_sequence = list_of<ScreenType>(v, [](std::string s) {
screen_sequence.push_back(screen); auto screen = stringtoStartupScreenType(s);
else if (screen == ScreenType::Unknown)
throw std::runtime_error("unknown screen: " + *i); invalid_value(s);
return screen;
});
} }
} });
}, [this] { p.add("startup_screen", &startup_screen_type, "playlist", [](std::string v) {
screen_switcher_previous = false; auto screen = stringtoStartupScreenType(v);
screen_sequence = { ScreenType::Playlist, ScreenType::Browser }; if (screen == ScreenType::Unknown)
})); invalid_value(v);
p.add("startup_screen", option_parser::worker([this](std::string v) { return screen;
startup_screen_type = stringtoStartupScreenType(v); });
if (startup_screen_type == ScreenType::Unknown) p.add("startup_slave_screen", &startup_slave_screen_type, "", [](std::string v) {
throw std::runtime_error("unknown screen: " + v); boost::optional<ScreenType> screen;
}, defaults_to(startup_screen_type, ScreenType::Playlist) if (!v.empty())
)); {
p.add("startup_slave_screen", option_parser::worker([this](std::string v) { screen = stringtoStartupScreenType(v);
if (!v.empty()) if (screen == ScreenType::Unknown)
{ invalid_value(v);
startup_slave_screen_type = stringtoStartupScreenType(v); }
if (startup_slave_screen_type == ScreenType::Unknown) return screen;
throw std::runtime_error("unknown slave screen: " + v); });
} p.add("startup_slave_screen_focus", &startup_slave_screen_focus, "no", yes_no);
}, defaults_to(startup_slave_screen_type, boost::none) p.add("locked_screen_width_part", &locked_screen_width_part,
)); "50", [](std::string v) {
p.add("startup_slave_screen_focus", yes_no( return verbose_lexical_cast<double>(v) / 100;
startup_slave_screen_focus, false });
)); p.add("ask_for_locked_screen_width_part", &ask_for_locked_screen_width_part,
p.add("locked_screen_width_part", assign_default<double>( "yes", yes_no);
locked_screen_width_part, 50.0, [](double v) { p.add("jump_to_now_playing_song_at_start", &jump_to_now_playing_song_at_start,
return v / 100; "yes", yes_no);
})); p.add("ask_before_clearing_playlists", &ask_before_clearing_playlists,
p.add("ask_for_locked_screen_width_part", yes_no( "yes", yes_no);
ask_for_locked_screen_width_part, true p.add("ask_before_shuffling_playlists", &ask_before_clearing_playlists,
)); "yes", yes_no);
p.add("jump_to_now_playing_song_at_start", yes_no( p.add("clock_display_seconds", &clock_display_seconds, "no", yes_no);
jump_to_now_playing_song_at_start, true p.add("display_volume_level", &display_volume_level, "yes", yes_no);
)); p.add("display_bitrate", &display_bitrate, "no", yes_no);
p.add("ask_before_clearing_playlists", yes_no( p.add("display_remaining_time", &display_remaining_time, "no", yes_no);
ask_before_clearing_playlists, true p.add("regular_expressions", &regex_type, "perl", [](std::string v) {
)); if (v == "none")
p.add("ask_before_shuffling_playlists", yes_no( return boost::regex::icase | boost::regex::literal;
ask_before_shuffling_playlists, true else if (v == "basic")
)); return boost::regex::icase | boost::regex::basic;
p.add("clock_display_seconds", yes_no( else if (v == "extended")
clock_display_seconds, false return boost::regex::icase | boost::regex::extended;
)); else if (v == "perl")
p.add("display_volume_level", yes_no( return boost::regex::icase | boost::regex::perl;
display_volume_level, true else
)); invalid_value(v);
p.add("display_bitrate", yes_no( });
display_bitrate, false p.add("ignore_leading_the", &ignore_leading_the, "no", yes_no);
)); p.add("block_search_constraints_change_if_items_found",
p.add("display_remaining_time", yes_no( &block_search_constraints_change, "yes", yes_no);
display_remaining_time, false p.add("mouse_support", &mouse_support, "yes", yes_no);
)); p.add("mouse_list_scroll_whole_page", &mouse_list_scroll_whole_page, "yes", yes_no);
p.add("regular_expressions", option_parser::worker([this](std::string v) { p.add("empty_tag_marker", &empty_tag, "<empty>");
if (v == "none") p.add("tags_separator", &MPD::Song::TagsSeparator, " | ");
regex_type = boost::regex::literal; p.add("tag_editor_extended_numeration", &tag_editor_extended_numeration, "no", yes_no);
else if (v == "basic") p.add("media_library_sort_by_mtime", &media_library_sort_by_mtime, "no", yes_no);
regex_type = boost::regex::basic; p.add("enable_window_title", &set_window_title, "yes", [](std::string v) {
else if (v == "extended") // Consider this variable only if TERM variable is available and we're not
regex_type = boost::regex::extended; // in emacs terminal nor tty (through any wrapper like screen).
else if (v == "perl") auto term = getenv("TERM");
regex_type = boost::regex::perl; if (term != nullptr
else && strstr(term, "linux") == nullptr
throw std::runtime_error("invalid argument: " + v); && strncmp(term, "eterm", const_strlen("eterm")))
regex_type |= boost::regex::icase; return yes_no(v);
}, defaults_to(regex_type, boost::regex::perl | boost::regex::icase) else
)); {
p.add("ignore_leading_the", yes_no(
ignore_leading_the, false
));
p.add("block_search_constraints_change_if_items_found", yes_no(
block_search_constraints_change, true
));
p.add("mouse_support", yes_no(
mouse_support, true
));
p.add("mouse_list_scroll_whole_page", yes_no(
mouse_list_scroll_whole_page, true
));
p.add("empty_tag_marker", assign_default(
empty_tag, "<empty>"
));
p.add("tags_separator", assign_default(
MPD::Song::TagsSeparator, " | "
));
p.add("tag_editor_extended_numeration", yes_no(
tag_editor_extended_numeration, false
));
p.add("media_library_sort_by_mtime", yes_no(
media_library_sort_by_mtime, false
));
p.add("enable_window_title", [this]() {
// Consider this variable only if TERM variable is available
// and we're not in emacs terminal nor tty (through any wrapper
// like screen).
auto term = getenv("TERM");
if (term != nullptr
&& strstr(term, "linux") == nullptr
&& strncmp(term, "eterm", const_strlen("eterm")))
return yes_no(set_window_title, true);
else
{
set_window_title = false;
return option_parser::worker([](std::string) {}, [] {
std::clog << "Terminal doesn't support window title, skipping 'enable_window_title'.\n"; std::clog << "Terminal doesn't support window title, skipping 'enable_window_title'.\n";
}); return false;
} }
}()); });
p.add("search_engine_default_search_mode", assign_default<unsigned>( p.add("search_engine_default_search_mode", &search_engine_default_search_mode,
search_engine_default_search_mode, 1, [](unsigned v) { "1", [](std::string v) {
boundsCheck(v, 1u, 3u); auto mode = verbose_lexical_cast<unsigned>(v);
return --v; boundsCheck<unsigned>(mode, 1, 3);
})); return --mode;
p.add("external_editor", assign_default<std::string>( });
external_editor, "nano", adjust_path p.add("external_editor", &external_editor, "nano", adjust_path);
)); p.add("use_console_editor", &use_console_editor, "yes", yes_no);
p.add("use_console_editor", yes_no( p.add("colors_enabled", &colors_enabled, "yes", yes_no);
use_console_editor, true p.add("empty_tag_color", &empty_tags_color, "cyan");
)); p.add("header_window_color", &header_color, "default");
p.add("colors_enabled", yes_no( p.add("volume_color", &volume_color, "default");
colors_enabled, true p.add("state_line_color", &state_line_color, "default");
)); p.add("state_flags_color", &state_flags_color, "default");
p.add("empty_tag_color", assign_default( p.add("main_window_color", &main_color, "yellow");
empty_tags_color, NC::Color::Cyan p.add("color1", &color1, "white");
)); p.add("color2", &color2, "green");
p.add("header_window_color", assign_default( p.add("main_window_highlight_color", &main_highlight_color, "yellow");
header_color, NC::Color::Default p.add("progressbar_color", &progressbar_color, "black");
)); p.add("progressbar_elapsed_color", &progressbar_elapsed_color, "green");
p.add("volume_color", assign_default( p.add("statusbar_color", &statusbar_color, "default");
volume_color, NC::Color::Default p.add("alternative_ui_separator_color", &alternative_ui_separator_color, "black");
)); p.add("active_column_color", &active_column_color, "red");
p.add("state_line_color", assign_default( p.add("window_border_color", &window_border, "green", verbose_lexical_cast<NC::Color>);
state_line_color, NC::Color::Default p.add("active_window_border", &active_window_border, "red",
)); verbose_lexical_cast<NC::Color>);
p.add("state_flags_color", assign_default(
state_flags_color, NC::Color::Default
));
p.add("main_window_color", assign_default(
main_color, NC::Color::Yellow
));
p.add("color1", assign_default(
color1, NC::Color::White
));
p.add("color2", assign_default(
color2, NC::Color::Green
));
p.add("main_window_highlight_color", assign_default(
main_highlight_color, NC::Color::Yellow
));
p.add("progressbar_color", assign_default(
progressbar_color, NC::Color::Black
));
p.add("progressbar_elapsed_color", assign_default(
progressbar_elapsed_color, NC::Color::Green
));
p.add("statusbar_color", assign_default(
statusbar_color, NC::Color::Default
));
p.add("alternative_ui_separator_color", assign_default(
alternative_ui_separator_color, NC::Color::Black
));
p.add("active_column_color", assign_default(
active_column_color, NC::Color::Red
));
p.add("window_border_color", border(
window_border, NC::Color::Green
));
p.add("active_window_border", border(
active_window_border, NC::Color::Red
));
return std::all_of( return std::all_of(
config_paths.begin(), config_paths.begin(),
@@ -739,5 +519,3 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
} }
) && p.initialize_undefined(ignore_errors); ) && p.initialize_undefined(ignore_errors);
} }
/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */

View File

@@ -193,7 +193,7 @@ struct Configuration
ScreenType startup_screen_type; ScreenType startup_screen_type;
boost::optional<ScreenType> startup_slave_screen_type; boost::optional<ScreenType> startup_slave_screen_type;
std::list<ScreenType> screen_sequence; std::vector<ScreenType> screen_sequence;
SortMode browser_sort_mode; SortMode browser_sort_mode;

View File

@@ -35,6 +35,18 @@
#include "utility/option_parser.h" #include "utility/option_parser.h"
bool yes_no(std::string v)
{
if (v == "yes")
return true;
else if (v == "no")
return false;
else
invalid_value(v);
}
////////////////////////////////////////////////////////////////////////////////
bool option_parser::run(std::istream &is, bool ignore_errors) bool option_parser::run(std::istream &is, bool ignore_errors)
{ {
// quoted value. leftmost and rightmost quotation marks are the delimiters. // quoted value. leftmost and rightmost quotation marks are the delimiters.
@@ -52,10 +64,14 @@ bool option_parser::run(std::istream &is, bool ignore_errors)
auto it = m_parsers.find(option); auto it = m_parsers.find(option);
if (it != m_parsers.end()) if (it != m_parsers.end())
{ {
try { try
{
it->second.parse(match[2]); it->second.parse(match[2]);
} catch (std::exception &e) { }
std::cerr << "Error while processing option \"" << option << "\": " << e.what() << "\n"; catch (std::exception &e)
{
std::cerr << "Error while processing option \"" << option
<< "\": " << e.what() << "\n";
if (!ignore_errors) if (!ignore_errors)
return false; return false;
} }
@@ -73,14 +89,19 @@ bool option_parser::run(std::istream &is, bool ignore_errors)
bool option_parser::initialize_undefined(bool ignore_errors) bool option_parser::initialize_undefined(bool ignore_errors)
{ {
for (auto &p : m_parsers) for (auto &pp : m_parsers)
{ {
if (!p.second.defined()) auto &p = pp.second;
if (!p.used())
{ {
try { try
p.second.run_default(); {
} catch (std::exception &e) { p.parse_default();
std::cerr << "Error while initializing option \"" << p.first << "\": " << e.what() << "\n"; }
catch (std::exception &e)
{
std::cerr << "Error while initializing option \"" << pp.first
<< "\": " << e.what() << "\n";
if (ignore_errors) if (ignore_errors)
return false; return false;
} }
@@ -88,15 +109,3 @@ bool option_parser::initialize_undefined(bool ignore_errors)
} }
return true; return true;
} }
option_parser::worker yes_no(bool &arg, bool value)
{
return option_parser::worker([&arg](std::string &&v) {
if (v == "yes")
arg = true;
else if (v == "no")
arg = false;
else
throw std::runtime_error("invalid argument: " + v);
}, defaults_to(arg, value));
}

View File

@@ -33,152 +33,144 @@
#ifndef NCMPCPP_UTILITY_OPTION_PARSER_H #ifndef NCMPCPP_UTILITY_OPTION_PARSER_H
#define NCMPCPP_UTILITY_OPTION_PARSER_H #define NCMPCPP_UTILITY_OPTION_PARSER_H
#include <boost/algorithm/string/trim.hpp>
#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <cassert> #include <cassert>
#include <stdexcept> #include <stdexcept>
#include <unordered_map> #include <unordered_map>
#include "utility/functional.h" [[noreturn]] inline void invalid_value(const std::string &v)
struct option_parser
{ {
typedef std::function<void(std::string &&)> parser_t; throw std::runtime_error("invalid value: " + v);
typedef std::function<void()> default_t; }
template <typename DestT, typename SourceT> template <typename DestT>
struct assign_value_once DestT verbose_lexical_cast(std::string v)
{ {
typedef DestT dest_type; try {
typedef typename std::decay<SourceT>::type source_type; return boost::lexical_cast<DestT>(std::move(v));
} catch (boost::bad_lexical_cast &) {
invalid_value(v);
}
}
template <typename ArgT> template <typename ValueT, typename ConvertT>
assign_value_once(DestT &dest, ArgT &&value) std::vector<ValueT> list_of(const std::string &v, ConvertT convert)
: m_dest(dest), m_source(std::make_shared<source_type>(std::forward<ArgT>(value))) { } {
std::vector<ValueT> result;
boost::tokenizer<boost::escaped_list_separator<char>> elems(v);
for (auto &value : elems)
result.push_back(convert(boost::trim_copy(value)));
if (result.empty())
throw std::runtime_error("empty list");
return result;
}
void operator()() template <typename ValueT>
{ std::vector<ValueT> list_of(const std::string &v)
assert(m_source.get() != nullptr); {
m_dest = std::move(*m_source); return list_of<ValueT>(v, verbose_lexical_cast<ValueT>);
m_source.reset(); }
}
private: bool yes_no(std::string v);
DestT &m_dest;
std::shared_ptr<source_type> m_source;
};
template <typename IntermediateT, typename DestT, typename TransformT> ////////////////////////////////////////////////////////////////////////////////
struct parse_and_transform
{
template <typename ArgT>
parse_and_transform(DestT &dest, ArgT &&map)
: m_dest(dest), m_map(std::forward<ArgT>(map)) { }
void operator()(std::string &&v)
{
try {
m_dest = m_map(boost::lexical_cast<IntermediateT>(v));
} catch (boost::bad_lexical_cast &) {
throw std::runtime_error("invalid value: " + v);
}
}
private:
DestT &m_dest;
TransformT m_map;
};
class option_parser
{
template <typename DestT>
struct worker struct worker
{ {
worker() { } template <typename MapT>
worker(DestT *dest, MapT &&map)
: m_dest(dest), m_map(std::forward<MapT>(map)), m_dest_set(false)
{ }
template <typename ParserT, typename DefaultT> void operator()(std::string value)
worker(ParserT &&p, DefaultT &&d)
: m_defined(false), m_parser(std::forward<ParserT>(p))
, m_default(std::forward<DefaultT>(d)) { }
template <typename ValueT>
void parse(ValueT &&value)
{ {
if (m_defined) if (m_dest_set)
throw std::runtime_error("option already defined"); throw std::runtime_error("option already set");
m_parser(std::forward<ValueT>(value)); assign<DestT, void>::apply(m_dest, m_map, value);
m_defined = true; m_dest_set = true;
}
bool defined() const
{
return m_defined;
}
void run_default()
{
if (m_defined)
throw std::runtime_error("option already defined");
m_default();
m_defined = true;
} }
private: private:
bool m_defined; template <typename ValueT, typename VoidT>
parser_t m_parser; struct assign {
default_t m_default; static void apply(ValueT *dest,
std::function<DestT(std::string)> &map,
std::string &value) {
*dest = map(std::move(value));
}
};
template <typename VoidT>
struct assign<void, VoidT> {
static void apply(void *,
std::function<void(std::string)> &map,
std::string &value) {
map(std::move(value));
}
};
DestT *m_dest;
std::function<DestT(std::string)> m_map;
bool m_dest_set;
}; };
template <typename ParserT, typename DefaultT> struct parser {
void add(const std::string &option, ParserT &&p, DefaultT &&d) template <typename StringT, typename SetterT>
parser(StringT &&default_, SetterT &&setter_)
: m_used(false)
, m_default_value(std::forward<StringT>(default_))
, m_worker(std::forward<SetterT>(setter_))
{ }
bool used() const
{
return m_used;
}
void parse(std::string v)
{
m_worker(std::move(v));
m_used = true;
}
void parse_default() const
{
assert(!m_used);
m_worker(m_default_value);
}
private:
bool m_used;
std::string m_default_value;
std::function<void(std::string)> m_worker;
};
std::unordered_map<std::string, parser> m_parsers;
public:
template <typename DestT, typename MapT>
void add(std::string option, DestT *dest, std::string default_, MapT &&map)
{ {
assert(m_parsers.count(option) == 0); assert(m_parsers.count(option) == 0);
m_parsers[option] = worker(std::forward<ParserT>(p), std::forward<DefaultT>(d)); m_parsers.emplace(
std::move(option),
parser(
std::move(default_),
worker<DestT>(dest, std::forward<MapT>(map))));
} }
template <typename WorkerT> template <typename DestT>
void add(const std::string &option, WorkerT &&w) void add(std::string option, DestT *dest, std::string default_)
{ {
assert(m_parsers.count(option) == 0); add(std::move(option), dest, std::move(default_), verbose_lexical_cast<DestT>);
m_parsers[option] = std::forward<WorkerT>(w);
} }
bool run(std::istream &is, bool ignore_errors); bool run(std::istream &is, bool warn_on_errors);
bool initialize_undefined(bool ignore_errors); bool initialize_undefined(bool warn_on_errors);
private:
std::unordered_map<std::string, worker> m_parsers;
}; };
template <typename IntermediateT, typename ArgT, typename TransformT>
option_parser::parser_t assign(ArgT &arg, TransformT &&map = id_())
{
return option_parser::parse_and_transform<IntermediateT, ArgT, TransformT>(
arg, std::forward<TransformT>(map)
);
}
template <typename ArgT, typename ValueT>
option_parser::default_t defaults_to(ArgT &arg, ValueT &&value)
{
return option_parser::assign_value_once<ArgT, ValueT>(
arg, std::forward<ValueT>(value)
);
}
template <typename IntermediateT, typename ArgT, typename ValueT, typename TransformT>
option_parser::worker assign_default(ArgT &arg, ValueT &&value, TransformT &&map)
{
return option_parser::worker(
assign<IntermediateT>(arg, std::forward<TransformT>(map)),
defaults_to(arg, map(std::forward<ValueT>(value)))
);
}
template <typename ArgT, typename ValueT>
option_parser::worker assign_default(ArgT &arg, ValueT &&value)
{
return assign_default<ArgT>(arg, std::forward<ValueT>(value), id_());
}
// workers for specific types
option_parser::worker yes_no(bool &arg, bool value);
#endif // NCMPCPP_UTILITY_OPTION_PARSER_H #endif // NCMPCPP_UTILITY_OPTION_PARSER_H