Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Name argument if data conversion fails in std.getopt #10593

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
111 changes: 101 additions & 10 deletions std/getopt.d
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,23 @@ private template optionValidator(A...)
alias optionValidator = message;
}

private void handleConversion(R)(string option, string value, R* receiver,
size_t idx, string file = __FILE__, size_t line = __LINE__)
{
import std.conv : to, ConvException;
import std.format : format;
try
{
*receiver = to!(typeof(*receiver))(value);
}
catch (ConvException e)
{
throw new GetOptException(format("Argument '%s' at position '%u' could "
~ "not be converted to type '%s' as required by option '%s'.",
value, idx, R.stringof, option), e, file, line);
}
}

@safe pure unittest
{
alias P = void*;
Expand Down Expand Up @@ -864,7 +881,7 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
if (val.length)
{
// parse '--b=true/false'
*receiver = to!(typeof(*receiver))(val);
handleConversion(option, val, receiver, i);
}
else
{
Expand All @@ -888,15 +905,22 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
val = args[i];
args = args[0 .. i] ~ args[i + 1 .. $];
}
static if (is(typeof(*receiver) == enum))
static if (is(typeof(*receiver) == enum) ||
is(typeof(*receiver) == string))
{
*receiver = to!(typeof(*receiver))(val);
handleConversion(option, val, receiver, i);
}
else static if (is(typeof(*receiver) : real))
{
// numeric receiver
if (incremental) ++*receiver;
else *receiver = to!(typeof(*receiver))(val);
if (incremental)
{
++*receiver;
}
else
{
handleConversion(option, val, receiver, i);
}
}
else static if (is(typeof(*receiver) == string))
{
Expand Down Expand Up @@ -936,12 +960,18 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,

if (arraySep == "")
{
*receiver ~= to!E(val);
E tmp;
handleConversion(option, val, &tmp, i);
*receiver ~= tmp;
}
else
{
foreach (elem; val.splitter(arraySep).map!(a => to!E(a))())
*receiver ~= elem;
foreach (elem; val.splitter(arraySep))
{
E tmp;
handleConversion(option, elem, &tmp, i);
*receiver ~= tmp;
}
}
}
else static if (isAssociativeArray!(typeof(*receiver)))
Expand All @@ -961,7 +991,14 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
~ to!string(assignChar) ~ "' in argument '" ~ input ~ "'.");
auto key = input[0 .. j];
auto value = input[j + 1 .. $];
return tuple(to!K(key), to!V(value));

K k;
handleConversion("", key, &k, 0);

V v;
handleConversion("", value, &v, 0);

return tuple(k,v);
}

static void setHash(Range)(R receiver, Range range)
Expand Down Expand Up @@ -1477,7 +1514,7 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assertThrown!GetOptException(getopt(args, "abc", &abc));

args = ["prog", "--abc=string"];
assertThrown!ConvException(getopt(args, "abc", &abc));
assertThrown!GetOptException(getopt(args, "abc", &abc));
}

// https://issues.dlang.org/show_bug.cgi?id=7693
Expand Down Expand Up @@ -1946,3 +1983,57 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt, st
~ "information.\n";
assert(wanted == helpMsg);
}


@safe unittest
{
import std.string : indexOf;

enum UniqueIdentifer {
a,
b
}

UniqueIdentifer a;

auto args = ["prog", "--foo", "HELLO"];
try
{
auto t = getopt(args, "foo|f", &a);
assert(false, "Must not be reached, as \"HELLO\" cannot be converted"
~ " to enum A.");
}
catch (GetOptException e)
{
string str = () @trusted { return e.toString(); }();
assert(str.indexOf("HELLO") != -1);
assert(str.indexOf("UniqueIdentifer") != -1);
assert(str.indexOf("foo") != -1);
}
}

@safe unittest
{
import std.string : indexOf;

int a;

auto args = ["prog", "--foo", "HELLO"];
try
{
auto t = getopt(args, "foo|f", &a);
assert(false, "Must not be reached, as \"HELLO\" cannot be converted"
~ " to an int");
}
catch (GetOptException e)
{
string str = () @trusted { return e.toString(); }();
assert(str.indexOf("HELLO") != -1);
assert(str.indexOf("int") != -1);
assert(str.indexOf("foo") != -1);
}

args = ["prog", "--foo", "1337"];
getopt(args, "foo|f", &a);
assert(a == 1337);
}
Loading