╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ command.zig ──────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
158 } else null; 159 160 return .{ 161 .allocator = allocator, 162 .arena = arena, 163 .command_kind = command_kind, 164 .command_name = command_name, 165 .positional_args = extra_args, 166 .map_args = map_args, 167 .unused_args = unused_args, 168 }; 169 } 170 } 171 172 pub fn deinit(self: *CommandArgs) void { 173 self.arena.deinit(); 174 self.allocator.destroy(self.arena); 175 } 176 177 pub fn contains(self: *CommandArgs, arg: []const u8) bool { 178 _ = self.unused_args.orderedRemove(arg); 179 return self.map_args.contains(arg); 180 } 181 182 pub fn get(self: *CommandArgs, comptime arg: []const u8) ??[]const u8 { 183 comptime std.debug.assert(value_flags.has(arg)); // can only call `get` with flags included in `value_flags` 184 _ = self.unused_args.orderedRemove(arg); 185 return self.map_args.get(arg); 186 } 187 }; 188 189 /// parses the args into a format that can be directly used by a repo. 190 /// if any additional allocation needs to be done, the arena inside the cmd args will be used. 191 pub const Command = union(CommandKind) { 192 serve: struct { 193 http_listen: []const u8, 194 ssh_listen: ?[]const u8, 195 data_dir: []const u8, 196 ui: bool, 197 }, 198 ssh_helper: struct { 199 ssh_connect: []const u8, 200 service: ?[]const u8, 201 dir: ?[]const u8, 202 }, 203 204 pub fn initMaybe(cmd_args: *CommandArgs) !?Command { 205 const command_kind = cmd_args.command_kind orelse return null; 206 switch (command_kind) { 207 .serve => { 208 if (cmd_args.positional_args.len != 0) return null; 209 210 return .{ .serve = .{ 211 .http_listen = (cmd_args.get("--http-listen") orelse null) orelse "127.0.0.1:8080", 212 .ssh_listen = (cmd_args.get("--ssh-listen") orelse null), 213 .data_dir = (cmd_args.get("--data-dir") orelse null) orelse ".", 214 .ui = cmd_args.contains("--ui"), 215 } }; 216 }, 217 .ssh_helper => { 218 if (cmd_args.positional_args.len > 1) return null; 219 220 return .{ .ssh_helper = .{ 221 .ssh_connect = (cmd_args.get("--ssh-connect") orelse null) orelse "127.0.0.1:8081", 222 .service = (cmd_args.get("--service") orelse null), 223 .dir = if (cmd_args.positional_args.len == 1) cmd_args.positional_args[0] else null, 224 } }; 225 }, 226 } 227 } 228 }; 229 230 /// parses the given args into a command if valid, and determines how it should be run 231 /// (via the TUI or CLI). 232 pub const CommandDispatch = union(enum) { 233 invalid: union(enum) { 234 command: []const u8, 235 argument: struct { 236 command: ?CommandKind, 237 value: []const u8, 238 }, 239 }, 240 help: ?CommandKind, 241 cli: Command, 242 243 pub fn init(cmd_args: *CommandArgs) !CommandDispatch { 244 const dispatch = try initIgnoreUnused(cmd_args); 245 if (cmd_args.unused_args.count() > 0) { 246 return .{ 247 .invalid = .{ 248 .argument = .{ 249 .command = switch (dispatch) { 250 .invalid => return dispatch, // if there was already an error, return it instead 251 .help => |cmd_kind_maybe| cmd_kind_maybe, 252 .cli => |command| command, 253 }, 254 .value = cmd_args.unused_args.keys()[0], 255 }, 256 }, 257 }; 258 } 259 return dispatch; 260 } 261 262 pub fn initIgnoreUnused(cmd_args: *CommandArgs) !CommandDispatch { 263 const show_help = cmd_args.contains("--help"); 264 265 if (cmd_args.command_kind) |command_kind| { 266 if (show_help) { 267 return .{ .help = command_kind }; 268 } else if (try Command.initMaybe(cmd_args)) |cmd| { 269 return .{ .cli = cmd }; 270 } else { 271 return .{ .help = command_kind }; 272 } 273 } else if (cmd_args.command_name) |command_name| { 274 return .{ .invalid = .{ .command = command_name } }; 275 } else if (show_help) { 276 return .{ .help = null }; 277 } else { 278 return .{ .help = null }; 279 } 280 } 281 }; 282