1 module aurorafw.core.opt;
2 
3 /****************************************************************************
4 ** ┌─┐┬ ┬┬─┐┌─┐┬─┐┌─┐  ┌─┐┬─┐┌─┐┌┬┐┌─┐┬ ┬┌─┐┬─┐┬┌─
5 ** ├─┤│ │├┬┘│ │├┬┘├─┤  ├┤ ├┬┘├─┤│││├┤ ││││ │├┬┘├┴┐
6 ** ┴ ┴└─┘┴└─└─┘┴└─┴ ┴  └  ┴└─┴ ┴┴ ┴└─┘└┴┘└─┘┴└─┴ ┴
7 ** A Powerful General Purpose Framework
8 ** More information in: https://aurora-fw.github.io/
9 **
10 ** Copyright (C) 2018 Aurora Framework, All rights reserved.
11 **
12 ** This file is part of the Aurora Framework. This framework is free
13 ** software; you can redistribute it and/or modify it under the terms of
14 ** the GNU Lesser General Public License version 3 as published by the
15 ** Free Software Foundation and appearing in the file LICENSE included in
16 ** the packaging of this file. Please review the following information to
17 ** ensure the GNU Lesser General Public License version 3 requirements
18 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
19 ****************************************************************************/
20 
21 import aurorafw.core.debugmanager : debugMsgPrefix;
22 import std.algorithm.searching : find;
23 import std.range.primitives : empty;
24 
25 @safe pure struct OptionHandler {
26 	struct Element {
27 		string opt;
28 		string desc;
29 	}
30 
31 	struct SplitedElement {
32 		bool active;
33 		size_t count;
34 
35 	private:
36 		string optLong;
37 		string optShort;
38 		string desc;
39 
40 		ptrdiff_t valpos;
41 	}
42 
43 	enum OptionType {
44 		Posix,
45 		Windows,
46 		None
47 	}
48 
49 	this(string[] args, OptionType type = __currentType)
50 	{
51 		_args = args;
52 		_type = type;
53 	}
54 
55 	void add(Element opte)
56 	{
57 		import std.array : split;
58 		immutable string[] elems = split(opte.opt, "|");
59 		SplitedElement sopte;
60 
61 		assert(elems.length == 1, "Blank option elements not allowed!");
62 
63 		switch(_type)
64 		{
65 			case OptionType.Posix:
66 				if(elems.length > 1)
67 				{
68 					sopte.optShort = "-" ~ ((elems[0].length < elems[1].length) ? elems[0] : elems[1]);
69 					sopte.optLong = "--" ~ ((elems[0].length > elems[1].length) ? elems[0] : elems[1]);
70 				}
71 				else if (elems[0].length > 1)
72 				{
73 					sopte.optLong = "--" ~ elems[0];
74 				}
75 				else
76 				{
77 					sopte.optShort = "-" ~ elems[0];
78 				}
79 				break;
80 			
81 			case OptionType.Windows:
82 				if (elems.length > 1)
83 				{
84 					sopte.optShort = "/" ~ ((elems[0].length < elems[1].length) ? elems[0] : elems[1]);
85 					sopte.optLong = "/" ~ ((elems[0].length > elems[1].length) ? elems[0] : elems[1]);
86 				}
87 				else
88 				{
89 					sopte.optLong = "/" ~ elems[0];
90 				}
91 				break;
92 			
93 			case OptionType.None:
94 			default:
95 				if(elems.length > 1)
96 				{
97 					sopte.optShort = ((elems[0].length < elems[1].length) ? elems[0] : elems[1]);
98 					sopte.optLong = ((elems[0].length > elems[1].length) ? elems[0] : elems[1]);
99 				}
100 				else
101 				{
102 					sopte.optLong = elems[0];
103 				}
104 				break;
105 		}
106 		sopte.desc = opte.desc;
107 
108 		foreach(size_t i, string arg; _args)
109 			if((find(arg, sopte.optLong).empty &&
110 				((arg.length == sopte.optLong.length) ||
111 					((arg.length > sopte.optLong.length) && (arg[sopte.optLong.length .. arg.length][0] == '=')) ))
112 				|| (find(arg, sopte.optShort).empty && (arg.length == sopte.optShort.length )) )
113 			{
114 				if(arg.length > sopte.optLong.length) sopte.valpos = sopte.optLong.length;
115 				else sopte.valpos = -1;
116 				sopte.active = true;
117 				sopte.count = i;
118 			}
119 			else {
120 				sopte.active = false;
121 			}
122 
123 		_opts ~= sopte;
124 	}
125 
126 	pragma(inline, true) void add(string opt, string desc) { add(Element(opt, desc)); }
127 
128 	void add(Element[] mopte)
129 	{
130 		foreach(Element opte; mopte)
131 			add(opte);
132 	}
133 
134 	SplitedElement option(string opt)
135 	{
136 		foreach(SplitedElement sopte; _opts)
137 			if(!find(sopte.optLong, opt).empty || !find(sopte.optShort, opt).empty)
138 				return sopte;
139 
140 		assert(0, "Invalid option name!");
141 	}
142 
143 	string value(SplitedElement sopte)
144 	{
145 		if(sopte.valpos == -1)
146 			return _args[sopte.count+1];
147 		else
148 			return _args[sopte.count][sopte.valpos .. _args[sopte.count].length];
149 	}
150 
151 	pragma(inline, true) string value(string opt) { return value(option(opt)); }
152 
153 	void print(string fmt = "  %s\t%s\t\t%s") @safe
154 	{
155 		pragma(msg, debugMsgPrefix, "FIXME: Need to be implemented");
156 	}
157 
158 private:
159 	string[] _args;
160 	SplitedElement[] _opts;
161 	OptionType _type;
162 
163 	version(Windows) enum OptionType __currentType = OptionType.Windows;
164 	else enum OptionType __currentType = OptionType.Posix;
165 }