voile.parseopt

コマンドライン引数のパーサー
getoptと同じくコマンドラインの解析を行う。 getoptとの違いは、getoptは関数の引数に基づいて引数の解析を行うが、 本モジュールの parseOptions は、構造体の変数を引数に取ることで、構造体メンバ に関連付けられたUDAによりコマンドライン引数の解析を行う。
@help("Help messages for heading of application.\n")
struct Dat
{
  @help("Description of `value`")
  string value;

  string nonHelpedValue;

  @ignore
  string ignoredValue;

  @opt("a|aaaa") @help("Description of `i32value`")
  int i32value;

  @opt("f|ff")
  int f32value;
}
構造体(型)に付与できるUDAは以下の通り
  • help(str) : アプリケーションのヘルプメッセージ
  • caseSensitive : コマンドライン引数の大文字小文字を区別するかどうか。デフォルトは区別しない。
  • passThrough : 解釈されなかったコマンドライン引数を無視するか例外としてはじくか。デフォルトは例外としてはじく。
  • binding : 短いコマンドライン引数を、まとめて指定できるか(-abc-a -b -cと同じ解釈にさせるかどうか)
  • assignChar(str) : コマンドライン引数の「割り当て」に使用する文字を指定できる。(デフォルトは=-a=xxx--arg=xxx など。区切り文字を変更可能。)
  • arraySeparator(str) : コマンドライン引数の配列の区切り文字を指定できる。(デフォルトは,(カンマ)。-a=xxx,yyy,zzz,を変更可能。)
  • endOfOptions(str) : コマンドライン引数として解釈させる最後の文字を指定できる。(デフォルトは--で、-a=xxx -b=yyy -- -c=zzzでは、-a-bだけ解釈させる区切り文字として作用する。この区切り文字を変更可能。)
  • shortOpt(str) : 短いコマンドライン引数として解釈させるprefixを指定できる。(デフォルトは--aなどのように使われる。)
  • longOpt(str) : 短いコマンドライン引数として解釈させるprefixを指定できる。(デフォルトは----argなどのように使われる。)
構造体メンバーに付与できるUDAは以下の通り
  • help(str) : 引数のヘルプメッセージ
  • opt(str) : 引数。"a|args" とすることで、-a--argsを同時に定義可能。指定しなければオプションとして指定できない。
  • required : 引数を指定することを必須化する。デフォルトはfalse
  • convBy!fn : 指定した関数fnによって引数の値をメンバ変数の型に変換する。
  • ignore : 指定したメンバーをコマンドライン引数の解釈に使用しない。
コマンドライン引数の解釈の対象となるメンバーの型は以下の通り。※リストの上のものほど優先度が高い。
  • convByが指定されている引数
    • T function(string arg)
    • void function(ref T dst, string arg)
    • void function(out T dst, string arg)
    • void function(in S dat, ref T dst, string arg)
    • void function(in S dat, out T dst, string arg)
    • void function(ref S dat, string arg)
  • bool : 真偽値
  • string : 文字列型
  • T[] : 配列。std.conv.toにより、文字列がTに変換可能。
  • V[K] : 連想配列。std.conv.toにより文字列がV, Kに変換可能。
  • T : std.conv.toにより、文字列がTに変換可能。
  • void function(bool shortPrefix, string arg)
  • void function(string prefix, string arg)
  • void function(string arg)
  • void function(bool enabled)
  • void function()
pure nothrow @nogc @safe Help help(string str);
pure nothrow @nogc @safe CaseSensitive caseSensitive();
pure nothrow @nogc @safe PassThrough passThrough();
pure nothrow @nogc @safe Binding binding();
pure nothrow @nogc @safe AssignChar assignChar(dchar c = '=');
pure nothrow @nogc @safe ArraySeparator arraySeparator(dchar c = ',');
EndOfOptions endOfOptions(string str);
pure nothrow @nogc @safe Opt opt(string str);
pure nothrow @nogc @safe Req required();
pure nothrow @safe OptShort optShort(string[] str...);
pure nothrow @safe OptLong optLong(string[] str...);
template convBy(alias fn)
alias ignore = Ignore;
pure nothrow @nogc @safe TypeOption typeOption(string help = null, bool caseSensitive = false, bool passThrough = false, bool binding = false, dchar assignChar = '=', dchar arraySeparator = ',', string endOfOptions = "--", string[] shortOpt = ["-"], string[] longOpt = ["--"]);
pure nothrow @nogc @safe Option option(string opt, string help = null, bool required = false);
class ParseOptException: object.Exception;
struct HelpInformation;
string help;
bool wanted;
const pure nothrow @nogc T opCast(T)()
if(is(T == bool));
@safe HelpInformation parseOptions(T)(ref string[] args, ref T dat);
Examples:
import std.exception;
struct Dat
{
	@opt("a|arg")
	string value;
	
	@opt("b|arg2")
	int intValue;
}
Dat dat;
string[] args;
args = ["xxx", "-a=vvv"];
args.parseOptions(dat);
assert(dat.value == "vvv");

dat = Dat.init;
args = ["xxx", "-ac=vvv"];
args.parseOptions(dat);
assert(dat.value == "c=vvv");

dat = Dat.init;
args = ["xxx", "-a=vvv", "--arg2=123"];
args.parseOptions(dat);
assert(dat.intValue == 123);
Examples:
auto args = ["prog", "--foo", "-b"];

@help("Some information about the program.")
struct Dat
{
	@option("foo|f", "Some information about foo.")
	bool foo;
	
	@option("bar|b", "Some help message about bar.")
	bool bar;
}

Dat dat;
if (auto helpWanted = args.parseOptions(dat))
{
	import std.stdio;
	writeln(helpWanted.help);
	assert(0);
}

assert(dat.foo);
assert(dat.bar);
Examples:
import std.exception : assertThrown;
string[] args = ["program", "-a"];
@passThrough
struct Dat
{
	@(.opt("a"))
	bool opt;
}
Dat dat;
args.parseOptions(dat);
assert(dat.opt);

@caseSensitive
struct Dat2
{
	@help("help string")
	@(.opt("a"))
	bool opt;
}
Dat2 dat2;
args = ["program", "-a"];
args.parseOptions(dat2);
assert(dat2.opt);

struct Dat3
{
	@help("forgot to put a string")
	@(.opt("a"))
	bool opt;
}
Dat3 dat3;
args = ["program", ""];
assertThrown(
	args.parseOptions(dat3));
Examples: This behavior is different from Phobos' std.getopt.
import std.algorithm.searching : startsWith;
@arraySeparator(',')
struct Dat
{
	@opt("m")
	string[string] mapping;
}
Dat dat;
string[] args = ["testProgram", "-m", "a=b,c=\"d,e,f\""];
// getopt may thrown but parseOptions is not
args.parseOptions(dat);
assert("a" in dat.mapping);
assert("c" in dat.mapping);
assert("e" in dat.mapping);
assert("f\"" in dat.mapping);
assert(dat.mapping["a"] == "b");
assert(dat.mapping["c"] == "\"d");
assert(dat.mapping["e"] == null);
assert(dat.mapping["f\""] == null);
Examples:
import std.conv;

@arraySeparator(',')
struct Dat
{
	@opt("n|name")
	string[] names;
}
Dat dat;
auto args = ["program.name", "-nfoo,bar,baz"];
args.parseOptions(dat);
assert(dat.names == ["foo", "bar", "baz"], to!string(dat.names));

dat = Dat.init;
args = ["program.name", "-n", "foo,bar,baz"];
args.parseOptions(dat);
assert(dat.names == ["foo", "bar", "baz"], to!string(dat.names));

dat = Dat.init;
args = ["program.name", "--name=foo,bar,baz"];
args.parseOptions(dat);
assert(dat.names == ["foo", "bar", "baz"], to!string(dat.names));

dat = Dat.init;
args = ["program.name", "--name", "foo,bar,baz"];
args.parseOptions(dat);
assert(dat.names == ["foo", "bar", "baz"], to!string(dat.names));
Examples:
import std.conv;

struct Dat
{
	@opt("values|v")
	int[string] values;
	
	void hogehoge(string x) /+ pure but not annotation +/ { }
}
Dat dat;

dat.values = dat.values.init;
auto args = ["program.name", "-vfoo=0,bar=1,baz=2"];
args.parseOptions(dat);
assert(dat.values == ["foo":0, "bar":1, "baz":2], to!string(dat.values));

dat.values = dat.values.init;
args = ["program.name", "-v", "foo=0,bar=1,baz=2"];
args.parseOptions(dat);
assert(dat.values == ["foo":0, "bar":1, "baz":2], to!string(dat.values));

dat.values = dat.values.init;
args = ["program.name", "--values=foo=0,bar=1,baz=2"];
args.parseOptions(dat);
assert(dat.values == ["foo":0, "bar":1, "baz":2], to!string(dat.values));

dat.values = dat.values.init;
args = ["program.name", "--values", "foo=0,bar=1,baz=2"];
// call from pure
() pure {
	args.parseOptions(dat);
} ();
assert(dat.values == ["foo":0, "bar":1, "baz":2], to!string(dat.values));
Examples:
import std.conv, std.math;
static int xxxxx = 10;

struct Dat
{
	@opt("foo")
	@convBy!(a => a ~ a)
	string valueStr;
	
	@ignore
	static void convBar(ref int dst, string src)
	{
		import std.conv;
		dst = to!int(src);
	}
	
	@ignore
	static void convHoge(ref Dat dst, string src)
	{
		import std.conv, std.range, std.array;
		auto v = to!int(src);
		dst.hogeLen = v;
		dst.hoge = repeat("hoge", v).join;
	}
	
	@opt("bar")
	@convBy!convBar
	int valueI32;
	
	float valueF32;
	
	@ignore
	size_t hogeLen;
	
	@convBy!convHoge
	string hoge;
	
	void fuga(string x) /+ impure +/
	{
		valueF32 += valueI32 + xxxxx;
		valueI32 += to!int(x);
	}
}
Dat dat;

dat.valueStr = dat.valueStr.init;
dat.valueI32 = dat.valueI32.init;
dat.valueF32 = dat.valueF32.init;
auto args = ["program.name", "--foo=aaa", "--bar=12345", "--valueF32=10", "--hoge=3", "--fuga=5"];
args.parseOptions(dat);
assert(dat.valueStr == "aaaaaa");
assert(dat.valueI32 == 12345+5);
assert(dat.valueF32.isClose(10.0f+12345 + 10));
assert(dat.hogeLen == 3);
assert(dat.hoge == "hogehogehoge");
Examples:
import std.conv, std.math;

@passThrough()
struct Dat
{
	@opt("foo")
	int foo;
	@opt("bar")
	string bar;
}
Dat dat;

auto args = ["program.name", "--foo=10", "aaa", "--bar=12345", "--valueF32=10", "bbb", "--hoge=", "ccc", "--fuga=5"];
args.parseOptions(dat);
assert(dat.foo == 10);
assert(dat.bar == "12345");
assert(args == ["program.name", "aaa", "--valueF32=10", "bbb", "--hoge=", "ccc", "--fuga=5"]);