lib/opt: add OptionCount
[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 attr _names: Array[String]
18
19 # Type of the value of the option
20 type VALUE: Object
21
22 # Human readable description of the option
23 readable attr _helptext: String
24
25 # Is this option mandatory?
26 readable writable attr _mandatory: Bool
27
28 # context where the option is located
29 readable writable attr _context: OptionContext
30
31 # Current value of this option
32 readable writable attr _value: VALUE
33
34 # Default value of this option
35 readable writable attr _default_value: VALUE
36
37 # Create a new option
38 init init_opt(help: String, default: VALUE, names: 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 meth add_aliases(names: String...) do _names.add_all(names)
53
54 # An help text for this option with default settings
55 redef meth to_s do return pretty(2)
56
57 # A pretty print for this help
58 meth 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 meth pretty_default: String
71 do
72 if default_value != null then
73 return " ({default_value})"
74 end
75 return ""
76 end
77
78 # Consume parameters for this option
79 protected meth read_param(it: Iterator[String]) is abstract
80 end
81
82 class OptionText
83 special Option
84 init(text: String) do init_opt(text, null, null)
85
86 redef meth pretty(off) do return to_s
87
88 redef meth to_s do return helptext
89 end
90
91 class OptionBool
92 special Option
93 redef type VALUE: Bool
94
95 init(help: String, names: String...) do init_opt(help, false, names)
96
97 redef meth read_param(it) do value = true
98 end
99
100 class OptionCount
101 special Option
102 redef type VALUE: Int
103
104 init(help: String, names: String...) do init_opt(help, 0, names)
105
106 redef meth read_param(it) do value += 1
107 end
108
109 # Option with one mandatory parameter
110 class OptionParameter
111 special Option
112 protected meth convert(str: String): VALUE is abstract
113
114 redef meth read_param(it)
115 do
116 assert context != null
117 if it.is_ok then
118 value = convert(it.item)
119 it.next
120 else
121 # TODO: What to do?
122 end
123 end
124
125 init init_opt(h, d, n) do super
126 end
127
128 class OptionString
129 special OptionParameter
130 redef type VALUE: String
131
132 init(help: String, names: String...) do init_opt(help, null, names)
133
134 redef meth convert(str) do return str
135 end
136
137 class OptionEnum
138 special OptionParameter
139 redef type VALUE: Int
140 attr _enum: Array[String]
141
142 init(enum: Array[String], help: String, default: Int, names: String...)
143 do
144 assert enum != null and enum.length > 0
145 _enum = enum.to_a
146 init_opt("{help} <{enum.join(", ")}>", default, names)
147 end
148
149 redef meth convert(str)
150 do
151 var id = _enum.index_of(str)
152 return id
153 end
154
155 redef meth pretty_default
156 do
157 if default_value != null then
158 return " ({_enum[default_value]})"
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 meth 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 attr _values: Array[String]
185 redef meth convert(str)
186 do
187 _values.add(str)
188 return _values
189 end
190 end
191
192 class OptionContext
193 readable attr _options: Array[Option]
194 readable attr _rest: Array[String]
195
196 attr _optmap: Map[String, Option]
197
198 meth 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 meth parse(argv: Collection[String])
216 do
217 var it = argv.iterator
218 parse_intern(it)
219 end
220
221 protected meth 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 meth add_option(opts: Option...)
247 do
248 for opt in opts do
249 opt.context = self
250 _options.add(opt)
251 end
252 end
253
254 init
255 do
256 _options = new Array[Option]
257 _optmap = new HashMap[String, Option]
258 _rest = new Array[String]
259 end
260
261 private meth build
262 do
263 for o in _options do
264 for n in o.names do
265 _optmap[n] = o
266 end
267 end
268 end
269 end