Skip to content

Commit 7735b46

Browse files
committed
Name argument if data conversion fails getopt
Fixes #9662
1 parent 8973596 commit 7735b46

File tree

1 file changed

+101
-10
lines changed

1 file changed

+101
-10
lines changed

std/getopt.d

+101-10
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,23 @@ private template optionValidator(A...)
610610
alias optionValidator = message;
611611
}
612612

613+
private void handleConversion(R)(string option, string value, R* receiver,
614+
size_t idx, string file = __FILE__, size_t line = __LINE__)
615+
{
616+
import std.conv : to, ConvException;
617+
import std.format : format;
618+
try
619+
{
620+
*receiver = to!(typeof(*receiver))(value);
621+
}
622+
catch(ConvException e)
623+
{
624+
throw new GetOptException(format("Argument '%s' at position '%u' could "
625+
~ "not be converted to type '%s' as required by option '%s'.",
626+
value, idx, R.stringof, option), e, file, line);
627+
}
628+
}
629+
613630
@safe pure unittest
614631
{
615632
alias P = void*;
@@ -864,7 +881,7 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
864881
if (val.length)
865882
{
866883
// parse '--b=true/false'
867-
*receiver = to!(typeof(*receiver))(val);
884+
handleConversion(option, val, receiver, i);
868885
}
869886
else
870887
{
@@ -888,15 +905,22 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
888905
val = args[i];
889906
args = args[0 .. i] ~ args[i + 1 .. $];
890907
}
891-
static if (is(typeof(*receiver) == enum))
908+
static if (is(typeof(*receiver) == enum) ||
909+
is(typeof(*receiver) == string))
892910
{
893-
*receiver = to!(typeof(*receiver))(val);
911+
handleConversion(option, val, receiver, i);
894912
}
895913
else static if (is(typeof(*receiver) : real))
896914
{
897915
// numeric receiver
898-
if (incremental) ++*receiver;
899-
else *receiver = to!(typeof(*receiver))(val);
916+
if (incremental)
917+
{
918+
++*receiver;
919+
}
920+
else
921+
{
922+
handleConversion(option, val, receiver, i);
923+
}
900924
}
901925
else static if (is(typeof(*receiver) == string))
902926
{
@@ -936,12 +960,18 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
936960

937961
if (arraySep == "")
938962
{
939-
*receiver ~= to!E(val);
963+
E tmp;
964+
handleConversion(option, val, &tmp, i);
965+
*receiver ~= tmp;
940966
}
941967
else
942968
{
943-
foreach (elem; val.splitter(arraySep).map!(a => to!E(a))())
944-
*receiver ~= elem;
969+
foreach (elem; val.splitter(arraySep))
970+
{
971+
E tmp;
972+
handleConversion(option, elem, &tmp, i);
973+
*receiver ~= tmp;
974+
}
945975
}
946976
}
947977
else static if (isAssociativeArray!(typeof(*receiver)))
@@ -961,7 +991,14 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
961991
~ to!string(assignChar) ~ "' in argument '" ~ input ~ "'.");
962992
auto key = input[0 .. j];
963993
auto value = input[j + 1 .. $];
964-
return tuple(to!K(key), to!V(value));
994+
995+
K k;
996+
handleConversion("", key, &k, 0);
997+
998+
V v;
999+
handleConversion("", value, &v, 0);
1000+
1001+
return tuple(k,v);
9651002
}
9661003

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

14791516
args = ["prog", "--abc=string"];
1480-
assertThrown!ConvException(getopt(args, "abc", &abc));
1517+
assertThrown!GetOptException(getopt(args, "abc", &abc));
14811518
}
14821519

14831520
// https://issues.dlang.org/show_bug.cgi?id=7693
@@ -1946,3 +1983,57 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt, st
19461983
~ "information.\n";
19471984
assert(wanted == helpMsg);
19481985
}
1986+
1987+
1988+
@safe unittest
1989+
{
1990+
import std.string : indexOf;
1991+
1992+
enum UniqueIdentifer {
1993+
a,
1994+
b
1995+
}
1996+
1997+
UniqueIdentifer a;
1998+
1999+
auto args = ["prog", "--foo", "HELLO"];
2000+
try
2001+
{
2002+
auto t = getopt(args, "foo|f", &a);
2003+
assert(false, "Must not be reached, as \"HELLO\" cannot be converted"
2004+
~ " to enum A.");
2005+
}
2006+
catch(GetOptException e)
2007+
{
2008+
string str = () @trusted { return e.toString(); }();
2009+
assert(str.indexOf("HELLO") != -1);
2010+
assert(str.indexOf("UniqueIdentifer") != -1);
2011+
assert(str.indexOf("foo") != -1);
2012+
}
2013+
}
2014+
2015+
@safe unittest
2016+
{
2017+
import std.string : indexOf;
2018+
2019+
int a;
2020+
2021+
auto args = ["prog", "--foo", "HELLO"];
2022+
try
2023+
{
2024+
auto t = getopt(args, "foo|f", &a);
2025+
assert(false, "Must not be reached, as \"HELLO\" cannot be converted"
2026+
~ " to an int");
2027+
}
2028+
catch(GetOptException e)
2029+
{
2030+
string str = () @trusted { return e.toString(); }();
2031+
assert(str.indexOf("HELLO") != -1);
2032+
assert(str.indexOf("int") != -1);
2033+
assert(str.indexOf("foo") != -1);
2034+
}
2035+
2036+
args = ["prog", "--foo", "1337"];
2037+
getopt(args, "foo|f", &a);
2038+
assert(a == 1337);
2039+
}

0 commit comments

Comments
 (0)