Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions app/cli/args.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,96 @@
#include "args.h"

#include <iostream>
#include <set>
#include <stdexcept>

namespace minitts::cli {

namespace {

// Every option name the CLI looked for, split by whether the lookup consumes the following
// argument. Populated by the lookups below, so a new flag is recognised the moment its lookup
// is added and there is no second list to keep in sync.
std::set<std::string> & value_names() {
static std::set<std::string> names;
return names;
}

std::set<std::string> & flag_names() {
static std::set<std::string> names;
return names;
}

void record_value_query(const std::string & name) {
value_names().insert(name);
}

void record_flag_query(const std::string & name) {
flag_names().insert(name);
}

std::vector<std::string> args_nobody_asked_for(int argc, char ** argv) {
const auto & values = value_names();
const auto & flags = flag_names();
std::vector<std::string> unknown;
for (int i = 1; i < argc; ++i) {
const std::string token = argv[i];
if (token.rfind("--", 0) != 0 || token == "--") {
continue;
}
// Only an option that takes a value consumes the next argument, so only then can the
// next argument be something that merely looks like an option.
if (values.count(token) != 0) {
++i;
continue;
}
if (flags.count(token) != 0) {
continue;
}
unknown.push_back(token);
}
return unknown;
}

// Set once the strict check has spoken for this run, so the warning below does not repeat it.
bool strict_check_ran = false;

} // namespace

void require_known_args(int argc, char ** argv) {
strict_check_ran = true;
const auto unknown = args_nobody_asked_for(argc, argv);
if (unknown.empty()) {
return;
}
std::string message = unknown.size() > 1 ? "unknown options:" : "unknown option:";
for (const auto & token : unknown) {
message += " " + token;
}
throw std::runtime_error(message);
}

void warn_ignored_args(int argc, char ** argv) {
if (strict_check_ran) {
return;
}
const auto ignored = args_nobody_asked_for(argc, argv);
if (ignored.empty()) {
return;
}
std::cerr << "audiocpp_cli warning: ignored option";
if (ignored.size() > 1) {
std::cerr << "s";
}
std::cerr << ":";
for (const auto & token : ignored) {
std::cerr << " " << token;
}
std::cerr << "\n";
}

std::optional<std::string> find_arg(int argc, char ** argv, const std::string & name) {
record_value_query(name);
for (int i = 1; i + 1 < argc; ++i) {
if (argv[i] == name) {
return std::string(argv[i + 1]);
Expand All @@ -14,6 +100,7 @@ std::optional<std::string> find_arg(int argc, char ** argv, const std::string &
}

bool has_arg(int argc, char ** argv, const std::string & name) {
record_flag_query(name);
for (int i = 1; i < argc; ++i) {
if (argv[i] == name) {
return true;
Expand All @@ -23,6 +110,7 @@ bool has_arg(int argc, char ** argv, const std::string & name) {
}

std::vector<std::string> collect_args(int argc, char ** argv, const std::string & name) {
record_value_query(name);
std::vector<std::string> values;
for (int i = 1; i + 1 < argc; ++i) {
if (argv[i] == name) {
Expand Down
10 changes: 10 additions & 0 deletions app/cli/args.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@

namespace minitts::cli {

// Throws if the command line carries a --flag that no lookup ever asked for, so a typo like
// --bakend is refused instead of being silently discarded. Call once every option has been
// read, which on the run paths is before the model does any work.
void require_known_args(int argc, char ** argv);

// The same check reported as a warning, for the informational commands. Those return before the
// rest of the options are read, so all that can honestly be said there is that an option was
// ignored, not that it was misspelled. Does nothing if require_known_args already ran.
void warn_ignored_args(int argc, char ** argv);

std::optional<std::string> find_arg(int argc, char ** argv, const std::string & name);
bool has_arg(int argc, char ** argv, const std::string & name);
std::vector<std::string> collect_args(int argc, char ** argv, const std::string & name);
Expand Down
8 changes: 3 additions & 5 deletions app/cli/batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ bool has_batch_input(int argc, char ** argv) {
minitts::app::AppBatchRequest build_batch_request_from_cli(
int argc,
char ** argv,
const engine::runtime::TaskRequest & base_request) {
const engine::runtime::TaskRequest & base_request,
const std::string & audio_role) {
const auto request_sequence_path = optional_path_arg(argc, argv, "--request-sequence");
const auto batch_text_file = optional_path_arg(argc, argv, "--batch-text-file");
const auto batch_text_dir = optional_path_arg(argc, argv, "--batch-text-dir");
Expand Down Expand Up @@ -293,10 +294,7 @@ minitts::app::AppBatchRequest build_batch_request_from_cli(
base_request,
find_arg(argc, argv, "--language").value_or(""));
}
return build_audio_dir_batch(
*batch_audio_dir,
base_request,
find_arg(argc, argv, "--batch-audio-role").value_or("audio"));
return build_audio_dir_batch(*batch_audio_dir, base_request, audio_role);
}

} // namespace minitts::cli
3 changes: 2 additions & 1 deletion app/cli/batch.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ bool has_batch_input(int argc, char ** argv);
minitts::app::AppBatchRequest build_batch_request_from_cli(
int argc,
char ** argv,
const engine::runtime::TaskRequest & base_request);
const engine::runtime::TaskRequest & base_request,
const std::string & audio_role);

} // namespace minitts::cli
Loading
Loading