lib: change identifier enum to values
[nit.git] / lib / opts.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Floréal Morandat <morandat@lirmm.fr>
4 # Copyright 2008 Jean Privat <jean@pryen.org>
5 #
6 # This file is free software, which comes along with NIT. This software is
7 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
8 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
9 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
10 # is kept unaltered, and a notification of the changes is added.
11 # You are allowed to redistribute it and sell it, alone or is a part of
12 # another product.
13
14 # Super class of all option's class
15 class Option
16 # Names for the option (including long and short ones)
17 readable var _names: Array[String]
18
19 # Type of the value of the option
20 type VALUE: nullable Object
21
22 # Human readable description of the option
23 readable var _helptext: String
24
25 # Is this option mandatory?
26 readable writable var _mandatory: Bool
27
28 # Current value of this option
29 writable var _value: nullable VALUE
30
31 # Current value of this option
32 fun value: VALUE do return _value.as(VALUE)
33
34 # Default value of this option
35 readable writable var _default_value: nullable VALUE
36
37 # Create a new option
38 init init_opt(help: String, default: nullable VALUE, names: nullable Array[String])
39 do
40 if names == null then
41 _names = new Array[String]
42 else
43 _names = names.to_a
44 end
45 _helptext = help
46 _mandatory = false
47 _default_value = default
48 _value = default
49 end
50
51 # Add new aliases for this option
52 fun add_aliases(names: String...) do _names.add_all(names)
53
54 # An help text for this option with default settings
55 redef fun to_s do return pretty(2)
56
57 # A pretty print for this help
58 fun pretty(off: Int): String
59 do
60 var text = new Buffer.from(" ")
61 text.append(_names.join(", "))
62 text.append(" ")
63 var rest = off - text.length
64 if rest > 0 then text.append(" " * rest)
65 text.append(helptext)
66 #text.append(pretty_default)
67 return text.to_s
68 end
69
70 fun pretty_default: String
71 do
72 var dv = default_value
73 if dv != null then return " ({dv})"
74 return ""
75 end
76
77 # Consume parameters for this option
78 protected fun read_param(it: Iterator[String]) is abstract
79 end
80
81 class OptionText
82 special Option
83 init(text: String) do init_opt(text, null, null)
84
85 redef fun pretty(off) do return to_s
86
87 redef fun to_s do return helptext
88 end
89
90 class OptionBool
91 special Option
92 redef type VALUE: Bool
93
94 init(help: String, names: String...) do init_opt(help, false, names)
95
96 redef fun read_param(it) do value = true
97 end
98
99 class OptionCount
100 special Option
101 redef type VALUE: Int
102
103 init(help: String, names: String...) do init_opt(help, 0, names)
104
105 redef fun read_param(it) do value += 1
106 end
107
108 # Option with one mandatory parameter
109 class OptionParameter
110 special Option
111 protected fun convert(str: String): VALUE is abstract
112
113 redef fun read_param(it)
114 do
115 if it.is_ok then
116 value = convert(it.item)
117 it.next
118 else
119 # TODO: What to do?
120 end
121 end
122
123 init init_opt(h, d, n) do super
124 end
125
126 class OptionString
127 special OptionParameter
128 redef type VALUE: nullable String
129
130 init(help: String, names: String...) do init_opt(help, null, names)
131
132 redef fun convert(str) do return str
133 end
134
135 class OptionEnum
136 special OptionParameter
137 redef type VALUE: Int
138 var _values: Array[String]
139
140 init(values: Array[String], help: String, default: Int, names: String...)
141 do
142 assert values.length > 0
143 _values = values.to_a
144 init_opt("{help} <{values.join(", ")}>", default, names)
145 end
146
147 redef fun convert(str)
148 do
149 var id = _values.index_of(str)
150 return id
151 end
152
153 fun value_name: String = _values[value]
154
155 redef fun pretty_default
156 do
157 if default_value != null then
158 return " ({_values[default_value.as(not null)]})"
159 else
160 return ""
161 end
162 end
163 end
164
165 class OptionInt
166 special OptionParameter
167 redef type VALUE: Int
168
169 init(help: String, default: Int, names: String...) do init_opt(help, default, names)
170
171 redef fun convert(str) do return str.to_i
172 end
173
174 class OptionArray
175 special OptionParameter
176 redef type VALUE: Array[String]
177
178 init(help: String, names: String...)
179 do
180 _values = new Array[String]
181 init_opt(help, _values, names)
182 end
183
184 var _values: Array[String]
185 redef fun convert(str)
186 do
187 _values.add(str)
188 return _values
189 end
190 end
191
192 class OptionContext
193 readable var _options: Array[Option]
194 readable var _rest: Array[String]
195
196 var _optmap: Map[String, Option]
197
198 fun usage
199 do
200 var lmax = 1
201 for i in _options do
202 var l = 3
203 for n in i.names do
204 l += n.length + 2
205 end
206 if lmax < l then lmax = l
207 end
208
209 for i in _options do
210 print(i.pretty(lmax))
211 end
212 end
213
214 # Parse ans assign options everywhere is the argument list
215 fun parse(argv: Collection[String])
216 do
217 var it = argv.iterator
218 parse_intern(it)
219 end
220
221 protected fun parse_intern(it: Iterator[String])
222 do
223 var parseargs = true
224 build
225 var rest = _rest
226
227 while parseargs and it.is_ok do
228 var str = it.item
229 if str == "--" then
230 it.next
231 rest.add_all(it.to_a)
232 parseargs = false
233 else
234 if _optmap.has_key(str) then
235 var opt = _optmap[str]
236 it.next
237 opt.read_param(it)
238 else
239 rest.add(it.item)
240 it.next
241 end
242 end
243 end
244 end
245
246 fun add_option(opts: Option...)
247 do
248 for opt in opts do
249 _options.add(opt)
250 end
251 end
252
253 init
254 do
255 _options = new Array[Option]
256 _optmap = new HashMap[String, Option]
257 _rest = new Array[String]
258 end
259
260 private fun build
261 do
262 for o in _options do
263 for n in o.names do
264 _optmap[n] = o
265 end
266 end
267 end
268 end