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