╭ try.zig ──────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1 //! start a server with some fake data and then launch 2 //! the TUI. this provides a nice way to test things 3 //! out safely. 4 5 const std = @import("std"); 6 const hx = @import("haxy"); 7 const srv = hx.serve; 8 const evt = hx.event; 9 const xit = hx.xit; 10 const rp = xit.repo; 11 const ui = hx.ui; 12 13 pub fn main(init: std.process.Init) !void { 14 var gpa: std.heap.DebugAllocator(.{}) = .init; 15 defer _ = gpa.deinit(); 16 const allocator = gpa.allocator(); 17 18 var threaded = std.Io.Threaded.init(allocator, .{ 19 .environ = init.minimal.environ, 20 }); 21 defer threaded.deinit(); 22 const io = threaded.io(); 23 24 const temp_dir_name = "temp-try"; 25 26 // create the temp dir 27 const cwd = std.Io.Dir.cwd(); 28 var temp_dir_or_err = cwd.openDir(io, temp_dir_name, .{}); 29 if (temp_dir_or_err) |*temp_dir| { 30 temp_dir.close(io); 31 try cwd.deleteTree(io, temp_dir_name); 32 } else |_| {} 33 var temp_dir = try cwd.createDirPathOpen(io, temp_dir_name, .{}); 34 defer cwd.deleteTree(io, temp_dir_name) catch {}; 35 defer temp_dir.close(io); 36 37 const cwd_path = try std.process.currentPathAlloc(io, allocator); 38 defer allocator.free(cwd_path); 39 40 var page_arena = std.heap.ArenaAllocator.init(allocator); 41 defer page_arena.deinit(); 42 43 const work_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "server", "admin" }); 44 defer allocator.free(work_path); 45 46 const repo_opts: rp.RepoOpts(.xit) = .{ .is_test = true }; 47 const Repo = rp.Repo(.xit, repo_opts); 48 var repo = try Repo.init(io, allocator, .{ .path = work_path }); 49 defer repo.deinit(io, allocator); 50 51 var session: ui.Session = .{}; 52 53 const page: ui.Page = blk: { 54 var arena = std.heap.ArenaAllocator.init(allocator); 55 defer arena.deinit(); 56 57 // define test events 58 59 var prng = std.Random.DefaultPrng.init(std.testing.random_seed); 60 61 const user_data = [_]struct { 62 name: []const u8, 63 display_name: []const u8, 64 email: []const u8, 65 }{ 66 .{ .name = "alice", .display_name = "Alice Tulley", .email = "alice@example.test" }, 67 .{ .name = "bob", .display_name = "Bob Smith", .email = "bob@example.test" }, 68 .{ .name = "carol", .display_name = "Carol Johnson", .email = "carol@example.test" }, 69 .{ .name = "dave", .display_name = "Dave Wilson", .email = "dave@example.test" }, 70 .{ .name = "eve", .display_name = "Eve Anderson", .email = "eve@example.test" }, 71 .{ .name = "frank", .display_name = "Frank Miller", .email = "frank@example.test" }, 72 .{ .name = "grace", .display_name = "Grace Lee", .email = "grace@example.test" }, 73 .{ .name = "henry", .display_name = "Henry Davis", .email = "henry@example.test" }, 74 .{ .name = "ivy", .display_name = "Ivy Martinez", .email = "ivy@example.test" }, 75 .{ .name = "jack", .display_name = "Jack Thompson", .email = "jack@example.test" }, 76 .{ .name = "kate", .display_name = "Kate Robinson", .email = "kate@example.test" }, 77 .{ .name = "liam", .display_name = "Liam Walker", .email = "liam@example.test" }, 78 .{ .name = "mona", .display_name = "Mona Patel", .email = "mona@example.test" }, 79 .{ .name = "noah", .display_name = "Noah Garcia", .email = "noah@example.test" }, 80 .{ .name = "olivia", .display_name = "Olivia Hernandez", .email = "olivia@example.test" }, 81 .{ .name = "peter", .display_name = "Peter Wright", .email = "peter@example.test" }, 82 .{ .name = "quinn", .display_name = "Quinn Foster", .email = "quinn@example.test" }, 83 .{ .name = "rachel", .display_name = "Rachel Bennett", .email = "rachel@example.test" }, 84 .{ .name = "sam", .display_name = "Sam Brooks", .email = "sam@example.test" }, 85 .{ .name = "tina", .display_name = "Tina Cooper", .email = "tina@example.test" }, 86 }; 87 88 const repo_data = [_]struct { 89 user_index: usize, 90 name: []const u8, 91 description: []const u8, 92 }{ 93 .{ .user_index = 0, .name = "ziglings", .description = "Learn the Zig programming language by fixing tiny broken programs" }, 94 .{ .user_index = 1, .name = "linux", .description = "Linux kernel source tree" }, 95 .{ .user_index = 2, .name = "kubernetes", .description = "Production-grade container orchestration" }, 96 .{ .user_index = 3, .name = "react", .description = "A declarative, efficient, and flexible JavaScript library for building user interfaces" }, 97 .{ .user_index = 4, .name = "typescript", .description = "TypeScript is a superset of JavaScript that compiles to clean JavaScript output" }, 98 .{ .user_index = 5, .name = "rust", .description = "Empowering everyone to build reliable and efficient software" }, 99 .{ .user_index = 6, .name = "go", .description = "The Go programming language" }, 100 .{ .user_index = 7, .name = "nodejs", .description = "Node.js JavaScript runtime" }, 101 .{ .user_index = 8, .name = "cpython", .description = "The Python programming language" }, 102 .{ .user_index = 9, .name = "docker", .description = "Container platform for developing, shipping, and running applications" }, 103 .{ .user_index = 0, .name = "vim", .description = "The ubiquitous text editor" }, 104 .{ .user_index = 1, .name = "neovim", .description = "Hyperextensible Vim-based text editor" }, 105 .{ .user_index = 2, .name = "emacs", .description = "GNU Emacs source code mirror" }, 106 .{ .user_index = 3, .name = "tmux", .description = "Terminal multiplexer" }, 107 .{ .user_index = 4, .name = "zsh", .description = "Mirror of the Z shell source code repository" }, 108 .{ .user_index = 5, .name = "git", .description = "Distributed version control system" }, 109 .{ .user_index = 6, .name = "mercurial", .description = "Source-control management tool" }, 110 .{ .user_index = 7, .name = "tensorflow", .description = "An end-to-end open source machine learning platform" }, 111 .{ .user_index = 8, .name = "pytorch", .description = "Tensors and dynamic neural networks in Python with strong GPU acceleration" }, 112 .{ .user_index = 9, .name = "numpy", .description = "The fundamental package for scientific computing with Python" }, 113 .{ .user_index = 0, .name = "pandas", .description = "Flexible and powerful data analysis and manipulation library for Python" }, 114 .{ .user_index = 1, .name = "scikit-learn", .description = "Machine learning in Python" }, 115 .{ .user_index = 2, .name = "nginx", .description = "High performance HTTP server and reverse proxy" }, 116 .{ .user_index = 3, .name = "redis", .description = "In-memory data structure store, used as a database, cache, and message broker" }, 117 .{ .user_index = 4, .name = "postgres", .description = "The world's most advanced open source relational database" }, 118 .{ .user_index = 5, .name = "sqlite", .description = "Self-contained, serverless, zero-configuration SQL database engine" }, 119 .{ .user_index = 6, .name = "mongodb", .description = "The MongoDB Database" }, 120 .{ .user_index = 7, .name = "elasticsearch", .description = "Free and open, distributed, RESTful search engine" }, 121 .{ .user_index = 8, .name = "kafka", .description = "Distributed event streaming platform" }, 122 .{ .user_index = 9, .name = "terraform", .description = "Infrastructure as code tool" }, 123 .{ .user_index = 10, .name = "svelte", .description = "Cybernetically enhanced web apps" }, 124 .{ .user_index = 11, .name = "vue", .description = "The progressive JavaScript framework" }, 125 .{ .user_index = 12, .name = "flask", .description = "The Python micro framework for building web applications" }, 126 .{ .user_index = 13, .name = "django", .description = "The web framework for perfectionists with deadlines" }, 127 .{ .user_index = 14, .name = "rails", .description = "Ruby on Rails web framework" }, 128 .{ .user_index = 15, .name = "phoenix", .description = "Peace of mind from prototype to production for Elixir web apps" }, 129 .{ .user_index = 16, .name = "laravel", .description = "The PHP framework for web artisans" }, 130 .{ .user_index = 17, .name = "prometheus", .description = "The Prometheus monitoring system and time series database" }, 131 .{ .user_index = 18, .name = "grafana", .description = "The open and composable observability and data visualization platform" }, 132 .{ .user_index = 19, .name = "ansible", .description = "Simple, agentless IT automation" }, 133 }; 134 135 var user_ids: [user_data.len][evt.event_id_size]u8 = undefined; 136 for (&user_ids) |*id| id.* = evt.EventWithId.randomId(prng.random()); 137 138 var repo_event_ids: [repo_data.len][evt.event_id_size]u8 = undefined; 139 for (&repo_event_ids) |*id| id.* = evt.EventWithId.randomId(prng.random()); 140 141 var password_hash_buf: [evt.User.password_hash_max_len]u8 = undefined; 142 const password_hash = try evt.User.hashPassword("password", &password_hash_buf, io); 143 144 var events_to_consume: [user_data.len + repo_data.len]evt.EventWithId = undefined; 145 for (user_data, 0..) |u, i| { 146 events_to_consume[i] = .{ 147 .id = std.fmt.bytesToHex(user_ids[i], .lower), 148 .event = .{ 149 .user = .{ 150 .name = u.name, 151 .display_name = u.display_name, 152 .email = u.email, 153 .password_hash = password_hash, 154 }, 155 }, 156 }; 157 } 158 for (repo_data, 0..) |r, i| { 159 events_to_consume[user_data.len + i] = .{ 160 .id = std.fmt.bytesToHex(repo_event_ids[i], .lower), 161 .event = .{ 162 .repo = .{ 163 .user_id = &user_ids[r.user_index], 164 .name = r.name, 165 .description = r.description, 166 .enable_issue = true, 167 }, 168 }, 169 }; 170 } 171 172 // insert users and repos as commits in the repo 173 { 174 var json: std.Io.Writer.Allocating = .init(allocator); 175 defer json.deinit(); 176 177 for (events_to_consume) |event| { 178 json.clearRetainingCapacity(); 179 180 try std.json.Stringify.value(event, .{}, &json.writer); 181 182 // commit the event into a special branch 183 _ = try repo.commitAtRef(io, allocator, .{ .message = json.written() }, null, .{ .kind = .head, .name = "haxy/meta" }); 184 } 185 } 186 187 // consume events into the database 188 try evt.consume(repo_opts, io, allocator, &repo, .{ .kind = .head, .name = "haxy/meta" }); 189 190 break :blk .{ .home = try .init(repo_opts, &page_arena, &repo, &session) }; 191 }; 192 193 // start the server 194 195 var cli = false; 196 197 var arg_it = try init.minimal.args.iterateAllocator(allocator); 198 defer arg_it.deinit(); 199 _ = arg_it.skip(); 200 while (arg_it.next()) |arg| { 201 if (std.mem.eql(u8, "--cli", arg)) { 202 cli = true; 203 } 204 } 205 206 const server_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "server" }); 207 defer allocator.free(server_path); 208 209 if (cli) { 210 var stdout_writer = std.Io.File.stdout().writer(io, &.{}); 211 var stderr_writer = std.Io.File.stderr().writer(io, &.{}); 212 const run_opts = hx.main.RunOpts{ .out = &stdout_writer.interface, .err = &stderr_writer.interface }; 213 214 const Runnable = struct { 215 io: std.Io, 216 217 pub fn run(self: @This()) !void { 218 std.debug.print( 219 \\ 220 \\connect to the TUI with: 221 \\ ssh -p 8022 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null localhost 222 \\ 223 \\create a git repo and push over SSH: 224 \\ mkdir -p temp-try/client/test 225 \\ cd temp-try/client/test 226 \\ git init 227 \\ echo "hello" > hello.txt 228 \\ git add hello.txt 229 \\ git commit -m "let there be light" 230 \\ GIT_SSH_COMMAND='ssh -p 8022 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' git push localhost:test HEAD:master 231 \\ 232 \\to quit, press enter. 233 \\ 234 , .{}); 235 // portable stdin read via std.Io — equivalent to the 236 // std.posix.read(STDIN_FILENO, ...) that doesn't compile 237 // on windows. 238 var buf: [1]u8 = undefined; 239 var stdin_reader = std.Io.File.stdin().reader(self.io, &buf); 240 _ = stdin_reader.interface.takeByte() catch {}; 241 } 242 }; 243 244 try srv.run(.xit, .{}, io, allocator, cwd_path, .{ 245 .data_dir = server_path, 246 }, run_opts.err, Runnable{ .io = io }); 247 } else { 248 var null_writer = std.Io.Writer.Discarding.init(&.{}); 249 const run_opts = hx.main.RunOpts{ .out = &null_writer.writer, .err = &null_writer.writer }; 250 251 const Runnable = struct { 252 io: std.Io, 253 allocator: std.mem.Allocator, 254 page: *const ui.Page, 255 session: *ui.Session, 256 257 pub fn run(self: @This()) !void { 258 // launch the TUI 259 try hx.ui.run(self.io, self.allocator, self.page, self.session); 260 } 261 }; 262 263 try srv.run(.xit, .{}, io, allocator, cwd_path, .{ 264 .data_dir = server_path, 265 }, run_opts.err, Runnable{ .io = io, .allocator = allocator, .page = &page, .session = &session }); 266 } 267 } 268