mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-17 05:55:58 +08:00
Complete implementation of read_cli()
This commit is contained in:
parent
c9f0a4c934
commit
90395f9394
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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__
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user