Complete implementation of read_cli()

This commit is contained in:
Alessandro Ranellucci 2017-03-18 00:48:24 +01:00
parent c9f0a4c934
commit 90395f9394
4 changed files with 154 additions and 51 deletions

View File

@ -106,7 +106,6 @@ bool unescape_string_cstyle(const std::string &str, std::string &str_out)
bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out)
{
out.clear();
if (str.empty())
return true;
@ -500,56 +499,109 @@ DynamicConfig::erase(const t_config_option_key &opt_key) {
this->options.erase(opt_key);
}
void
DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
{
std::vector<const char*> _argv;
// push a bogus executable name (argv[0])
_argv.push_back("");
for (size_t i = 0; i < tokens.size(); ++i)
_argv.push_back(const_cast<const char*>(tokens[i].c_str()));
this->read_cli(_argv.size(), &_argv[0], extra);
}
void
DynamicConfig::read_cli(const int argc, const char** argv, t_config_option_keys* extra)
{
// cache the CLI option => opt_key mapping
std::map<std::string,std::string> opts;
for (const auto &oit : this->def->options) {
std::string cli = oit.second.cli;
cli = cli.substr(0, cli.find("="));
boost::trim_right_if(cli, boost::is_any_of("!"));
std::vector<std::string> tokens;
boost::split(tokens, cli, boost::is_any_of("|"));
for (const std::string &t : tokens)
opts[t] = oit.first;
}
bool parse_options = true;
for (int i = 1; i < argc; ++i) {
std::string token = argv[i];
// Store non-option arguments in the provided vector.
if (!parse_options || !boost::starts_with(token, "-")) {
extra->push_back(token);
continue;
}
// Stop parsing tokens as options when -- is supplied.
if (token == "--") {
// stop parsing tokens as options
parse_options = false;
} else if (parse_options && boost::starts_with(token, "-")) {
boost::algorithm::trim_left_if(token, boost::algorithm::is_any_of("-"));
// TODO: handle --key=value
continue;
}
// look for the option def
t_config_option_key opt_key;
const ConfigOptionDef* optdef;
for (t_optiondef_map::const_iterator oit = this->def->options.begin();
oit != this->def->options.end(); ++oit) {
optdef = &oit->second;
// Remove leading dashes
boost::trim_left_if(token, boost::is_any_of("-"));
if (optdef->cli == token
|| optdef->cli == token + '!'
|| boost::starts_with(optdef->cli, token + "=")
|| boost::starts_with(optdef->cli, token + "|")
|| (token.length() == 1 && boost::contains(optdef->cli, "|" + token))) {
opt_key = oit->first;
break;
}
// Remove the "no-" prefix used to negate boolean options.
bool no = false;
if (boost::starts_with(token, "no-")) {
no = true;
boost::replace_first(token, "no-", "");
}
// Read value when supplied in the --key=value form.
std::string value;
{
size_t equals_pos = token.find("=");
if (equals_pos != std::string::npos) {
value = token.substr(equals_pos+1);
token.erase(equals_pos);
}
}
if (opt_key.empty()) {
printf("Warning: unknown option --%s\n", token.c_str());
// Look for the cli -> option mapping.
const auto it = opts.find(token);
if (it == opts.end()) {
printf("Warning: unknown option --%s\n", token.c_str());
continue;
}
const t_config_option_key opt_key = it->second;
const ConfigOptionDef &optdef = this->def->options.at(opt_key);
// If the option type expects a value and it was not already provided,
// look for it in the next token.
if (optdef.type != coBool && optdef.type != coBools && value.empty()) {
if (i == (argc-1)) {
printf("No value supplied for --%s\n", token.c_str());
continue;
}
value = argv[++i];
}
if (ConfigOptionBool* opt = this->opt<ConfigOptionBool>(opt_key, true)) {
opt->value = !boost::starts_with(token, "no-");
} else if (ConfigOptionBools* opt = this->opt<ConfigOptionBools>(opt_key, true)) {
opt->values.push_back(!boost::starts_with(token, "no-"));
} else {
// we expect one more token carrying the value
if (i == (argc-1)) {
printf("No value supplied for --%s\n", token.c_str());
exit(1);
}
this->set_deserialize(opt_key, argv[++i], true);
}
// Store the option value.
const bool existing = this->has(opt_key);
if (ConfigOptionBool* opt = this->opt<ConfigOptionBool>(opt_key, true)) {
opt->value = !no;
} else if (ConfigOptionBools* opt = this->opt<ConfigOptionBools>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->values.push_back(!no);
} else if (ConfigOptionStrings* opt = this->opt<ConfigOptionStrings>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else if (ConfigOptionFloats* opt = this->opt<ConfigOptionFloats>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else if (ConfigOptionPoints* opt = this->opt<ConfigOptionPoints>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else {
extra->push_back(token);
this->set_deserialize(opt_key, value, true);
}
}
}

View File

@ -12,6 +12,9 @@
#include <vector>
#include "libslic3r.h"
#include "Point.hpp"
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/lexical_cast.hpp>
namespace Slic3r {
@ -248,6 +251,7 @@ class ConfigOptionStrings : public ConfigOptionVector<std::string>
};
bool deserialize(std::string str, bool append = false) {
if (!append) this->values.clear();
return unescape_strings_cstyle(str, this->values);
};
};
@ -392,20 +396,23 @@ class ConfigOptionPoints : public ConfigOptionVector<Pointf>
bool deserialize(std::string str, bool append = false) {
if (!append) this->values.clear();
std::istringstream is(str);
std::string point_str;
while (std::getline(is, point_str, ',')) {
Pointf point;
std::istringstream iss(point_str);
std::string coord_str;
if (std::getline(iss, coord_str, 'x')) {
std::istringstream(coord_str) >> point.x;
if (std::getline(iss, coord_str, 'x')) {
std::istringstream(coord_str) >> point.y;
}
std::vector<std::string> tokens;
boost::split(tokens, str, boost::is_any_of("x,"));
if (tokens.size() % 2) return false;
try {
for (size_t i = 0; i < tokens.size(); ++i) {
Pointf point;
point.x = boost::lexical_cast<coordf_t>(tokens[i]);
point.y = boost::lexical_cast<coordf_t>(tokens[++i]);
this->values.push_back(point);
}
this->values.push_back(point);
} catch (boost::bad_lexical_cast &e) {
printf("%s\n", e.what());
return false;
}
return true;
};
};
@ -691,6 +698,7 @@ class DynamicConfig : public virtual ConfigBase
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false);
t_config_option_keys keys() const;
void erase(const t_config_option_key &opt_key);
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra);
void read_cli(const int argc, const char **argv, t_config_option_keys* extra);
private:

View File

@ -4,7 +4,7 @@ use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 147;
use Test::More tests => 159;
use Data::Dumper;
foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintConfig) {
@ -251,4 +251,45 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
ok 1, 'did not crash on reading invalid items in config';
}
{
my $parse = sub {
my @argv = @_;
my $config = Slic3r::Config->new;
$config->read_cli(\@argv);
return $config;
};
{
my $config = $parse->(qw(--extra-perimeters --perimeters 1 --layer-height 0.45
--fill-density 70% --detect-bridging-perimeters --notes=foobar));
is $config->get('extra_perimeters'), 1, 'read_cli(): bool';
is $config->get('perimeters'), 1, 'read_cli(): int';
is $config->get('layer_height'), 0.45, 'read_cli(): float';
is $config->serialize('fill_density'), '70%', 'read_cli(): percent';
is $config->get('overhangs'), 1, 'read_cli(): alternative';
is $config->get('notes'), 'foobar', 'read_cli(): key=val';
}
{
my $config = $parse->(qw(--extra-perimeters --no-extra-perimeters));
ok $config->has('extra_perimeters'), 'read_cli(): negated bool';
is_deeply $config->get('extra_perimeters'), 0, 'read_cli(): negated bool';
}
{
my $config = $parse->(qw(--wipe --no-wipe --wipe));
is_deeply $config->get('wipe'), [1,0,1], 'read_cli(): bools array';
}
{
my $config = $parse->(qw(--post-process foo --post-process bar));
is_deeply $config->get('post_process'), ['foo', 'bar'], 'read_cli(): strings array';
}
{
my $config = $parse->(qw(--retract-speed 0.4 --retract-speed 0.5));
is_deeply $config->get('retract_speed'), [0.4, 0.5], 'read_cli(): floats array';
}
{
my $config = $parse->(qw(--extruder-offset 0,0 --extruder-offset 10x5));
is_deeply [ map $_->pp, @{$config->get('extruder_offset')} ],
[[0,0], [10,5]], 'read_cli(): points array';
}
}
__END__

View File

@ -40,6 +40,8 @@
double min_object_distance();
%name{_load} void load(std::string file);
%name{_save} void save(std::string file);
std::vector<std::string> read_cli(std::vector<std::string> _argv)
%code{% THIS->read_cli(_argv, &RETVAL); %};
};
%name{Slic3r::Config::Static} class StaticPrintConfig {