blob: 56d7c453e604acb6eb09ed07a95ef7f4ce960d1b [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lorgnette/cli/scan_options.h"
#include <cstdlib>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <base/containers/contains.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include "lorgnette/cli/commands.h"
namespace {
std::optional<bool> ParseBoolVal(const std::string& value) {
if (value == "1" || value == "true" || value == "yes") {
return true;
}
if (value == "0" || value == "false" || value == "no") {
return false;
}
return std::nullopt;
}
std::optional<std::vector<int>> ParseIntVal(const std::string& value) {
std::vector<int> result;
for (const auto& s : base::SplitString(value, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
char* end;
int i = strtol(s.c_str(), &end, 10);
if (!end || *end != '\0') {
return std::nullopt;
}
result.push_back(i);
}
return result;
}
std::optional<std::vector<double>> ParseFloatVal(const std::string& value) {
std::vector<double> result;
for (const auto& s : base::SplitString(value, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
char* end;
double d = strtod(s.c_str(), &end);
if (!end || *end != '\0') {
return std::nullopt;
}
result.push_back(d);
}
return result;
}
} // namespace
namespace lorgnette::cli {
base::StringPairs GetScanOptions(const std::vector<std::string>& args) {
base::StringPairs options;
bool parse_options = false;
for (const std::string& arg : args) {
if (base::Contains(kCommandMap, arg) &&
kCommandMap.at(arg) == Command::kSetOptions) {
parse_options = true;
continue;
}
if (!parse_options) {
continue;
}
size_t eq = arg.find("=", 1);
if (eq == std::string::npos) {
// Stop parsing at the first argument that isn't a valid option setting.
break;
}
if (base::StartsWith(arg, "-")) {
// Count -arg as the end of settings as well.
break;
}
options.emplace_back(
base::TrimWhitespaceASCII(arg.substr(0, eq), base::TRIM_ALL),
base::TrimWhitespaceASCII(arg.substr(eq + 1), base::TRIM_ALL));
}
return options;
}
std::optional<lorgnette::SetOptionsRequest> MakeSetOptionsRequest(
const lorgnette::ScannerConfig& config, const base::StringPairs& options) {
lorgnette::SetOptionsRequest request;
for (const auto& [option, raw_value] : options) {
if (!config.options().contains(option)) {
std::cerr << "Option " << option << " not found" << std::endl;
return std::nullopt;
}
lorgnette::ScannerOption* setting = request.add_options();
setting->set_name(option);
switch (config.options().at(option).option_type()) {
case lorgnette::TYPE_BOOL: {
std::optional<bool> value = ParseBoolVal(raw_value);
if (!value.has_value()) {
std::cerr << "Unable to parse \"" << raw_value << "\" as boolean for "
<< option << std::endl;
return std::nullopt;
}
setting->set_option_type(lorgnette::TYPE_BOOL);
setting->set_bool_value(value.value());
break;
}
case lorgnette::TYPE_INT: {
std::optional<std::vector<int>> value = ParseIntVal(raw_value);
if (!value.has_value()) {
std::cerr << "Unable to parse \"" << raw_value
<< "\" as int list for " << option << std::endl;
return std::nullopt;
}
setting->set_option_type(lorgnette::TYPE_INT);
for (int i : value.value()) {
setting->mutable_int_value()->add_value(i);
}
break;
}
case lorgnette::TYPE_FIXED: {
std::optional<std::vector<double>> value = ParseFloatVal(raw_value);
if (!value.has_value()) {
std::cerr << "Unable to parse \"" << raw_value
<< "\" as float list for " << option << std::endl;
return std::nullopt;
}
setting->set_option_type(lorgnette::TYPE_FIXED);
for (double d : value.value()) {
setting->mutable_fixed_value()->add_value(d);
}
break;
}
case lorgnette::TYPE_STRING:
setting->set_option_type(lorgnette::TYPE_STRING);
setting->set_string_value(raw_value);
break;
default:
std::cerr << "Option " << option << " cannot take a value" << std::endl;
return std::nullopt;
}
}
*request.mutable_scanner() = config.scanner();
return request;
}
} // namespace lorgnette::cli