╭ main.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 const xit = @import("xit"); 4 const rp = xit.repo; 5 const cmd = @import("./command.zig"); 6 const serve = @import("./serve.zig"); 7 8 pub const RunOpts = struct { 9 out: *std.Io.Writer, 10 err: *std.Io.Writer, 11 environ_map: *std.process.Environ.Map, 12 }; 13 14 pub fn main(init: std.process.Init) !u8 { 15 var debug_allocator: std.heap.DebugAllocator(.{}) = .init; 16 const allocator = if (builtin.mode == .Debug) debug_allocator.allocator() else std.heap.smp_allocator; 17 defer if (builtin.mode == .Debug) { 18 _ = debug_allocator.deinit(); 19 }; 20 21 var threaded = std.Io.Threaded.init(allocator, .{}); 22 defer threaded.deinit(); 23 const io = threaded.io(); 24 25 var args: std.ArrayList([]const u8) = .empty; 26 defer args.deinit(allocator); 27 28 var arg_it = try init.minimal.args.iterateAllocator(allocator); 29 defer arg_it.deinit(); 30 _ = arg_it.skip(); 31 while (arg_it.next()) |arg| { 32 try args.append(allocator, arg); 33 } 34 35 var stdout_writer = std.Io.File.stdout().writer(io, &.{}); 36 var stderr_writer = std.Io.File.stderr().writer(io, &.{}); 37 const run_opts = RunOpts{ .out = &stdout_writer.interface, .err = &stderr_writer.interface, .environ_map = init.environ_map }; 38 39 const cwd_path = try std.process.currentPathAlloc(io, allocator); 40 defer allocator.free(cwd_path); 41 42 run(.xit, .{}, io, allocator, args.items, cwd_path, run_opts) catch |err| switch (err) { 43 error.HandledError => return 1, 44 else => |e| return e, 45 }; 46 47 return 0; 48 } 49 50 pub fn run( 51 comptime repo_kind: rp.RepoKind, 52 comptime any_repo_opts: rp.AnyRepoOpts(repo_kind), 53 io: std.Io, 54 allocator: std.mem.Allocator, 55 args: []const []const u8, 56 cwd_path: []const u8, 57 run_opts: RunOpts, 58 ) !void { 59 var cmd_args = try cmd.CommandArgs.init(allocator, args); 60 defer cmd_args.deinit(); 61 62 switch (try cmd.CommandDispatch(repo_kind, any_repo_opts.toRepoOpts().hash).init(&cmd_args)) { 63 .invalid => |invalid| switch (invalid) { 64 .command => |command| { 65 try run_opts.err.print("\"{s}\" is not a valid command\n\n", .{command}); 66 try cmd.printHelp(null, run_opts.err); 67 return error.HandledError; 68 }, 69 .argument => |argument| { 70 try run_opts.err.print("\"{s}\" is not a valid argument\n\n", .{argument.value}); 71 try cmd.printHelp(argument.command, run_opts.err); 72 return error.HandledError; 73 }, 74 }, 75 .help => |cmd_kind_maybe| try cmd.printHelp(cmd_kind_maybe, run_opts.out), 76 .cli => |cli_cmd| { 77 if (cli_cmd == .serve) { 78 try serve.run(repo_kind, any_repo_opts, io, allocator, cwd_path, .{ 79 .http_listen = cli_cmd.serve.http_listen, 80 .project_root = cli_cmd.serve.project_root, 81 }, run_opts.err); 82 return; 83 } 84 85 // some commands allow the path to be specified. for all others, just use the cwd path. 86 const work_path = switch (cli_cmd) { 87 .upload_pack => |upload_pack| try std.fs.path.resolve(allocator, &.{ cwd_path, upload_pack.dir }), 88 .receive_pack => |receive_pack| try std.fs.path.resolve(allocator, &.{ cwd_path, receive_pack.dir }), 89 .http_backend => xit.net_server_http_backend.resolveDir(allocator, cwd_path, run_opts.environ_map) catch { 90 var http_stdout_buf: [any_repo_opts.buffer_size]u8 = undefined; 91 var http_stdout_writer = std.Io.File.stdout().writer(io, &http_stdout_buf); 92 try xit.net_server_http_backend.sendNotFound(&http_stdout_writer.interface); 93 return; 94 }, 95 .serve => unreachable, 96 }; 97 defer allocator.free(work_path); 98 99 if (any_repo_opts.hash) |hash_kind| { 100 var repo = try rp.Repo(repo_kind, any_repo_opts.toRepoOptsWithHash(hash_kind)).open(io, allocator, .{ .path = work_path }); 101 defer repo.deinit(io, allocator); 102 try runCommand(repo_kind, any_repo_opts.toRepoOptsWithHash(hash_kind), &repo, io, allocator, cli_cmd, run_opts); 103 } else { 104 // if no hash was specified, use AnyRepo to detect the hash being used 105 var any_repo = try rp.AnyRepo(repo_kind, any_repo_opts).open(io, allocator, .{ .path = work_path }); 106 defer any_repo.deinit(io, allocator); 107 switch (any_repo) { 108 inline else => |*repo| { 109 const cmd_maybe = try cmd.Command(repo.self_repo_kind, repo.self_repo_opts.hash).initMaybe(&cmd_args); 110 try runCommand(repo.self_repo_kind, repo.self_repo_opts, repo, io, allocator, cmd_maybe orelse return error.InvalidCommand, run_opts); 111 }, 112 } 113 } 114 }, 115 } 116 } 117 118 fn runCommand( 119 comptime repo_kind: rp.RepoKind, 120 comptime repo_opts: rp.RepoOpts(repo_kind), 121 repo: *rp.Repo(repo_kind, repo_opts), 122 io: std.Io, 123 allocator: std.mem.Allocator, 124 command: cmd.Command(repo_kind, repo_opts.hash), 125 run_opts: RunOpts, 126 ) !void { 127 switch (command) { 128 .upload_pack => |upload_pack_cmd| { 129 var options = upload_pack_cmd.options; 130 options.protocol_version = xit.net_server_common.detectProtocolVersion(run_opts.environ_map); 131 var stdin_buf: [repo_opts.net_buffer_size]u8 = undefined; 132 var stdin_reader = std.Io.File.stdin().reader(io, &stdin_buf); 133 var stdout_buf: [repo_opts.net_buffer_size]u8 = undefined; 134 var stdout_writer = std.Io.File.stdout().writer(io, &stdout_buf); 135 try repo.uploadPack(io, allocator, &stdin_reader.interface, &stdout_writer.interface, options); 136 }, 137 .receive_pack => |receive_pack_cmd| { 138 var options = receive_pack_cmd.options; 139 options.protocol_version = xit.net_server_common.detectProtocolVersion(run_opts.environ_map); 140 var stdin_buf: [repo_opts.net_buffer_size]u8 = undefined; 141 var stdin_reader = std.Io.File.stdin().reader(io, &stdin_buf); 142 var stdout_buf: [repo_opts.net_buffer_size]u8 = undefined; 143 var stdout_writer = std.Io.File.stdout().writer(io, &stdout_buf); 144 try repo.receivePack(io, allocator, &stdin_reader.interface, &stdout_writer.interface, options); 145 }, 146 .http_backend => { 147 const environ_map = run_opts.environ_map; 148 var path_buf: [std.Io.Dir.max_path_bytes]u8 = undefined; 149 const path = try xit.net_server_http_backend.resolveRepoPath(environ_map, &path_buf); 150 151 var stdout_buf: [repo_opts.net_buffer_size]u8 = undefined; 152 var stdout_writer = std.Io.File.stdout().writer(io, &stdout_buf); 153 154 const handler, const suffix = for (&xit.net_server_http_backend.routes) |*svc| { 155 if (std.mem.endsWith(u8, path, svc.suffix)) 156 break .{ svc.handler, svc.suffix }; 157 } else { 158 try xit.net_server_http_backend.sendNotFound(&stdout_writer.interface); 159 return; 160 }; 161 162 const request_method: std.http.Method = blk: { 163 const method_str = environ_map.get("REQUEST_METHOD") orelse break :blk .GET; 164 const method = std.meta.stringToEnum(std.http.Method, method_str) orelse break :blk .GET; 165 break :blk if (method == .HEAD) .GET else method; 166 }; 167 168 var stdin_buf: [repo_opts.net_buffer_size]u8 = undefined; 169 var stdin_reader = std.Io.File.stdin().reader(io, &stdin_buf); 170 try repo.httpBackend(io, allocator, &stdin_reader.interface, &stdout_writer.interface, .cgi, .{ 171 .request_method = request_method, 172 .handler = handler, 173 .suffix = suffix, 174 .query_string = environ_map.get("QUERY_STRING") orelse "", 175 .content_type = environ_map.get("CONTENT_TYPE") orelse "", 176 .has_remote_user = environ_map.get("REMOTE_USER") != null, 177 .protocol_version = xit.net_server_common.detectProtocolVersion(environ_map), 178 }); 179 }, 180 .serve => unreachable, 181 } 182 } 183