@@ -4,8 +4,12 @@ const mem = std.mem;
44const os = std .os ;
55const expect = std .testing .expect ;
66
7+ /// Parsed option struct.
78pub const Option = struct {
9+ /// Option character.
810 opt : u8 ,
11+
12+ /// Option argument, if any.
913 arg : ? []const u8 = null ,
1014};
1115
@@ -15,8 +19,12 @@ pub const OptionsIterator = struct {
1519 argv : [][* :0 ]const u8 ,
1620 opts : []const u8 ,
1721
22+ /// Index of the current element of the argv vector.
1823 optind : usize = 1 ,
24+
1925 optpos : usize = 1 ,
26+
27+ /// Current option character.
2028 optopt : u8 = undefined ,
2129
2230 pub fn next (self : * OptionsIterator ) Error ! ? Option {
@@ -35,13 +43,13 @@ pub const OptionsIterator = struct {
3543
3644 self .optopt = arg [self .optpos ];
3745
38- const maybe_i = mem .indexOfScalar (u8 , self .opts , self .optopt );
39- if (maybe_i ) | i | {
40- if (i < self .opts .len - 1 and self .opts [i + 1 ] == ':' ) {
46+ const maybe_idx = mem .indexOfScalar (u8 , self .opts , self .optopt );
47+ if (maybe_idx ) | idx | {
48+ if (idx < self .opts .len - 1 and self .opts [idx + 1 ] == ':' ) {
4149 if (arg [self .optpos + 1 ] != 0 ) {
4250 const res = Option {
4351 .opt = self .optopt ,
44- .arg = mem .sliceTo (arg + self .optpos + 1 , 0 ),
52+ .arg = mem .span (arg + self .optpos + 1 ),
4553 };
4654 self .optind += 1 ;
4755 self .optpos = 1 ;
@@ -66,22 +74,27 @@ pub const OptionsIterator = struct {
6674 } else return Error .InvalidOption ;
6775 }
6876
69- pub fn args (self : * OptionsIterator ) [][* :0 ]const u8 {
70- return argv [self .optind .. ];
77+ /// Return remaining arguments, if any.
78+ pub fn args (self : * OptionsIterator ) ? [][* :0 ]const u8 {
79+ if (self .optind < self .argv .len )
80+ return self .argv [self .optind .. ]
81+ else
82+ return null ;
7183 }
7284};
7385
74- fn getoptArgv (argv : [][* :0 ]const u8 , opts : []const u8 ) OptionsIterator {
86+ fn getoptArgv (argv : [][* :0 ]const u8 , optstring : []const u8 ) OptionsIterator {
7587 return OptionsIterator {
7688 .argv = argv ,
77- .opts = opts ,
89+ .opts = optstring ,
7890 };
7991}
8092
81- pub fn getopt (opts : []const u8 ) OptionsIterator {
93+ /// Parse os.argv according to the optstring.
94+ pub fn getopt (optstring : []const u8 ) OptionsIterator {
8295 // https://github.com/ziglang/zig/issues/8808
8396 const argv : [][* :0 ]const u8 = os .argv ;
84- return getoptArgv (argv , opts );
97+ return getoptArgv (argv , optstring );
8598}
8699
87100test "no args separate" {
@@ -107,6 +120,8 @@ test "no args separate" {
107120 try expect (opt .arg == null and expected [i ].arg == null );
108121 }
109122 }
123+
124+ try expect (opts .args () == null );
110125}
111126
112127test "no args joined" {
@@ -216,7 +231,7 @@ test "invalid option" {
216231 if (maybe_opt ) {
217232 unreachable ;
218233 } else | err | {
219- try expect (err == OptionError .InvalidOption );
234+ try expect (err == Error .InvalidOption );
220235 try expect (opts .optopt == 'z' );
221236 }
222237}
@@ -236,7 +251,70 @@ test "missing argument" {
236251 if (maybe_opt ) {
237252 unreachable ;
238253 } else | err | {
239- try expect (err == OptionError .MissingArgument );
254+ try expect (err == Error .MissingArgument );
240255 try expect (opts .optopt == 'z' );
241256 }
242257}
258+
259+ test "positional args" {
260+ var argv = [_ ][* :0 ]const u8 {
261+ "getopt" ,
262+ "-abc10" ,
263+ "-d" ,
264+ "foo" ,
265+ "bar" ,
266+ };
267+
268+ const expected = [_ ]Option {
269+ .{ .opt = 'a' },
270+ .{ .opt = 'b' },
271+ .{
272+ .opt = 'c' ,
273+ .arg = "10" ,
274+ },
275+ .{ .opt = 'd' },
276+ };
277+
278+ var opts = getoptArgv (& argv , "abc:d" );
279+
280+ var i : usize = 0 ;
281+ while (try opts .next ()) | opt | : (i += 1 ) {
282+ try expect (opt .opt == expected [i ].opt );
283+ if (opt .arg != null and expected [i ].arg != null ) {
284+ try expect (mem .eql (u8 , opt .arg .? , expected [i ].arg .? ));
285+ } else {
286+ try expect (opt .arg == null and expected [i ].arg == null );
287+ }
288+ }
289+
290+ try expect (mem .eql ([* :0 ]const u8 , opts .args ().? , &[_ ][* :0 ]const u8 { "foo" , "bar" }));
291+ }
292+
293+ test "positional args with separator" {
294+ var argv = [_ ][* :0 ]const u8 {
295+ "getopt" ,
296+ "-ab" ,
297+ "--" ,
298+ "foo" ,
299+ "bar" ,
300+ };
301+
302+ const expected = [_ ]Option {
303+ .{ .opt = 'a' },
304+ .{ .opt = 'b' },
305+ };
306+
307+ var opts = getoptArgv (& argv , "ab" );
308+
309+ var i : usize = 0 ;
310+ while (try opts .next ()) | opt | : (i += 1 ) {
311+ try expect (opt .opt == expected [i ].opt );
312+ if (opt .arg != null and expected [i ].arg != null ) {
313+ try expect (mem .eql (u8 , opt .arg .? , expected [i ].arg .? ));
314+ } else {
315+ try expect (opt .arg == null and expected [i ].arg == null );
316+ }
317+ }
318+
319+ try expect (mem .eql ([* :0 ]const u8 , opts .args ().? , &[_ ][* :0 ]const u8 { "foo" , "bar" }));
320+ }
0 commit comments