Improved formatting of command-line output
This commit is contained in:
parent
5c0fe24fae
commit
9e9a432f70
|
@ -100,6 +100,8 @@ set(SOURCE_FILES
|
||||||
src/audioInput/WaveFileReader.cpp
|
src/audioInput/WaveFileReader.cpp
|
||||||
src/audioInput/waveFileWriting.cpp
|
src/audioInput/waveFileWriting.cpp
|
||||||
src/stringTools.cpp
|
src/stringTools.cpp
|
||||||
|
src/NiceCmdLineOutput.cpp
|
||||||
|
src/TablePrinter.cpp
|
||||||
)
|
)
|
||||||
add_executable(rhubarb ${SOURCE_FILES})
|
add_executable(rhubarb ${SOURCE_FILES})
|
||||||
target_link_libraries(rhubarb ${Boost_LIBRARIES} cppFormat sphinxbase pocketSphinx)
|
target_link_libraries(rhubarb ${Boost_LIBRARIES} cppFormat sphinxbase pocketSphinx)
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
#include "regex"
|
||||||
|
#include "NiceCmdLineOutput.h"
|
||||||
|
#include "platformTools.h"
|
||||||
|
#include "TablePrinter.h"
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using TCLAP::CmdLineInterface;
|
||||||
|
using std::cout;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
|
string getBinaryName() {
|
||||||
|
return getBinPath().filename().string();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiceCmdLineOutput::version(CmdLineInterface& cli) {
|
||||||
|
cout << endl << cli.getMessage() << " version " << cli.getVersion() << endl << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiceCmdLineOutput::usage(CmdLineInterface& cli) {
|
||||||
|
cout << endl << "Short usage:" << endl;
|
||||||
|
printShortUsage(cli, cout);
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
|
cout << "Long usage:" << endl << endl;
|
||||||
|
printLongUsage(cli, cout);
|
||||||
|
|
||||||
|
cout << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiceCmdLineOutput::failure(CmdLineInterface& cli, TCLAP::ArgException& e) {
|
||||||
|
std::cerr << "Invalid command-line arguments. " << e.argId() << endl;
|
||||||
|
std::cerr << e.error() << endl << endl;
|
||||||
|
|
||||||
|
if (cli.hasHelpAndVersion()) {
|
||||||
|
std::cerr << "Short usage:" << endl;
|
||||||
|
printShortUsage(cli, std::cerr);
|
||||||
|
|
||||||
|
std::cerr << endl << "For complete usage and help, type `" << getBinaryName() << " --help`" << endl << endl;
|
||||||
|
} else {
|
||||||
|
usage(cli);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiceCmdLineOutput::printShortUsage(CmdLineInterface& cli, std::ostream& outStream) const {
|
||||||
|
string shortUsage = getBinaryName() + " ";
|
||||||
|
|
||||||
|
// Print XOR arguments
|
||||||
|
TCLAP::XorHandler xorHandler = cli.getXorHandler();
|
||||||
|
const vector<vector<TCLAP::Arg*>> xorArgGroups = xorHandler.getXorList();
|
||||||
|
for (const vector<TCLAP::Arg*>& xorArgGroup : xorArgGroups) {
|
||||||
|
shortUsage += " {";
|
||||||
|
|
||||||
|
for (auto arg : xorArgGroup) shortUsage += arg->shortID() + "|";
|
||||||
|
shortUsage.pop_back();
|
||||||
|
|
||||||
|
shortUsage += '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print regular arguments
|
||||||
|
std::list<TCLAP::Arg*> argList = cli.getArgList();
|
||||||
|
for (auto arg : argList) {
|
||||||
|
if (xorHandler.contains(arg)) continue;
|
||||||
|
|
||||||
|
shortUsage += " " + arg->shortID();
|
||||||
|
}
|
||||||
|
|
||||||
|
outStream << shortUsage << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
string wrapLongID(const string& s) {
|
||||||
|
return std::regex_replace(s, std::regex(", "), ",\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiceCmdLineOutput::printLongUsage(CmdLineInterface& cli, std::ostream& outStream) const {
|
||||||
|
TablePrinter tablePrinter(&outStream, { 20, 56 });
|
||||||
|
|
||||||
|
// Print XOR arguments
|
||||||
|
TCLAP::XorHandler xorHandler = cli.getXorHandler();
|
||||||
|
const vector<vector<TCLAP::Arg*>> xorArgGroups = xorHandler.getXorList();
|
||||||
|
for (const vector<TCLAP::Arg*>& xorArgGroup : xorArgGroups) {
|
||||||
|
for (auto arg : xorArgGroup) {
|
||||||
|
if (arg != xorArgGroup[0])
|
||||||
|
outStream << "-- or --" << endl;
|
||||||
|
|
||||||
|
tablePrinter.printRow({ wrapLongID(arg->longID()), arg->getDescription() });
|
||||||
|
}
|
||||||
|
outStream << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print regular arguments
|
||||||
|
std::list<TCLAP::Arg*> argList = cli.getArgList();
|
||||||
|
for (auto arg : argList) {
|
||||||
|
if (xorHandler.contains(arg)) continue;
|
||||||
|
|
||||||
|
tablePrinter.printRow({ wrapLongID(arg->longID()), arg->getDescription() });
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef RHUBARB_LIP_SYNC_NICECMDLINEOUTPUT_H
|
||||||
|
#define RHUBARB_LIP_SYNC_NICECMDLINEOUTPUT_H
|
||||||
|
|
||||||
|
#include <tclap/StdOutput.h>
|
||||||
|
|
||||||
|
class NiceCmdLineOutput : public TCLAP::CmdLineOutput {
|
||||||
|
public:
|
||||||
|
void usage(TCLAP::CmdLineInterface& cli) override;
|
||||||
|
void version(TCLAP::CmdLineInterface& cli) override;
|
||||||
|
void failure(TCLAP::CmdLineInterface& cli, TCLAP::ArgException& e) override;
|
||||||
|
private:
|
||||||
|
// Writes a brief usage message with short args.
|
||||||
|
void printShortUsage(TCLAP::CmdLineInterface& cli, std::ostream& outStream) const;
|
||||||
|
|
||||||
|
// Writes a longer usage message with long and short args, providing descriptions
|
||||||
|
void printLongUsage(TCLAP::CmdLineInterface& cli, std::ostream& outStream) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //RHUBARB_LIP_SYNC_NICECMDLINEOUTPUT_H
|
|
@ -0,0 +1,63 @@
|
||||||
|
#include "TablePrinter.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <boost/io/ios_state.hpp>
|
||||||
|
#include "stringTools.h"
|
||||||
|
|
||||||
|
using std::ostream;
|
||||||
|
using std::initializer_list;
|
||||||
|
using std::invalid_argument;
|
||||||
|
using std::vector;
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
TablePrinter::TablePrinter(ostream *stream, initializer_list<int> columnWidths, int columnSpacing) :
|
||||||
|
stream(stream),
|
||||||
|
columnWidths(columnWidths.begin(), columnWidths.end()),
|
||||||
|
columnSpacing(columnSpacing)
|
||||||
|
{
|
||||||
|
if (stream == nullptr) throw invalid_argument("stream is null.");
|
||||||
|
if (columnWidths.size() < 1) throw invalid_argument("No columns defined.");
|
||||||
|
if (std::any_of(columnWidths.begin(), columnWidths.end(), [](int width){ return width <= 1; })) {
|
||||||
|
throw invalid_argument("All columns must have a width of at least 1.");
|
||||||
|
}
|
||||||
|
if (columnSpacing < 0) throw invalid_argument("columnSpacing must not be negative.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TablePrinter::printRow(initializer_list<string> columns) const {
|
||||||
|
if (columns.size() != columnWidths.size()) throw invalid_argument("Number of specified strings does not match number of defined columns.");
|
||||||
|
|
||||||
|
// Some cells may span multiple lines.
|
||||||
|
// Create matrix of text lines in columns.
|
||||||
|
vector<vector<string>> strings(columns.size());
|
||||||
|
size_t lineCount = 0;
|
||||||
|
{
|
||||||
|
int columnIndex = 0;
|
||||||
|
for (const string& column : columns) {
|
||||||
|
vector<string> lines = wrapString(column, columnWidths[columnIndex]);
|
||||||
|
if (lines.size() > lineCount) lineCount = lines.size();
|
||||||
|
strings[columnIndex] = move(lines);
|
||||||
|
|
||||||
|
columnIndex++;
|
||||||
|
}
|
||||||
|
// Make sure the matrix is uniform
|
||||||
|
for (vector<string>& columnRows : strings) {
|
||||||
|
columnRows.resize(lineCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save stream flags, restore them at end of scope
|
||||||
|
boost::io::ios_flags_saver ifs(*stream);
|
||||||
|
|
||||||
|
// Print lines
|
||||||
|
*stream << std::left;
|
||||||
|
string spacer(columnSpacing, ' ');
|
||||||
|
for (size_t rowIndex = 0; rowIndex < lineCount; rowIndex++) {
|
||||||
|
for (size_t columnIndex = 0; columnIndex < columns.size(); columnIndex++) {
|
||||||
|
if (columnIndex != 0) {
|
||||||
|
*stream << spacer;
|
||||||
|
}
|
||||||
|
*stream << std::setw(columnWidths[columnIndex]) << strings[columnIndex][rowIndex];
|
||||||
|
}
|
||||||
|
*stream << std::endl;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef RHUBARB_LIP_SYNC_TABLEPRINTER_H
|
||||||
|
#define RHUBARB_LIP_SYNC_TABLEPRINTER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <ostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class TablePrinter {
|
||||||
|
public:
|
||||||
|
TablePrinter(std::ostream* stream, std::initializer_list<int> columnWidths, int columnSpacing = 2);
|
||||||
|
void printRow(std::initializer_list<std::string> columns) const;
|
||||||
|
private:
|
||||||
|
std::ostream* const stream;
|
||||||
|
const std::vector<int> columnWidths;
|
||||||
|
const int columnSpacing;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //RHUBARB_LIP_SYNC_TABLEPRINTER_H
|
13
src/main.cpp
13
src/main.cpp
|
@ -8,6 +8,7 @@
|
||||||
#include "mouthAnimation.h"
|
#include "mouthAnimation.h"
|
||||||
#include "platformTools.h"
|
#include "platformTools.h"
|
||||||
#include "appInfo.h"
|
#include "appInfo.h"
|
||||||
|
#include "NiceCmdLineOutput.h"
|
||||||
|
|
||||||
using std::exception;
|
using std::exception;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
@ -69,13 +70,14 @@ ptree createXmlTree(const path& filePath, const map<centiseconds, Phone>& phones
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
try {
|
|
||||||
// Define command-line parameters
|
// Define command-line parameters
|
||||||
const char argumentValueSeparator = ' ';
|
const char argumentValueSeparator = ' ';
|
||||||
TCLAP::CmdLine cmd(appName, argumentValueSeparator, appVersion);
|
TCLAP::CmdLine cmd(appName, argumentValueSeparator, appVersion);
|
||||||
cmd.setExceptionHandling(false);
|
cmd.setExceptionHandling(false);
|
||||||
|
cmd.setOutput(new NiceCmdLineOutput());
|
||||||
TCLAP::UnlabeledValueArg<string> inputFileName("inputFile", "The input file. Must be a sound file in WAVE format.", true, "", "string", cmd);
|
TCLAP::UnlabeledValueArg<string> inputFileName("inputFile", "The input file. Must be a sound file in WAVE format.", true, "", "string", cmd);
|
||||||
|
|
||||||
|
try {
|
||||||
// Parse command line
|
// Parse command line
|
||||||
cmd.parse(argc, argv);
|
cmd.parse(argc, argv);
|
||||||
|
|
||||||
|
@ -93,10 +95,15 @@ int main(int argc, char *argv[]) {
|
||||||
boost::property_tree::write_xml(std::cout, xmlTree, boost::property_tree::xml_writer_settings<string>(' ', 2));
|
boost::property_tree::write_xml(std::cout, xmlTree, boost::property_tree::xml_writer_settings<string>(' ', 2));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const TCLAP::ArgException& e) {
|
} catch (TCLAP::ArgException& e) {
|
||||||
std::cerr << "Invalid command-line arguments regarding `" << e.argId() << "`. " << e.error();
|
// Error parsing command-line args.
|
||||||
|
cmd.getOutput()->failure(cmd, e);
|
||||||
return 1;
|
return 1;
|
||||||
|
} catch (TCLAP::ExitException& e) {
|
||||||
|
// A built-in TCLAP command (like --help) has finished. Exit application.
|
||||||
|
return 0;
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
|
// Generic error
|
||||||
std::cerr << "An error occurred. " << getMessage(e);
|
std::cerr << "An error occurred. " << getMessage(e);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue