Merge lp:~thomas-voss/biometryd/restructure-cli into lp:biometryd

Proposed by Thomas Voß
Status: Merged
Approved by: Thomas Voß
Approved revision: 10
Merged at revision: 10
Proposed branch: lp:~thomas-voss/biometryd/restructure-cli
Merge into: lp:biometryd
Diff against target: 1263 lines (+554/-432)
19 files modified
src/biometry/CMakeLists.txt (+0/-2)
src/biometry/cmds/enroll.cpp (+8/-16)
src/biometry/cmds/enroll.h (+1/-1)
src/biometry/cmds/help.cpp (+0/-40)
src/biometry/cmds/help.h (+0/-47)
src/biometry/cmds/identify.cpp (+7/-7)
src/biometry/cmds/identify.h (+1/-1)
src/biometry/cmds/list_devices.cpp (+8/-7)
src/biometry/cmds/list_devices.h (+1/-1)
src/biometry/cmds/run.cpp (+4/-6)
src/biometry/cmds/run.h (+1/-1)
src/biometry/cmds/version.cpp (+8/-7)
src/biometry/cmds/version.h (+1/-1)
src/biometry/daemon.cpp (+10/-99)
src/biometry/daemon.h (+1/-4)
src/biometry/daemon_main.cpp (+5/-4)
src/biometry/util/cli.cpp (+240/-25)
src/biometry/util/cli.h (+251/-156)
tests/test_daemon.cpp (+7/-7)
To merge this branch: bzr merge lp:~thomas-voss/biometryd/restructure-cli
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+294229@code.launchpad.net

Commit message

Alter cli::* interface and provide a cli::Command::Context to run invocations.
Restructure setup of default help options and commands.

Description of the change

Alter cli::* interface and provide a cli::Command::Context to run invocations.
Restructure setup of default help options and commands.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/biometry/CMakeLists.txt'
2--- src/biometry/CMakeLists.txt 2016-05-09 14:37:52 +0000
3+++ src/biometry/CMakeLists.txt 2016-05-10 11:29:00 +0000
4@@ -37,8 +37,6 @@
5 cmds/identify.cpp
6 cmds/list_devices.h
7 cmds/list_devices.cpp
8- cmds/help.h
9- cmds/help.cpp
10 cmds/run.h
11 cmds/run.cpp
12 cmds/version.h
13
14=== modified file 'src/biometry/cmds/enroll.cpp'
15--- src/biometry/cmds/enroll.cpp 2016-05-09 14:37:52 +0000
16+++ src/biometry/cmds/enroll.cpp 2016-05-10 11:29:00 +0000
17@@ -35,26 +35,18 @@
18 namespace cli = biometry::util::cli;
19
20 biometry::cmds::Enroll::Enroll()
21- : Command
22- {
23- {
24- Name{"enroll"},
25- Usage{"enroll"},
26- Description{"enrolls a new template to a device"},
27- {}
28- }
29- },
30+ : CommandWithFlagsAndAction{cli::Name{"enroll"}, cli::Usage{"enroll"}, cli::Description{"enrolls a new template to a device"}},
31 user(biometry::User::current())
32 {
33- mutable_info().flags.push_back(cli::make_flag(Name{"device"}, Description{"The device to enroll to"}, device));
34- mutable_info().flags.push_back(cli::make_flag(Name{"device"}, Description{"The device to enroll to"}, device));
35- mutable_info().flags.push_back(cli::make_flag(Name{"user"}, Description{"The user to enroll for"}, device));
36+ flag(cli::make_flag(cli::Name{"device"}, cli::Description{"The device to enroll to"}, device));
37+ flag(cli::make_flag(cli::Name{"device"}, cli::Description{"The device to enroll to"}, device));
38+ flag(cli::make_flag(cli::Name{"user"}, cli::Description{"The user to enroll for"}, device));
39
40- mutable_run() = [this]()
41+ action([this](const cli::Command::Context& ctxt)
42 {
43 if (device.empty())
44 {
45- std::cout << "You must specify a device for enrolling a template" << std::endl;
46+ ctxt.cout << "You must specify a device for enrolling a template" << std::endl;
47 return EXIT_FAILURE;
48 }
49
50@@ -71,10 +63,10 @@
51
52 auto op = device->template_store().enroll(biometry::Application::system(), user);
53
54- std::cout << "Starting template enrollment for " << user << " to " << descriptor->name() << std::endl;
55+ ctxt.cout << "Starting template enrollment for " << user << " to " << descriptor->name() << std::endl;
56
57 op->start_with_observer(std::make_shared<TracingObserver<biometry::TemplateStore::Enrollment>>());
58
59 return 0;
60- };
61+ });
62 }
63
64=== modified file 'src/biometry/cmds/enroll.h'
65--- src/biometry/cmds/enroll.h 2016-05-09 14:37:52 +0000
66+++ src/biometry/cmds/enroll.h 2016-05-10 11:29:00 +0000
67@@ -36,7 +36,7 @@
68 namespace cmds
69 {
70 /// @brief Enroll requests enrollment of a new template to a biometric device.
71-class Enroll : public util::cli::Command
72+class Enroll : public util::cli::CommandWithFlagsAndAction
73 {
74 public:
75 /// @brief Enroll creates a new instance, initializing flags to default values.
76
77=== removed file 'src/biometry/cmds/help.cpp'
78--- src/biometry/cmds/help.cpp 2016-05-09 14:37:52 +0000
79+++ src/biometry/cmds/help.cpp 1970-01-01 00:00:00 +0000
80@@ -1,40 +0,0 @@
81-/*
82- * Copyright (C) 2016 Canonical, Ltd.
83- *
84- * This program is free software; you can redistribute it and/or modify
85- * it under the terms of the GNU Lesser General Public License as published by
86- * the Free Software Foundation; version 3.
87- *
88- * This program is distributed in the hope that it will be useful,
89- * but WITHOUT ANY WARRANTY; without even the implied warranty of
90- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
91- * GNU Lesser General Public License for more details.
92- *
93- * You should have received a copy of the GNU Lesser General Public License
94- * along with this program. If not, see <http://www.gnu.org/licenses/>.
95- *
96- * Authored by: Thomas Voß <thomas.voss@canonical.com>
97- *
98- */
99-
100-#include <biometry/cmds/help.h>
101-
102-namespace cli = biometry::util::cli;
103-
104-biometry::cmds::Help::Help(const CommandEnumerator& enumerator)
105- : Command{{Name{"help"}, Usage{"help"}, Description{"print a help message and exit"}, {}}, [this]()
106- {
107- std::cout << "Usage: biometryd [COMMAND] \n"
108- "\n"
109- "biometryd mediates access to biometric devices. \n"
110- "\n"
111- "Commands:\n";
112- Help::enumerator([](const Command::Ptr& command)
113- {
114- std::cout << " " << command->info().name << " " << command->info().description << std::endl;
115- });
116- return EXIT_FAILURE;
117- }},
118- enumerator{enumerator}
119-{
120-}
121
122=== removed file 'src/biometry/cmds/help.h'
123--- src/biometry/cmds/help.h 2016-05-09 14:37:52 +0000
124+++ src/biometry/cmds/help.h 1970-01-01 00:00:00 +0000
125@@ -1,47 +0,0 @@
126-/*
127- * Copyright (C) 2016 Canonical, Ltd.
128- *
129- * This program is free software; you can redistribute it and/or modify
130- * it under the terms of the GNU Lesser General Public License as published by
131- * the Free Software Foundation; version 3.
132- *
133- * This program is distributed in the hope that it will be useful,
134- * but WITHOUT ANY WARRANTY; without even the implied warranty of
135- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
136- * GNU Lesser General Public License for more details.
137- *
138- * You should have received a copy of the GNU Lesser General Public License
139- * along with this program. If not, see <http://www.gnu.org/licenses/>.
140- *
141- * Authored by: Thomas Voß <thomas.voss@canonical.com>
142- *
143- */
144-
145-#ifndef BIOMETRYD_CMDS_HELP_H_
146-#define BIOMETRYD_CMDS_HELP_H_
147-
148-#include <biometry/util/cli.h>
149-
150-#include <functional>
151-#include <iostream>
152-#include <memory>
153-
154-namespace biometry
155-{
156-namespace cmds
157-{
158-class Help : public util::cli::Command
159-{
160-public:
161- typedef std::function<void(const Command::Ptr&)> Enumerator;
162- typedef std::function<void(const Enumerator&)> CommandEnumerator;
163-
164- Help(const CommandEnumerator& enumerator);
165-
166-private:
167- CommandEnumerator enumerator;
168-};
169-}
170-}
171-
172-#endif // BIOMETRYD_CMDS_HELP_H_
173
174=== modified file 'src/biometry/cmds/identify.cpp'
175--- src/biometry/cmds/identify.cpp 2016-05-09 14:37:52 +0000
176+++ src/biometry/cmds/identify.cpp 2016-05-10 11:29:00 +0000
177@@ -37,15 +37,15 @@
178 namespace cli = biometry::util::cli;
179
180 biometry::cmds::Identify::Identify()
181- : Command{{Name{"identify"}, Usage{"identify"}, Description{"tries to identify the user holding the device"}, {}}}
182+ : CommandWithFlagsAndAction{cli::Name{"identify"}, cli::Usage{"identify"}, cli::Description{"tries to identify the user holding the device"}}
183 {
184- mutable_info().flags.push_back(cli::make_flag(Name{"device"}, Description{"The device to enroll to"}, device));
185- mutable_info().flags.push_back(cli::make_flag(Command::Name{"config"}, Command::Description{"The daemon configuration"}, config));
186- mutable_run() = [this]()
187+ flag(cli::make_flag(cli::Name{"device"}, cli::Description{"The device to enroll to"}, device));
188+ flag(cli::make_flag(cli::Name{"config"}, cli::Description{"The daemon configuration"}, config));
189+ action([this](const cli::Command::Context& ctxt)
190 {
191 if (device.empty())
192 {
193- std::cout << "You must specify a device for identification" << std::endl;
194+ ctxt.cout << "You must specify a device for identification" << std::endl;
195 return EXIT_FAILURE;
196 }
197
198@@ -62,10 +62,10 @@
199
200 auto op = device->identifier().identify_user(biometry::Application::system(), biometry::Reason{"requested by cli"});
201
202- std::cout << "Starting identification using device " << descriptor->name() << std::endl;
203+ ctxt.cout << "Starting identification using device " << descriptor->name() << std::endl;
204
205 op->start_with_observer(std::make_shared<TracingObserver<biometry::Identification>>());
206
207 return 0;
208- };
209+ });
210 }
211
212=== modified file 'src/biometry/cmds/identify.h'
213--- src/biometry/cmds/identify.h 2016-05-09 14:37:52 +0000
214+++ src/biometry/cmds/identify.h 2016-05-10 11:29:00 +0000
215@@ -36,7 +36,7 @@
216 namespace cmds
217 {
218 /// @brief Identify requests identification of the user.
219-class Identify : public util::cli::Command
220+class Identify : public util::cli::CommandWithFlagsAndAction
221 {
222 public:
223 /// @brief Enroll creates a new instance, initializing flags to default values.
224
225=== modified file 'src/biometry/cmds/list_devices.cpp'
226--- src/biometry/cmds/list_devices.cpp 2016-05-09 14:37:52 +0000
227+++ src/biometry/cmds/list_devices.cpp 2016-05-10 11:29:00 +0000
228@@ -25,12 +25,13 @@
229 namespace cli = biometry::util::cli;
230
231 biometry::cmds::ListDevices::ListDevices()
232- : Command{{Name{"list-devices"}, Usage{"list-devices"}, Description{"lists all known devices"}, {}}, []()
233- {
234- std::cout << "Known devices:" << std::endl;
235- for (const auto& pair : biometry::device_registry())
236- std::cout << " - " << pair.first << "\t" << pair.second->description() << std::endl;
237- return 0;
238- }}
239+ : CommandWithFlagsAndAction{cli::Name{"list-devices"}, cli::Usage{"list-devices"}, cli::Description{"lists all known devices"}}
240 {
241+ action([](const cli::Command::Context& ctxt)
242+ {
243+ ctxt.cout << "Known devices:" << std::endl;
244+ for (const auto& pair : biometry::device_registry())
245+ ctxt.cout << " - " << pair.first << "\t" << pair.second->description() << std::endl;
246+ return 0;
247+ });
248 }
249
250=== modified file 'src/biometry/cmds/list_devices.h'
251--- src/biometry/cmds/list_devices.h 2016-05-09 14:37:52 +0000
252+++ src/biometry/cmds/list_devices.h 2016-05-10 11:29:00 +0000
253@@ -26,7 +26,7 @@
254 {
255 namespace cmds
256 {
257-class ListDevices : public util::cli::Command
258+class ListDevices : public util::cli::CommandWithFlagsAndAction
259 {
260 public:
261 ListDevices();
262
263=== modified file 'src/biometry/cmds/run.cpp'
264--- src/biometry/cmds/run.cpp 2016-05-09 14:37:52 +0000
265+++ src/biometry/cmds/run.cpp 2016-05-10 11:29:00 +0000
266@@ -47,11 +47,11 @@
267
268
269 biometry::cmds::Run::Run(const BusFactory& bus_factory)
270- : Command{{Name{"run"}, Usage{"run"}, Description{"run the daemon"}, {}}},
271+ : CommandWithFlagsAndAction{cli::Name{"run"}, cli::Usage{"run"}, cli::Description{"run the daemon"}},
272 bus_factory{bus_factory}
273 {
274- mutable_info().flags.push_back(cli::make_flag(Command::Name{"config"}, Command::Description{"The daemon configuration"}, config));
275- mutable_run() = [this]()
276+ flag(cli::make_flag(cli::Name{"config"}, cli::Description{"The daemon configuration"}, config));
277+ action([this](const cli::Command::Context&)
278 {
279 auto trap = core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_term});
280 trap->signal_raised().connect([trap](const core::posix::Signal&)
281@@ -59,8 +59,6 @@
282 trap->stop();
283 });
284
285- std::cout << "here " << config.get() << std::endl;
286-
287 using StreamingJsonConfigurationBuilder = util::StreamingConfigurationBuilder<util::JsonConfigurationBuilder>;
288 StreamingJsonConfigurationBuilder builder
289 {
290@@ -92,5 +90,5 @@
291 runtime->stop();
292
293 return EXIT_SUCCESS;
294- };
295+ });
296 }
297
298=== modified file 'src/biometry/cmds/run.h'
299--- src/biometry/cmds/run.h 2016-05-09 14:37:52 +0000
300+++ src/biometry/cmds/run.h 2016-05-10 11:29:00 +0000
301@@ -36,7 +36,7 @@
302 {
303 namespace cmds
304 {
305-class Run : public util::cli::Command
306+class Run : public util::cli::CommandWithFlagsAndAction
307 {
308 public:
309 /// @brief BusFactory models creation of bus instances.
310
311=== modified file 'src/biometry/cmds/version.cpp'
312--- src/biometry/cmds/version.cpp 2016-05-09 14:37:52 +0000
313+++ src/biometry/cmds/version.cpp 2016-05-10 11:29:00 +0000
314@@ -23,12 +23,13 @@
315 namespace cli = biometry::util::cli;
316
317 biometry::cmds::Version::Version()
318- : Command{{Name{"version"}, Usage{"version"}, Description{"print the version of the daemon"}, {}}, []()
319- {
320- std::uint32_t major, minor, patch;
321- biometry::version(major, minor, patch);
322- std::cout << "biometryd " << major << "." << minor << "." << patch << std::endl;
323- return 0;
324- }}
325+ : CommandWithFlagsAndAction{cli::Name{"version"}, cli::Usage{"version"}, cli::Description{"print the version of the daemon"}}
326 {
327+ action([](const cli::Command::Context& ctxt)
328+ {
329+ std::uint32_t major, minor, patch;
330+ biometry::version(major, minor, patch);
331+ ctxt.cout << "biometryd " << major << "." << minor << "." << patch << std::endl;
332+ return 0;
333+ });
334 }
335
336=== modified file 'src/biometry/cmds/version.h'
337--- src/biometry/cmds/version.h 2016-05-09 14:37:52 +0000
338+++ src/biometry/cmds/version.h 2016-05-10 11:29:00 +0000
339@@ -30,7 +30,7 @@
340 {
341 namespace cmds
342 {
343-class Version : public util::cli::Command
344+class Version : public util::cli::CommandWithFlagsAndAction
345 {
346 public:
347 Version();
348
349=== modified file 'src/biometry/daemon.cpp'
350--- src/biometry/daemon.cpp 2016-05-09 14:37:52 +0000
351+++ src/biometry/daemon.cpp 2016-05-10 11:29:00 +0000
352@@ -22,7 +22,6 @@
353 #include <biometry/devices/plugin/enumerator.h>
354
355 #include <biometry/cmds/enroll.h>
356-#include <biometry/cmds/help.h>
357 #include <biometry/cmds/identify.h>
358 #include <biometry/cmds/list_devices.h>
359 #include <biometry/cmds/run.h>
360@@ -37,106 +36,18 @@
361 namespace cli = biometry::util::cli;
362 namespace po = boost::program_options;
363
364-namespace
365-{
366-std::multimap<cli::Command::Name, std::function<void(const std::string&)>>& notifiers()
367-{
368- static std::multimap<cli::Command::Name, std::function<void(const std::string&)>> instance;
369- return instance;
370-}
371-}
372-
373-cli::Command::Flag::Flag(const Name& name, const Description& description)
374- : name_{name},
375- description_{description}
376-{
377-}
378-
379-const cli::Command::Name& cli::Command::Flag::name() const
380-{
381- return name_;
382-}
383-
384-const cli::Command::Description& cli::Command::Flag::description() const
385-{
386- return description_;
387-}
388-
389-biometry::Daemon::Daemon() : device_registrar{biometry::devices::plugin::DirectoryEnumerator{Configuration::default_plugin_directory()}}
390-{
391- install_command(std::make_shared<cmds::Enroll>());
392- install_command(std::make_shared<cmds::Identify>());
393- install_command(std::make_shared<cmds::ListDevices>());
394- install_command(std::make_shared<cmds::Run>());
395- install_command(std::make_shared<cmds::Version>());
396-
397- help = std::make_shared<cmds::Help>([this](const cmds::Help::Enumerator& enumerator)
398- {
399- for (const auto& pair : cmds)
400- enumerator(pair.second);
401- });
402+biometry::Daemon::Daemon()
403+ : device_registrar{biometry::devices::plugin::DirectoryEnumerator{Configuration::default_plugin_directory()}},
404+ cmd{cli::Name{"biometryd"}, cli::Usage{"biometryd"}, cli::Description{"biometryd"}}
405+{
406+ cmd.command(std::make_shared<cmds::Enroll>())
407+ .command(std::make_shared<cmds::Identify>())
408+ .command(std::make_shared<cmds::ListDevices>())
409+ .command(std::make_shared<cmds::Run>())
410+ .command(std::make_shared<cmds::Version>());
411 }
412
413 int biometry::Daemon::run(const std::vector<std::string>& args)
414 {
415- po::positional_options_description pdesc;
416- pdesc.add("command", 1);
417-
418- po::options_description desc("Options");
419- desc.add_options()
420- ("command", po::value<std::string>()->default_value(help->info().name), "the command to be executed");
421-
422- for (const auto& pair : cmds)
423- {
424- po::options_description cd(pair.first);
425- for (auto flag : pair.second->info().flags)
426- {
427- // Depending on the name of the flag, we only ever install it once
428- // to the options_description to avoid triggering an issue caused
429- // by ambigious options. Instead, we relay the notify method to inform multiple
430- // flags of the same name that a new value is available.
431- if (notifiers().count(flag->name()) == 0)
432- {
433- auto v = po::value<std::string>()->notifier([flag](const std::string& s)
434- {
435- // Iterate over all known identifiers.
436- auto range = notifiers().equal_range(flag->name());
437- while (range.first != range.second)
438- {
439- (range.first->second)(s);
440- ++range.first;
441- }
442- });
443-
444- cd.add_options()(flag->name().as_string().c_str(), v, flag->description().as_string().c_str());
445- }
446-
447- notifiers().insert(std::make_pair(flag->name(), [flag](const std::string& s) { flag->notify(s); }));
448- }
449- desc.add(cd);
450- }
451-
452- try
453- {
454- po::variables_map vm;
455- po::store(po::command_line_parser(args).options(desc).positional(pdesc).run(), vm);
456- po::notify(vm);
457-
458- auto command = vm["command"].as<std::string>();
459-
460- if (command == "help")
461- return help->run();
462-
463- return cmds[command]->run();
464- }
465- catch (const std::exception& e)
466- {
467- std::cout << e.what() << std::endl;
468- return help->run();
469- }
470-}
471-
472-void biometry::Daemon::install_command(const cli::Command::Ptr& command)
473-{
474- cmds[command->info().name] = command;
475+ return cmd.run({std::cin, std::cout, args});
476 }
477
478=== modified file 'src/biometry/daemon.h'
479--- src/biometry/daemon.h 2016-05-09 14:37:52 +0000
480+++ src/biometry/daemon.h 2016-05-10 11:29:00 +0000
481@@ -54,11 +54,8 @@
482 int run(const std::vector<std::string>& args);
483
484 private:
485- void install_command(const util::cli::Command::Ptr& command);
486-
487 DeviceRegistrar device_registrar;
488- std::unordered_map<std::string, util::cli::Command::Ptr> cmds;
489- util::cli::Command::Ptr help;
490+ util::cli::CommandWithSubcommands cmd;
491 };
492
493
494
495=== modified file 'src/biometry/daemon_main.cpp'
496--- src/biometry/daemon_main.cpp 2016-05-04 12:17:44 +0000
497+++ src/biometry/daemon_main.cpp 2016-05-10 11:29:00 +0000
498@@ -19,11 +19,12 @@
499
500 #include <biometry/daemon.h>
501
502+#include <biometry/util/cli.h>
503+
504+namespace cli = biometry::util::cli;
505+
506 int main(int argc, char** argv)
507 {
508- std::vector<std::string> args;
509- for (int i = 1; i < argc; i++) args.emplace_back(argv[i]);
510-
511 biometry::Daemon biometryd;
512- return biometryd.run(args);
513+ return biometryd.run(cli::args(argc, argv));
514 }
515
516=== modified file 'src/biometry/util/cli.cpp'
517--- src/biometry/util/cli.cpp 2016-05-09 14:37:52 +0000
518+++ src/biometry/util/cli.cpp 2016-05-10 11:29:00 +0000
519@@ -19,30 +19,245 @@
520
521 #include <biometry/util/cli.h>
522
523+#include <boost/format.hpp>
524+#include <boost/program_options.hpp>
525+
526 namespace cli = biometry::util::cli;
527-
528-cli::Command::Info cli::Command::info() const
529-{
530- return info_;
531-}
532-
533-int cli::Command::run()
534-{
535- return run_();
536-}
537-
538-cli::Command::Command(const cli::Command::Info& info, const std::function<int()>& run)
539- : info_(info),
540- run_{run}
541-{
542-}
543-
544-cli::Command::Info& cli::Command::mutable_info()
545-{
546- return info_;
547-}
548-
549-std::function<int()>& cli::Command::mutable_run()
550-{
551- return run_;
552+namespace po = boost::program_options;
553+
554+namespace
555+{
556+namespace pattern
557+{
558+static constexpr const char* help_for_command_with_subcommands =
559+"NAME:\n"
560+" %1% - %2%\n"
561+"\n"
562+"USAGE:\n"
563+" %3% [command options] [arguments...]";
564+
565+static constexpr const char* commands = "COMMANDS:";
566+static constexpr const char* command = " %1% %2%";
567+
568+static constexpr const char* options = "OPTIONS:";
569+static constexpr const char* option = " --%1% %2%";
570+}
571+
572+void add_to_desc_for_flags(po::options_description& desc, const std::set<cli::Flag::Ptr>& flags)
573+{
574+ for (auto flag : flags)
575+ {
576+ auto v = po::value<std::string>()->notifier([flag](const std::string& s)
577+ {
578+ flag->notify(s);
579+ });
580+ desc.add_options()(flag->name().as_string().c_str(), v, flag->description().as_string().c_str());
581+ }
582+}
583+}
584+
585+std::vector<std::string> cli::args(int argc, char **argv)
586+{
587+ std::vector<std::string> result;
588+ for (int i = 1; i < argc; i++) result.push_back(argv[i]);
589+ return result;
590+}
591+
592+const cli::Name& cli::Flag::name() const
593+{
594+ return name_;
595+}
596+
597+const cli::Description& cli::Flag::description() const
598+{
599+ return description_;
600+}
601+
602+cli::Flag::Flag(const Name& name, const Description& description)
603+ : name_{name},
604+ description_{description}
605+{
606+}
607+
608+cli::Name cli::Command::name() const
609+{
610+ return name_;
611+}
612+
613+cli::Usage cli::Command::usage() const
614+{
615+ return usage_;
616+}
617+
618+cli::Description cli::Command::description() const
619+{
620+ return description_;
621+}
622+
623+cli::Command::Command(const cli::Name& name, const cli::Usage& usage, const cli::Description& description)
624+ : name_(name),
625+ usage_(usage),
626+ description_(description)
627+{
628+}
629+
630+cli::CommandWithSubcommands::CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description)
631+ : Command{name, usage, description}
632+{
633+ command(std::make_shared<cmd::Help>(*this));
634+}
635+
636+cli::CommandWithSubcommands& cli::CommandWithSubcommands::command(const Command::Ptr& command)
637+{
638+ commands_[command->name().as_string()] = command;
639+ return *this;
640+}
641+
642+cli::CommandWithSubcommands& cli::CommandWithSubcommands::flag(const Flag::Ptr& flag)
643+{
644+ flags_.insert(flag);
645+ return *this;
646+}
647+
648+void cli::CommandWithSubcommands::help(std::ostream& out)
649+{
650+ out << boost::format(pattern::help_for_command_with_subcommands)
651+ % name().as_string() % usage().as_string()
652+ % name().as_string() << std::endl;
653+
654+ if (flags_.size() > 0)
655+ {
656+ out << std::endl << pattern::options << std::endl;
657+ for (const auto& flag : flags_)
658+ out << boost::format(pattern::option) % flag->name() % flag->description() << std::endl;
659+ }
660+
661+ if (commands_.size() > 0)
662+ {
663+ out << std::endl << pattern::commands << std::endl;
664+ for (const auto& cmd : commands_)
665+ out << boost::format(pattern::command) % cmd.second->name() % cmd.second->description() << std::endl;
666+ }
667+}
668+
669+int cli::CommandWithSubcommands::run(const cli::Command::Context& ctxt)
670+{
671+ po::positional_options_description pdesc;
672+ pdesc.add("command", 1);
673+
674+ po::options_description desc("Options");
675+ desc.add_options()("command", po::value<std::string>()->required(), "the command to be executed");
676+
677+ add_to_desc_for_flags(desc, flags_);
678+
679+ try
680+ {
681+ po::variables_map vm;
682+ auto parsed = po::command_line_parser(ctxt.args).options(desc).positional(pdesc).allow_unregistered().run();
683+ po::store(parsed, vm);
684+ po::notify(vm);
685+
686+ return commands_[vm["command"].as<std::string>()]->run(cli::Command::Context
687+ {
688+ ctxt.cin,
689+ ctxt.cout,
690+ po::collect_unrecognized(parsed.options, po::include_positional)
691+ });
692+ }
693+ catch (const po::error& e)
694+ {
695+ ctxt.cout << e.what() << std::endl;
696+ help(ctxt.cout);
697+ return EXIT_FAILURE;
698+ }
699+
700+ return EXIT_FAILURE;
701+}
702+
703+cli::CommandWithFlagsAndAction::CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description)
704+ : Command{name, usage, description}
705+{
706+}
707+
708+cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::flag(const Flag::Ptr& flag)
709+{
710+ flags_.insert(flag);
711+ return *this;
712+}
713+
714+cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::action(const Action& action)
715+{
716+ action_ = action;
717+ return *this;
718+}
719+
720+int cli::CommandWithFlagsAndAction::run(const Context& ctxt)
721+{
722+ po::options_description cd(name().as_string());
723+
724+ bool help_requested{false};
725+ cd.add_options()("help", po::bool_switch(&help_requested), "produces a help message");
726+
727+ add_to_desc_for_flags(cd, flags_);
728+
729+ try
730+ {
731+ po::variables_map vm;
732+ auto parsed = po::command_line_parser(ctxt.args).options(cd).allow_unregistered().run();
733+ po::store(parsed, vm);
734+ po::notify(vm);
735+
736+ if (help_requested)
737+ {
738+ help(ctxt.cout);
739+ return EXIT_SUCCESS;
740+ }
741+
742+ return action_(cli::Command::Context
743+ {
744+ ctxt.cin,
745+ ctxt.cout,
746+ po::collect_unrecognized(parsed.options, po::include_positional)
747+ });
748+ }
749+ catch (const po::error& e)
750+ {
751+ ctxt.cout << e.what() << std::endl;
752+ help(ctxt.cout);
753+ return EXIT_FAILURE;
754+ }
755+
756+ return EXIT_FAILURE;
757+}
758+
759+void cli::CommandWithFlagsAndAction::help(std::ostream& out)
760+{
761+ out << boost::format(pattern::help_for_command_with_subcommands)
762+ % name().as_string() % description().as_string()
763+ % name().as_string() << std::endl;
764+
765+ if (flags_.size() > 0)
766+ {
767+ out << std::endl << boost::format(pattern::options) << std::endl;
768+ for (const auto& flag : flags_)
769+ out << boost::format(pattern::option) % flag->name() % flag->description() << std::endl;
770+ }
771+}
772+
773+cli::cmd::Help::Help(Command& cmd)
774+ : Command{cli::Name{"help"}, cli::Usage{"prints a short help message"}, cli::Description{"prints a short help message"}},
775+ command{cmd}
776+{
777+}
778+
779+// From Command
780+int cli::cmd::Help::run(const Context &context)
781+{
782+ command.help(context.cout);
783+ return EXIT_FAILURE;
784+}
785+
786+void cli::cmd::Help::help(std::ostream &out)
787+{
788+ command.help(out);
789 }
790
791=== modified file 'src/biometry/util/cli.h'
792--- src/biometry/util/cli.h 2016-05-09 14:37:52 +0000
793+++ src/biometry/util/cli.h 2016-05-10 11:29:00 +0000
794@@ -26,9 +26,11 @@
795 #include <iomanip>
796 #include <iostream>
797 #include <memory>
798+#include <set>
799 #include <sstream>
800 #include <stdexcept>
801 #include <string>
802+#include <unordered_map>
803
804 namespace biometry
805 {
806@@ -78,170 +80,263 @@
807 return out << std::setw(max) << std::left << scs.as_string();
808 }
809
810+// We are imposing size constraints to ensure a consistent CLI layout.
811+typedef SizeConstrainedString<15> Name;
812+typedef SizeConstrainedString<60> Usage;
813+typedef SizeConstrainedString<60> Description;
814+
815+/// @brief Flag models an input parameter to a command.
816+class BIOMETRY_DLL_PUBLIC Flag : public DoNotCopyOrMove
817+{
818+public:
819+ // Safe us some typing.
820+ typedef std::shared_ptr<Flag> Ptr;
821+
822+ /// @brief notify announces a new value to the flag.
823+ virtual void notify(const std::string& value) = 0;
824+ /// @brief name returns the name of the Flag.
825+ const Name& name() const;
826+ /// @brief description returns a human-readable description of the flag.
827+ const Description& description() const;
828+
829+protected:
830+ /// @brief Flag creates a new instance, initializing name and description
831+ /// from the given values.
832+ Flag(const Name& name, const Description& description);
833+
834+private:
835+ Name name_;
836+ Description description_;
837+};
838+
839+/// @brief TypedFlag implements Flag relying on operator<< and operator>> to read/write values to/from strings.
840+template<typename T>
841+class BIOMETRY_DLL_PUBLIC TypedFlag : public Flag
842+{
843+public:
844+ typedef std::shared_ptr<TypedFlag<T>> Ptr;
845+
846+ TypedFlag(const Name& name, const Description& description) : Flag{name, description}
847+ {
848+ }
849+
850+ /// @brief value installs the given value in the flag.
851+ TypedFlag& value(const T& value)
852+ {
853+ value_ = value;
854+ return *this;
855+ }
856+
857+ /// @brief value returns the optional value associated with the flag.
858+ const Optional<T>& value() const
859+ {
860+ return value_;
861+ }
862+
863+ /// @brief notify tries to unwrap a value of type T from value.
864+ void notify(const std::string& s) override
865+ {
866+ std::stringstream ss{s};
867+ T value; ss >> value;
868+ value_ = value;
869+ }
870+
871+private:
872+ Optional<T> value_;
873+};
874+
875+/// @brief TypedReferenceFlag implements Flag, relying on operator<</>> to convert to/from string representations,
876+/// updating the given mutable reference to a value of type T.
877+template<typename T>
878+class BIOMETRY_DLL_PUBLIC TypedReferenceFlag : public Flag
879+{
880+public:
881+ // Safe us some typing.
882+ typedef std::shared_ptr<TypedReferenceFlag<T>> Ptr;
883+
884+ /// @brief TypedReferenceFlag initializes a new instance with name, description and value.
885+ TypedReferenceFlag(const Name& name, const Description& description, T& value)
886+ : Flag{name, description},
887+ value_{value}
888+ {
889+ }
890+
891+ /// @brief notify tries to unwrap a value of type T from value,
892+ /// relying on operator>> to read from given string s.
893+ void notify(const std::string& s) override
894+ {
895+ std::stringstream ss{s};
896+ ss >> value_.get();
897+ }
898+
899+private:
900+ std::reference_wrapper<T> value_;
901+};
902+
903+/// @brief OptionalTypedReferenceFlag handles Optional<T> references, making sure that
904+/// a value is always read on notify, even if the Optional<T> wasn't initialized previously.
905+template<typename T>
906+class BIOMETRY_DLL_PUBLIC OptionalTypedReferenceFlag : public Flag
907+{
908+public:
909+ typedef std::shared_ptr<OptionalTypedReferenceFlag<T>> Ptr;
910+
911+ OptionalTypedReferenceFlag(const Name& name, const Description& description, Optional<T>& value)
912+ : Flag{name, description},
913+ value_{value}
914+ {
915+ }
916+
917+ /// @brief notify tries to unwrap a value of type T from value.
918+ void notify(const std::string& s) override
919+ {
920+ std::stringstream ss{s}; T value; ss >> value;
921+ value_.get() = value;
922+ }
923+
924+private:
925+ std::reference_wrapper<Optional<T>> value_;
926+};
927+
928 /// @brief Command abstracts an individual command available from the daemon.
929 class BIOMETRY_DLL_PUBLIC Command : public DoNotCopyOrMove
930 {
931 public:
932 // Safe us some typing
933- typedef std::shared_ptr<Command> Ptr;
934-
935- // We are imposing size constraints to ensure a consistent CLI layout.
936- typedef SizeConstrainedString<15> Name;
937- typedef SizeConstrainedString<60> Usage;
938- typedef SizeConstrainedString<60> Description;
939-
940- /// @brief Flag models an input parameter to a command.
941- class Flag : public DoNotCopyOrMove
942- {
943- public:
944- // Safe us some typing.
945- typedef std::shared_ptr<Flag> Ptr;
946-
947- /// @brief notify announces a new value to the flag.
948- virtual void notify(const std::string& value) = 0;
949- /// @brief name returns the name of the Flag.
950- const Name& name() const;
951- /// @brief description returns a human-readable description of the flag.
952- const Description& description() const;
953-
954- protected:
955- /// @brief Flag creates a new instance, initializing name and description
956- /// from the given values.
957- Flag(const Name& name, const Description& description);
958-
959- private:
960- Name name_;
961- Description description_;
962- };
963-
964- /// @brief Info bundles details about a command.
965- struct Info
966- {
967- Name name; ///< The name of the command.
968- Usage usage; ///< Short usage description of the command.
969- Description description; /// More detailed description of the command.
970- std::vector<Flag::Ptr> flags; /// Flags known to the command.
971- };
972-
973- template<typename T>
974- class TypedFlag : public Flag
975- {
976- public:
977- typedef std::shared_ptr<TypedFlag<T>> Ptr;
978-
979- TypedFlag(const Name& name, const Description& description) : Flag{name, description}
980- {
981- }
982-
983- /// @brief value installs the given value in the flag.
984- TypedFlag& value(const T& value)
985- {
986- value_ = value;
987- return *this;
988- }
989-
990- /// @brief value returns the optional value associated with the flag.
991- const Optional<T>& value() const
992- {
993- return value_;
994- }
995-
996- /// @brief notify tries to unwrap a value of type T from value.
997- void notify(const std::string& s) override
998- {
999- std::stringstream ss{s};
1000- T value; ss >> value;
1001- value_ = value;
1002- }
1003-
1004- private:
1005- Optional<T> value_;
1006- };
1007-
1008- template<typename T>
1009- class TypedReferenceFlag : public Flag
1010- {
1011- public:
1012- typedef std::shared_ptr<TypedReferenceFlag<T>> Ptr;
1013-
1014- TypedReferenceFlag(const Name& name, const Description& description, T& value)
1015- : Flag{name, description},
1016- value_{value}
1017- {
1018- }
1019-
1020- /// @brief notify tries to unwrap a value of type T from value.
1021- void notify(const std::string& s) override
1022- {
1023- std::stringstream ss{s};
1024- ss >> value_.get();
1025- }
1026-
1027- private:
1028- std::reference_wrapper<T> value_;
1029- };
1030-
1031- /// @brief OptionalTypedReferenceFlag handles Optional<T> references, making sure that
1032- /// a value is always read on notify, even if the Optional<T> wasn't initialized previously.
1033- template<typename T>
1034- class OptionalTypedReferenceFlag : public Flag
1035- {
1036- public:
1037- typedef std::shared_ptr<OptionalTypedReferenceFlag<T>> Ptr;
1038-
1039- OptionalTypedReferenceFlag(const Name& name, const Description& description, Optional<T>& value)
1040- : Flag{name, description},
1041- value_{value}
1042- {
1043- }
1044-
1045- /// @brief notify tries to unwrap a value of type T from value.
1046- void notify(const std::string& s) override
1047- {
1048- std::stringstream ss{s}; T value; ss >> value;
1049- value_.get() = value;
1050- }
1051-
1052- private:
1053- std::reference_wrapper<Optional<T>> value_;
1054- };
1055-
1056- /// @brief info returns Info about a command.
1057- virtual Info info() const;
1058+ typedef std::shared_ptr<Command> Ptr;
1059+
1060+ /// @brief Context bundles information passed to Command::run invocations.
1061+ struct Context
1062+ {
1063+ std::istream& cin; ///< The std::istream that should be used for reading.
1064+ std::ostream& cout; ///< The std::ostream that should be used for writing.
1065+ std::vector<std::string> args; ///< The command line args.
1066+ };
1067+
1068+ /// @brief name returns the Name of the command.
1069+ virtual Name name() const;
1070+
1071+ /// @brief usage returns a short usage string for the command.
1072+ virtual Usage usage() const;
1073+
1074+ /// @brief description returns a longer string explaining the command.
1075+ virtual Description description() const;
1076+
1077 /// @brief run puts the command to execution.
1078- virtual int run();
1079+ virtual int run(const Context& context) = 0;
1080+
1081+ /// @brief help prints information about a command to out.
1082+ virtual void help(std::ostream& out) = 0;
1083
1084 protected:
1085- /// @brief Command initializes a new instance with the given info and functor.
1086- Command(const Info& info, const std::function<int()>& run = std::function<int()>());
1087-
1088- /// @brief info returns a mutable reference to info_.
1089- Info& mutable_info();
1090-
1091- /// @brief run returns a mutable reference to run_.
1092- std::function<int()>& mutable_run();
1093-
1094-private:
1095- Info info_;
1096- std::function<int()> run_;
1097-};
1098-
1099-template<typename T>
1100-BIOMETRY_DLL_PUBLIC typename Command::TypedFlag<T>::Ptr make_flag(const Command::Name& name, const Command::Description& desc)
1101-{
1102- return std::make_shared<Command::TypedFlag<T>>(name, desc);
1103-}
1104-
1105-template<typename T>
1106-BIOMETRY_DLL_PUBLIC typename Command::TypedReferenceFlag<T>::Ptr make_flag(const Command::Name& name, const Command::Description& desc, T& value)
1107-{
1108- return std::make_shared<Command::TypedReferenceFlag<T>>(name, desc, value);
1109-}
1110-
1111-template<typename T>
1112-BIOMETRY_DLL_PUBLIC typename Command::OptionalTypedReferenceFlag<T>::Ptr make_flag(const Command::Name& name, const Command::Description& desc, Optional<T>& value)
1113-{
1114- return std::make_shared<Command::OptionalTypedReferenceFlag<T>>(name, desc, value);
1115+ /// @brief Command initializes a new instance with the given name, usage and description.
1116+ Command(const Name& name, const Usage& usage, const Description& description);
1117+
1118+ /// @brief name adjusts the name of the command to n.
1119+ // virtual void name(const Name& n);
1120+ /// @brief usage adjusts the usage string of the comand to u.
1121+ // virtual void usage(const Usage& u);
1122+ /// @brief description adjusts the description string of the command to d.
1123+ // virtual void description(const Description& d);
1124+
1125+private:
1126+ Name name_;
1127+ Usage usage_;
1128+ Description description_;
1129+};
1130+
1131+/// @brief CommandWithSubcommands implements Command, selecting one of a set of actions.
1132+class BIOMETRY_DLL_PUBLIC CommandWithSubcommands : public Command
1133+{
1134+public:
1135+ typedef std::shared_ptr<CommandWithSubcommands> Ptr;
1136+ typedef std::function<int(const Context&)> Action;
1137+
1138+ /// @brief CommandWithSubcommands initializes a new instance with the given name, usage and description
1139+ CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description);
1140+
1141+ /// @brief command adds the given command to the set of known commands.
1142+ CommandWithSubcommands& command(const Command::Ptr& command);
1143+
1144+ /// @brief flag adds the given flag to the set of known flags.
1145+ CommandWithSubcommands& flag(const Flag::Ptr& flag);
1146+
1147+ // From Command
1148+ int run(const Context& context) override;
1149+ void help(std::ostream &out) override;
1150+
1151+private:
1152+ std::unordered_map<std::string, Command::Ptr> commands_;
1153+ std::set<Flag::Ptr> flags_;
1154+};
1155+
1156+/// @brief CommandWithFlagsAction implements Command, executing an Action after handling
1157+class BIOMETRY_DLL_PUBLIC CommandWithFlagsAndAction : public Command
1158+{
1159+public:
1160+ typedef std::shared_ptr<CommandWithFlagsAndAction> Ptr;
1161+ typedef std::function<int(const Context&)> Action;
1162+
1163+ /// @brief CommandWithFlagsAndAction initializes a new instance with the given name, usage and description
1164+ CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description);
1165+
1166+ /// @brief flag adds the given flag to the set of known flags.
1167+ CommandWithFlagsAndAction& flag(const Flag::Ptr& flag);
1168+
1169+ /// @brief action installs the given action.
1170+ CommandWithFlagsAndAction& action(const Action& action);
1171+
1172+ // From Command
1173+ int run(const Context& context) override;
1174+ void help(std::ostream &out) override;
1175+
1176+private:
1177+ std::set<Flag::Ptr> flags_;
1178+ Action action_;
1179+};
1180+
1181+namespace cmd
1182+{
1183+/// @brief HelpFor prints a help message for the given command on execution.
1184+class Help : public Command
1185+{
1186+public:
1187+ /// @brief HelpFor initializes a new instance with the given reference to a cmd.
1188+ explicit Help(Command& cmd);
1189+
1190+ // From Command
1191+ int run(const Context &context) override;
1192+ void help(std::ostream &out) override;
1193+
1194+private:
1195+ /// @cond
1196+ Command& command;
1197+ /// @endcond
1198+};
1199+}
1200+
1201+/// @brief args returns a vector of strings assembled from argc and argv.
1202+BIOMETRY_DLL_PUBLIC std::vector<std::string> args(int argc, char** argv);
1203+
1204+/// @brief make_flag returns a flag with the given name and description.
1205+template<typename T>
1206+BIOMETRY_DLL_PUBLIC typename TypedFlag<T>::Ptr make_flag(const Name& name, const Description& description)
1207+{
1208+ return std::make_shared<TypedFlag<T>>(name, description);
1209+}
1210+
1211+/// @brief make_flag returns a flag with the given name and description, notifying updates to value.
1212+template<typename T>
1213+BIOMETRY_DLL_PUBLIC typename TypedReferenceFlag<T>::Ptr make_flag(const Name& name, const Description& desc, T& value)
1214+{
1215+ return std::make_shared<TypedReferenceFlag<T>>(name, desc, value);
1216+}
1217+
1218+/// @brief make_flag returns a flag with the given name and description, updating the given optional value.
1219+template<typename T>
1220+BIOMETRY_DLL_PUBLIC typename OptionalTypedReferenceFlag<T>::Ptr make_flag(const Name& name, const Description& desc, Optional<T>& value)
1221+{
1222+ return std::make_shared<OptionalTypedReferenceFlag<T>>(name, desc, value);
1223 }
1224 }
1225 }
1226
1227=== modified file 'tests/test_daemon.cpp'
1228--- tests/test_daemon.cpp 2016-05-09 14:37:52 +0000
1229+++ tests/test_daemon.cpp 2016-05-10 11:29:00 +0000
1230@@ -65,18 +65,18 @@
1231
1232 TEST(TypedFlag, stores_name_and_desc_passed_on_construction)
1233 {
1234- cli::Command::Name name{"42"};
1235- cli::Command::Description desc{"43"};
1236- cli::Command::TypedFlag<int> flag{name, desc};
1237+ cli::Name name{"42"};
1238+ cli::Description desc{"43"};
1239+ cli::TypedFlag<int> flag{name, desc};
1240 EXPECT_EQ(name, flag.name());
1241 EXPECT_EQ(desc, flag.description());
1242 }
1243
1244 TEST(TypedFlag, parses_string_on_notify_and_sets_value)
1245 {
1246- cli::Command::Name name{"42"};
1247- cli::Command::Description desc{"43"};
1248- cli::Command::TypedFlag<int> flag{name, desc};
1249+ cli::Name name{"42"};
1250+ cli::Description desc{"43"};
1251+ cli::TypedFlag<int> flag{name, desc};
1252 EXPECT_FALSE(flag.value().is_initialized());
1253 flag.notify("42");
1254 EXPECT_TRUE(flag.value().is_initialized());
1255@@ -153,7 +153,7 @@
1256 biometry::Daemon daemon;
1257 auto rc = daemon.run(
1258 {
1259- "run", "--config", "test.json"
1260+ "run", "--config=test.json"
1261 });
1262
1263 return rc == EXIT_SUCCESS ? core::posix::exit::Status::success : core::posix::exit::Status::failure;

Subscribers

People subscribed via source and target branches