╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ main.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
34 var arg_it = try init.minimal.args.iterateAllocator(allocator); 35 defer arg_it.deinit(); 36 _ = arg_it.skip(); 37 while (arg_it.next()) |arg| { 38 try args.append(allocator, arg); 39 } 40 41 var stdout_writer = std.Io.File.stdout().writer(io, &.{}); 42 var stderr_writer = std.Io.File.stderr().writer(io, &.{}); 43 44 var environ_map = try std.process.Environ.createMap(init.minimal.environ, allocator); 45 defer environ_map.deinit(); 46 const global_config_path = try xit.config.globalConfigPath(io, allocator, &environ_map); 47 defer if (global_config_path) |path| allocator.free(path); 48 49 const run_opts = RunOpts{ 50 .out = &stdout_writer.interface, 51 .err = &stderr_writer.interface, 52 .global_config_path = global_config_path, 53 .color = std.mem.eql(u8, environ_map.get("NO_COLOR") orelse "", ""), 54 }; 55 56 const cwd_path = try std.process.currentPathAlloc(io, allocator); 57 defer allocator.free(cwd_path); 58 59 run(.xit, .{}, io, allocator, args.items, cwd_path, run_opts) catch |err| switch (err) { 60 error.HandledError => return 1, 61 else => |e| return e, 62 }; 63 64 return 0; 65 } 66 67 pub fn run( 68 comptime repo_kind: rp.RepoKind, 69 comptime any_repo_opts: rp.AnyRepoOpts(repo_kind), 70 io: std.Io, 71 allocator: std.mem.Allocator, 72 args: []const []const u8, 73 cwd_path: []const u8, 74 run_opts: RunOpts, 75 ) !void { 76 var cmd_args = try cmd.CommandArgs.init(allocator, args); 77 defer cmd_args.deinit(); 78 79 switch (try cmd.CommandDispatch.init(&cmd_args)) { 80 .invalid => |invalid| switch (invalid) { 81 .command => |command| { 82 try run_opts.err.print("\"{s}\" is not a valid command\n\n", .{command}); 83 try cmd.printHelp(null, run_opts.err); 84 return error.HandledError; 85 }, 86 .argument => |argument| { 87 try run_opts.err.print("\"{s}\" is not a valid argument\n\n", .{argument.value}); 88 try cmd.printHelp(argument.command, run_opts.err); 89 return error.HandledError; 90 }, 91 }, 92 .help => |cmd_kind_maybe| try cmd.printHelp(cmd_kind_maybe, run_opts.out), 93 .local => { 94 // session-lifetime allocations (the repo path, session state) 95 var session_arena = std.heap.ArenaAllocator.init(allocator); 96 defer session_arena.deinit(); 97 98 // probe both backends for a repo; the nearest one wins, so a repo 99 // isn't shadowed by one it is nested inside. only the work path 100 // and backend kind are kept; pages re-open the repo on each build. 101 const local: ui.RepoSource = blk: { 102 var found: ?ui.RepoSource = null; 103 inline for ([_]rp.RepoKind{ .git, .xit }) |probe_kind| { 104 if (rp.AnyRepo(probe_kind, .{}).open(io, allocator, .{ .path = cwd_path })) |repo| { 105 var opened = repo; 106 defer opened.deinit(io, allocator); 107 const work_path = switch (opened) { 108 inline else => |*r| r.core.work_path, 109 }; 110 const found_len = if (found) |f| f.path.len else 0; 111 if (work_path.len > found_len) { 112 found = .{ 113 .path = try session_arena.allocator().dupe(u8, work_path), 114 .repo_kind = probe_kind, 115 .global_config_path = run_opts.global_config_path, 116 }; 117 } 118 } else |err| switch (err) { 119 error.RepoNotFound => {}, 120 else => |e| return e, 121 } 122 } 123 break :blk found orelse { 124 try run_opts.err.print("no git or xit repo found in the current directory\n", .{}); 125 return error.HandledError; 126 }; 127 }; 128 129 var session = ui.Session{ 130 .arena = &session_arena, 131 .page_arena = &session_arena, 132 .io = io, 133 .local = local, 134 .is_terminal = true, 135 .color = run_opts.color, 136 .data = .{ 137 // the files root; local routes carry no identity 138 .current_page = .{ .repo_files = .{ .name = .{} } }, 139 .host_kind = .local, 140 }, 141 }; 142 143 // serve the web UI in the background while the TUI runs in the 144 // foreground; the footer points at whatever port it bound. 145 const Runnable = struct { 146 io: std.Io, 147 allocator: std.mem.Allocator, 148 session: *ui.Session, 149 150 pub fn run(self: @This(), web_port: u16) !void { 151 self.session.web_port = web_port; 152 try ui.run(self.io, self.allocator, self.session, null); 153 } 154 }; 155 156 try srv.runLocal(io, allocator, local, run_opts.err, Runnable{ .io = io, .allocator = allocator, .session = &session }); 157 }, 158 .cli => |cli_cmd| switch (cli_cmd) { 159 .serve => |options| try srv.run(repo_kind, any_repo_opts, io, allocator, cwd_path, options, run_opts.err, {}), 160 }, 161 } 162 } 163