44//! and subcommands (vmu zig/zls).
55
66const std = @import ("std" );
7+
78const errors = @import ("core/errors.zig" );
89
910/// Supported shell types for completion generation.
@@ -21,6 +22,7 @@ pub const Command = enum {
2122 vmu ,
2223 mirrorlist ,
2324 proxy ,
25+ probe ,
2426 completion ,
2527 version ,
2628 help ,
@@ -38,6 +40,7 @@ pub const InstallFlags = packed struct {
3840 zls : bool = false ,
3941 full : bool = false ,
4042 nomirror : bool = false ,
43+ reprobe : bool = false ,
4144};
4245
4346/// Flags for the list command.
@@ -83,11 +86,12 @@ pub const ParsedCommand = union(Command) {
8386 proxy : struct {
8487 url : ? []const u8 ,
8588 },
89+ probe ,
8690 completion : struct {
8791 shell : ShellType ,
8892 },
8993 version ,
90- help ,
94+ help : ? Command ,
9195};
9296
9397/// Global flags that apply before the command (e.g., --color).
@@ -112,6 +116,7 @@ const command_aliases = std.StaticStringMap(Command).initComptime(.{
112116 .{ "vmu" , .vmu },
113117 .{ "mirrorlist" , .mirrorlist },
114118 .{ "proxy" , .proxy },
119+ .{ "probe" , .probe },
115120 .{ "completion" , .completion },
116121 .{ "version" , .version },
117122 .{ "help" , .help },
@@ -147,7 +152,7 @@ pub fn parse(allocator: std.mem.Allocator, init: std.process.Init.Minimal) !stru
147152 } else if (std .mem .cutPrefix (u8 , arg , "--color=" )) | val | {
148153 if (parseColorValue (val )) | c | global_flags .color = c ;
149154 } else if (std .mem .eql (u8 , arg , "--help" ) or std .mem .eql (u8 , arg , "-h" )) {
150- return .{ .global = global_flags , .cmd = . help };
155+ return .{ .global = global_flags , .cmd = @unionInit ( ParsedCommand , " help" , null ) };
151156 } else if (std .mem .eql (u8 , arg , "--version" ) or std .mem .eql (u8 , arg , "-v" )) {
152157 return .{ .global = global_flags , .cmd = .version };
153158 } else {
@@ -158,7 +163,7 @@ pub fn parse(allocator: std.mem.Allocator, init: std.process.Init.Minimal) !stru
158163 }
159164 }
160165
161- const cmd = maybe_cmd orelse return .{ .global = global_flags , .cmd = . help };
166+ const cmd = maybe_cmd orelse return .{ .global = global_flags , .cmd = @unionInit ( ParsedCommand , " help" , null ) };
162167
163168 // Check if the raw command name implies --all
164169 const auto_all = if (cmd_raw ) | raw |
@@ -177,9 +182,10 @@ pub fn parse(allocator: std.mem.Allocator, init: std.process.Init.Minimal) !stru
177182 .vmu = > try parseVmu (allocator , & args ),
178183 .mirrorlist = > try parseMirrorlist (allocator , & args ),
179184 .proxy = > try parseProxy (allocator , & args ),
185+ .probe = > parseProbe (& args ),
180186 .completion = > try parseCompletion (& args ),
181187 .version = > ParsedCommand .version ,
182- .help = > ParsedCommand . help ,
188+ .help = > @unionInit ( ParsedCommand , " help" , null ) ,
183189 };
184190
185191 return .{ .global = global_flags , .cmd = parsed };
@@ -208,14 +214,18 @@ fn parseInstall(allocator: std.mem.Allocator, args: anytype) !ParsedCommand {
208214 var version : ? []const u8 = null ;
209215
210216 while (args .next ()) | arg | {
211- if (std .mem .eql (u8 , arg , "--force" ) or std .mem .eql (u8 , arg , "-f" )) {
217+ if (std .mem .eql (u8 , arg , "--help" ) or std .mem .eql (u8 , arg , "-h" )) {
218+ return .{ .help = .install };
219+ } else if (std .mem .eql (u8 , arg , "--force" ) or std .mem .eql (u8 , arg , "-f" )) {
212220 flags .force = true ;
213221 } else if (std .mem .eql (u8 , arg , "--zls" )) {
214222 flags .zls = true ;
215223 } else if (std .mem .eql (u8 , arg , "--full" )) {
216224 flags .full = true ;
217225 } else if (std .mem .eql (u8 , arg , "--nomirror" )) {
218226 flags .nomirror = true ;
227+ } else if (std .mem .eql (u8 , arg , "--test" ) or std .mem .eql (u8 , arg , "-t" )) {
228+ flags .reprobe = true ;
219229 } else {
220230 version = try allocator .dupe (u8 , arg );
221231 }
@@ -232,7 +242,9 @@ fn parseUse(allocator: std.mem.Allocator, args: anytype) !ParsedCommand {
232242 var version : ? []const u8 = null ;
233243
234244 while (args .next ()) | arg | {
235- if (std .mem .eql (u8 , arg , "--sync" )) {
245+ if (std .mem .eql (u8 , arg , "--help" ) or std .mem .eql (u8 , arg , "-h" )) {
246+ return @unionInit (ParsedCommand , "help" , .use );
247+ } else if (std .mem .eql (u8 , arg , "--sync" )) {
236248 flags .sync = true ;
237249 } else {
238250 version = try allocator .dupe (u8 , arg );
@@ -249,7 +261,9 @@ fn parseList(args: anytype, auto_all: bool) ParsedCommand {
249261 var flags : ListFlags = .{ .all = auto_all };
250262
251263 while (args .next ()) | arg | {
252- if (std .mem .eql (u8 , arg , "--all" ) or std .mem .eql (u8 , arg , "-a" )) {
264+ if (std .mem .eql (u8 , arg , "--help" ) or std .mem .eql (u8 , arg , "-h" )) {
265+ return @unionInit (ParsedCommand , "help" , .list );
266+ } else if (std .mem .eql (u8 , arg , "--all" ) or std .mem .eql (u8 , arg , "-a" )) {
253267 flags .all = true ;
254268 } else if (std .mem .eql (u8 , arg , "--vmu" )) {
255269 flags .vmu = true ;
@@ -261,13 +275,19 @@ fn parseList(args: anytype, auto_all: bool) ParsedCommand {
261275
262276fn parseUninstall (allocator : std.mem.Allocator , args : anytype ) ! ParsedCommand {
263277 const version = args .next () orelse return error .MissingArgument ;
278+ if (std .mem .eql (u8 , version , "--help" ) or std .mem .eql (u8 , version , "-h" )) {
279+ return @unionInit (ParsedCommand , "help" , .uninstall );
280+ }
264281 return .{ .uninstall = .{
265282 .version = try allocator .dupe (u8 , version ),
266283 } };
267284}
268285
269286fn parseRun (allocator : std.mem.Allocator , args : anytype ) ! ParsedCommand {
270287 const version = args .next () orelse return error .MissingArgument ;
288+ if (std .mem .eql (u8 , version , "--help" ) or std .mem .eql (u8 , version , "-h" )) {
289+ return @unionInit (ParsedCommand , "help" , .run );
290+ }
271291
272292 var run_args : std .ArrayList ([]const u8 ) = .empty ;
273293 errdefer run_args .deinit (allocator );
@@ -284,6 +304,9 @@ fn parseRun(allocator: std.mem.Allocator, args: anytype) !ParsedCommand {
284304
285305fn parseVmu (allocator : std.mem.Allocator , args : anytype ) ! ParsedCommand {
286306 const subcmd = args .next () orelse return error .MissingArgument ;
307+ if (std .mem .eql (u8 , subcmd , "--help" ) or std .mem .eql (u8 , subcmd , "-h" )) {
308+ return @unionInit (ParsedCommand , "help" , .vmu );
309+ }
287310 const value = args .next () orelse return error .MissingArgument ;
288311
289312 const target : VmuTarget = if (std .mem .eql (u8 , subcmd , "zig" ))
@@ -301,20 +324,44 @@ fn parseVmu(allocator: std.mem.Allocator, args: anytype) !ParsedCommand {
301324
302325fn parseMirrorlist (allocator : std.mem.Allocator , args : anytype ) ! ParsedCommand {
303326 const url = args .next ();
304- return .{ .mirrorlist = .{
305- .url = if (url ) | u | try allocator .dupe (u8 , u ) else null ,
306- } };
327+ if (url ) | u | {
328+ if (std .mem .eql (u8 , u , "--help" ) or std .mem .eql (u8 , u , "-h" )) {
329+ return @unionInit (ParsedCommand , "help" , .mirrorlist );
330+ }
331+ return .{ .mirrorlist = .{
332+ .url = try allocator .dupe (u8 , u ),
333+ } };
334+ }
335+ return .{ .mirrorlist = .{ .url = null } };
307336}
308337
309338fn parseProxy (allocator : std.mem.Allocator , args : anytype ) ! ParsedCommand {
310339 const url = args .next ();
311- return .{ .proxy = .{
312- .url = if (url ) | u | try allocator .dupe (u8 , u ) else null ,
313- } };
340+ if (url ) | u | {
341+ if (std .mem .eql (u8 , u , "--help" ) or std .mem .eql (u8 , u , "-h" )) {
342+ return @unionInit (ParsedCommand , "help" , .proxy );
343+ }
344+ return .{ .proxy = .{
345+ .url = try allocator .dupe (u8 , u ),
346+ } };
347+ }
348+ return .{ .proxy = .{ .url = null } };
349+ }
350+
351+ fn parseProbe (args : anytype ) ParsedCommand {
352+ if (args .next ()) | arg | {
353+ if (std .mem .eql (u8 , arg , "--help" ) or std .mem .eql (u8 , arg , "-h" )) {
354+ return @unionInit (ParsedCommand , "help" , .probe );
355+ }
356+ }
357+ return ParsedCommand .probe ;
314358}
315359
316360fn parseCompletion (args : anytype ) ! ParsedCommand {
317361 const shell_str = args .next () orelse return error .MissingArgument ;
362+ if (std .mem .eql (u8 , shell_str , "--help" ) or std .mem .eql (u8 , shell_str , "-h" )) {
363+ return @unionInit (ParsedCommand , "help" , .completion );
364+ }
318365 const shell : ShellType = if (std .mem .eql (u8 , shell_str , "zsh" ))
319366 .zsh
320367 else if (std .mem .eql (u8 , shell_str , "bash" ))
@@ -343,6 +390,7 @@ pub fn printHelp(writer: *std.Io.Writer) !void {
343390 \\ vmu Set version map source (zig/zls)
344391 \\ mirrorlist Set mirror distribution server
345392 \\ proxy Set HTTP/HTTPS proxy for downloads
393+ \\ probe Test mirror speeds without installing
346394 \\ completion Generate shell completion script
347395 \\ version Print zvm version
348396 \\ help Print this help message
@@ -371,6 +419,7 @@ pub fn printCommandHelp(writer: *std.Io.Writer, cmd: Command) !void {
371419 \\ --zls Also install ZLS (Zig Language Server)
372420 \\ --full Install ZLS with full compatibility mode
373421 \\ --nomirror Skip community mirror downloads
422+ \\ --test, -t Force re-probe mirrors (ignore cache)
374423 \\
375424 \\Examples:
376425 \\ zvm install master Install latest nightly
@@ -470,6 +519,27 @@ pub fn printCommandHelp(writer: *std.Io.Writer, cmd: Command) !void {
470519 \\ zvm proxy Show current proxy setting
471520 \\
472521 ),
522+ .probe = > try writer .writeAll (
523+ \\Test mirror download speeds without installing anything.
524+ \\
525+ \\Downloads data from each mirror for 5 seconds and measures
526+ \\throughput. Results are displayed in real-time.
527+ \\
528+ \\Usage:
529+ \\ zvm probe
530+ \\
531+ ),
532+ .completion = > try writer .writeAll (
533+ \\Generate shell completion script.
534+ \\
535+ \\Usage:
536+ \\ zvm completion <shell>
537+ \\
538+ \\Examples:
539+ \\ zvm completion zsh
540+ \\ zvm completion bash
541+ \\
542+ ),
473543 .version , .help = > printHelp (writer ) catch {},
474544 }
475545 try writer .flush ();
0 commit comments