5f409e8119f2208ad08f999013430dab06379aac
[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: nullable 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 # Current value of this option
29 writable attr _value: nullable VALUE
30
31 # Current value of this option
32 meth value: VALUE do return _value.as(VALUE)
33
34 # Default value of this option
35 readable writable attr _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 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 if it.is_ok then
117 value = convert(it.item)
118 it.next
119 else
120 # TODO: What to do?
121 end
122 end
123
124 init init_opt(h, d, n) do super
125 end
126
127 class OptionString
128 special OptionParameter
129 redef type VALUE: nullable String
130
131 init(help: String, names: String...) do init_opt(help, null, names)
132
133 redef meth convert(str) do return str
134 end
135
136 class OptionEnum
137 special OptionParameter
138 redef type VALUE: Int
139 attr _enum: Array[String]
140
141 init(enum: Array[String], help: String, default: Int, names: String...)
142 do
143 assert enum.length > 0
144 _enum = enum.to_a
145 init_opt("{help} <{enum.join(", ")}>", default, names)
146 end
147
148 redef meth convert(str)
149 do
150 var id = _enum.index_of(str)
151 return id
152 end
153
154 redef meth pretty_default
155 do
156 if default_value != null then
157 return " ({_enum[default_value.as(not null)]})"
158 else
159 return ""
160 end
161 end
162 end
163
164 class OptionInt
165 special OptionParameter
166 redef type VALUE: Int
167
168 init(help: String, default: Int, names: String...) do init_opt(help, default, names)
169
170 redef meth convert(str) do return str.to_i
171 end
172
173 class OptionArray
174 special OptionParameter
175 redef type VALUE: Array[String]
176
177 init(help: String, names: String...)
178 do
179 _values = new Array[String]
180 init_opt(help, _values, names)
181 end
182
183 attr _values: Array[String]
184 redef meth convert(str)
185 do
186 _values.add(str)
187 return _values
188 end
189 end
190
191 class OptionContext
192 readable attr _options: Array[Option]
193 readable attr _rest: Array[String]
194
195 attr _optmap: Map[String, Option]
196
197 meth usage
198 do
199 var lmax = 1
200 for i in _options do
201 var l = 3
202 for n in i.names do
203 l += n.length + 2
204 end
205 if lmax < l then lmax = l
206 end
207
208 for i in _options do
209 print(i.pretty(lmax))
210 end
211 end
212
213 # Parse ans assign options everywhere is the argument list
214 meth parse(argv: Collection[String])
215 do
216 var it = argv.iterator
217 parse_intern(it)
218 end
219
220 protected meth parse_intern(it: Iterator[String])
221 do
222 var parseargs = true
223 build
224 var rest = _rest
225
226 while parseargs and it.is_ok do
227 var str = it.item
228 if str == "--" then
229 it.next
230 rest.add_all(it.to_a)
231 parseargs = false
232 else
233 if _optmap.has_key(str) then
234 var opt = _optmap[str]
235 it.next
236 opt.read_param(it)
237 else
238 rest.add(it.item)
239 it.next
240 end
241 end
242 end
243 end
244
245 meth add_option(opts: Option...)
246 do
247 for opt in opts do
248 _options.add(opt)
249 end
250 end
251
252 init
253 do
254 _options = new Array[Option]
255 _optmap = new HashMap[String, Option]
256 _rest = new Array[String]
257 end
258
259 private meth build
260 do
261 for o in _options do
262 for n in o.names do
263 _optmap[n] = o
264 end
265 end
266 end
267 end