First NIT release and new clean mercurial repository
[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 end
49
50 # Add new aliases for this option
51 meth add_aliases(names: String...) do _names.add_all(names)
52
53 # An help text for this option with default settings
54 redef meth to_s do return pretty(2)
55
56 # A pretty print for this help
57 meth pretty(off: Int): String
58 do
59 var text = " "
60 text.append(_names.join(", "))
61 text.append(" ")
62 var rest = off - text.length
63 if rest > 0 then text.append(" " * rest)
64 text.append(helptext)
65 #text.append(pretty_default)
66 return text
67 end
68
69 meth pretty_default: String
70 do
71 if default_value != null then
72 return " ({default_value})"
73 end
74 return ""
75 end
76
77 # Consume parameters for this option
78 protected meth 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 meth pretty(off) do return to_s
86
87 redef meth 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 meth read_param(it) do value = true
97 end
98
99 # Option with one mandatory parameter
100 class OptionParameter
101 special Option
102 protected meth convert(str: String): VALUE is abstract
103
104 redef meth read_param(it)
105 do
106 assert context != null
107 if it.is_ok then
108 value = convert(it.item)
109 it.next
110 else
111 # TODO: What to do?
112 end
113 end
114
115 init init_opt(h, d, n) do super
116 end
117
118 class OptionString
119 special OptionParameter
120 redef type VALUE: String
121
122 init(help: String, names: String...) do init_opt(help, null, names)
123
124 redef meth convert(str) do return str
125 end
126
127 class OptionEnum
128 special OptionParameter
129 redef type VALUE: Int
130 attr _enum: Array[String]
131
132 init(enum: Array[String], help: String, default: Int, names: String...)
133 do
134 assert enum != null and enum.length > 0
135 _enum = enum.to_a
136 init_opt("{help} <{enum.join(", ")}>", default, names)
137 end
138
139 redef meth convert(str)
140 do
141 var id = _enum.index_of(str)
142 return id
143 end
144
145 redef meth pretty_default
146 do
147 if default_value != null then
148 return " ({_enum[default_value]})"
149 else
150 return ""
151 end
152 end
153 end
154
155 class OptionInt
156 special OptionParameter
157 redef type VALUE: Int
158
159 init(help: String, default: Int, names: String...) do init_opt(help, default, names)
160
161 redef meth convert(str) do return str.to_i
162 end
163
164 class OptionArray
165 special OptionParameter
166 redef type VALUE: Array[String]
167
168 init(help: String, names: String...)
169 do
170 _values = new Array[String]
171 init_opt(help, _values, names)
172 end
173
174 attr _values: Array[String]
175 redef meth convert(str)
176 do
177 _values.add(str)
178 return _values
179 end
180 end
181
182 class OptionContext
183 readable attr _options: Array[Option]
184 readable attr _rest: Array[String]
185
186 attr _optmap: Map[String, Option]
187
188 meth usage
189 do
190 var lmax = 1
191 for i in _options do
192 var l = 3
193 for n in i.names do
194 l += n.length + 2
195 end
196 if lmax < l then lmax = l
197 end
198
199 for i in _options do
200 print(i.pretty(lmax))
201 end
202 end
203
204 # Parse ans assign options everywhere is the argument list
205 meth parse(argv: Collection[String])
206 do
207 var it = argv.iterator
208 parse_intern(it)
209 end
210
211 protected meth parse_intern(it: Iterator[String])
212 do
213 var parseargs = true
214 build
215 var rest = _rest
216
217 while parseargs and it.is_ok do
218 var str = it.item
219 if str == "--" then
220 it.next
221 rest.add_all(it.to_a)
222 parseargs = false
223 else
224 if _optmap.has_key(str) then
225 var opt = _optmap[str]
226 it.next
227 opt.read_param(it)
228 else
229 rest.add(it.item)
230 it.next
231 end
232 end
233 end
234 end
235
236 meth add_option(opts: Option...)
237 do
238 for opt in opts do
239 opt.context = self
240 _options.add(opt)
241 end
242 end
243
244 init
245 do
246 _options = new Array[Option]
247 _optmap = new HashMap[String, Option]
248 _rest = new Array[String]
249 end
250
251 private meth build
252 do
253 for o in _options do
254 for n in o.names do
255 _optmap[n] = o
256 end
257 end
258 end
259 end