main.zig (8772B)
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 4 const options = @import("options"); 5 const ziggy = @import("ziggy"); 6 7 const environment = @import("environment.zig"); 8 const stats = @import("stats.zig"); 9 const sys_info = @import("sys_info.zig"); 10 11 const padding: u8 = 9; 12 13 const Widgets = enum { 14 distro, 15 uptime, 16 kernel, 17 desktop, 18 shell, 19 memory, 20 battery, 21 cpu, 22 disk, 23 }; 24 25 const Config = struct { 26 widgets: []const Widgets, 27 }; 28 29 const Options = struct { 30 help: bool = false, 31 version: bool = false, 32 33 fn optKind(a: []const u8) enum { short, long, positional } { 34 if (std.mem.startsWith(u8, a, "--")) return .long; 35 if (std.mem.startsWith(u8, a, "-")) return .short; 36 return .positional; 37 } 38 }; 39 40 const CONFIG_NAME = "config.ziggy"; 41 const HOME_DIR_NAME = ".lens"; 42 const XDG_CONFIG_HOME_DIR_NAME = "lens"; 43 const default_config = Config{ 44 .widgets = &[_]Widgets{ 45 .distro, 46 .uptime, 47 .kernel, 48 .desktop, 49 .shell, 50 .memory, 51 .battery, 52 .cpu, 53 .disk, 54 }, 55 }; 56 const help_menu = 57 \\Usage: lens 58 \\ 59 \\a minimal posix system fetch tool 60 \\ 61 \\Flags: 62 \\ -h, --help Show help information and exit. 63 \\ -v, --version Print version information and exit. 64 \\ 65 ; 66 67 pub fn main(init: std.process.Init) !void { 68 const gpa = init.gpa; 69 const io = init.io; 70 71 var stdout_buffer: [1024]u8 = undefined; 72 var stdout_file_writer: std.Io.File.Writer = .init(.stdout(), io, &stdout_buffer); 73 const stdout_writer = &stdout_file_writer.interface; 74 75 var opts = Options{}; 76 var args = init.minimal.args.iterate(); 77 _ = args.skip(); 78 while (args.next()) |arg| { 79 switch (Options.optKind(arg)) { 80 .short => { 81 const str = arg[1..]; 82 for (str) |b| { 83 switch (b) { 84 'v' => opts.version = true, 85 'h' => opts.help = true, 86 else => { 87 std.log.err("Invalid option: '{c}'", .{b}); 88 std.process.exit(1); 89 }, 90 } 91 } 92 }, 93 .long => { 94 var split = std.mem.splitScalar(u8, arg[2..], '='); 95 const opt = split.first(); 96 if (std.mem.eql(u8, opt, "version")) { 97 opts.version = true; 98 } else if (std.mem.eql(u8, opt, "help")) { 99 opts.help = true; 100 } 101 }, 102 .positional => { 103 std.log.err("Invalid opt: '{s}'. lens does not take positional arguments.", .{arg}); 104 std.process.exit(1); 105 }, 106 } 107 } 108 109 if (opts.help) { 110 try stdout_writer.writeAll(help_menu); 111 try stdout_writer.flush(); 112 return; 113 } 114 115 if (opts.version) { 116 try stdout_writer.print("lens v{f}\n", .{options.version}); 117 try stdout_writer.flush(); 118 return; 119 } 120 121 var arena = std.heap.ArenaAllocator.init(gpa); 122 defer _ = arena.deinit(); 123 var allocator = arena.allocator(); 124 125 // Read config 126 const config = lbl: { 127 var config_dir = dir: { 128 var dir_env: struct { dir: std.Io.Dir, name: []const u8 } = env: { 129 if (try environment.getXdgConfigHomeDir(io, init.environ_map)) |home_dir| { 130 break :env .{ .dir = home_dir, .name = XDG_CONFIG_HOME_DIR_NAME }; 131 } 132 133 if (try environment.getHomeDir(io, init.environ_map)) |home_dir| { 134 break :env .{ .dir = home_dir, .name = HOME_DIR_NAME }; 135 } 136 137 break :lbl default_config; 138 }; 139 defer dir_env.dir.close(io); 140 141 if (!environment.dirExists(dir_env.dir, io, dir_env.name)) { 142 break :lbl default_config; 143 } 144 145 const dir = try dir_env.dir.openDir(io, dir_env.name, .{ .iterate = true }); 146 if (!environment.fileExists(dir, io, CONFIG_NAME)) { 147 break :lbl default_config; 148 } 149 150 break :dir dir; 151 }; 152 153 const contents = config_dir.readFileAlloc(io, CONFIG_NAME, allocator, .limited(4096)) catch break :lbl default_config; 154 defer allocator.free(contents); 155 const contentsZ = try allocator.dupeZ(u8, contents); 156 defer allocator.free(contentsZ); 157 158 var meta: ziggy.Deserializer.Meta = undefined; 159 break :lbl ziggy.deserializeLeaky(Config, allocator, contentsZ, &meta, .{}) catch default_config; 160 }; 161 162 // Print header 163 const user_env = try sys_info.getUser(allocator, init.environ_map); 164 defer allocator.free(user_env); 165 const distro_id = try sys_info.getDistroId(io, allocator); 166 defer allocator.free(distro_id); 167 168 var header_buf: [1024]u8 = undefined; 169 const header = try std.fmt.bufPrint(&header_buf, "{s}@{s}\n", .{ user_env, distro_id }); 170 171 try stdout_writer.writeAll(header); 172 try stdout_writer.print("{[str]s:-<[count]}\n", .{ .str = "-", .count = header.len + 1 }); 173 174 // Print widgets 175 for (config.widgets) |widget| { 176 switch (widget) { 177 .distro => { 178 const distro = sys_info.getDistro(io, allocator) catch continue; 179 defer allocator.free(distro); 180 181 try stdout_writer.print( 182 "{[header]s: <[padding]}{[stat]s}\n", 183 .{ .header = "distro", .stat = distro, .padding = padding }, 184 ); 185 }, 186 .uptime => { 187 const uptime = stats.getUptime() catch continue; 188 189 try stdout_writer.print( 190 "{[header]s: <[padding]}{[days]d}d {[hours]d}h {[minutes]d}m\n", 191 .{ 192 .header = "uptime", 193 .days = uptime.days, 194 .hours = uptime.hours, 195 .minutes = uptime.minutes, 196 .padding = padding, 197 }, 198 ); 199 }, 200 .kernel => { 201 const utsname = std.posix.uname(); 202 const release = std.mem.sliceTo(&utsname.release, 0); 203 204 try stdout_writer.print( 205 "{[header]s: <[padding]}{[stat]s}\n", 206 .{ .header = "kernel", .stat = release, .padding = padding }, 207 ); 208 }, 209 .desktop => { 210 const desktop = try sys_info.getDesktop(allocator, init.environ_map); 211 defer allocator.free(desktop); 212 213 try stdout_writer.print( 214 "{[header]s: <[padding]}{[stat]s}\n", 215 .{ .header = "desktop", .stat = desktop, .padding = padding }, 216 ); 217 }, 218 .shell => { 219 const shell = sys_info.getShell(io, allocator, init.environ_map) catch continue; 220 defer allocator.free(shell); 221 222 try stdout_writer.print( 223 "{[header]s: <[padding]}{[stat]s}\n", 224 .{ .header = "shell", .stat = shell, .padding = padding }, 225 ); 226 }, 227 .memory => { 228 const mem = stats.getMemory(io, allocator, .{ .mb = true }) catch continue; 229 defer allocator.free(mem); 230 231 try stdout_writer.print( 232 "{[header]s: <[padding]}{[stat]s}\n", 233 .{ .header = "memory", .stat = mem, .padding = padding }, 234 ); 235 }, 236 .battery => { 237 const battery = stats.getBattery(io, allocator) catch continue; 238 defer allocator.free(battery); 239 240 try stdout_writer.print( 241 "{[header]s: <[padding]}{[stat]s}\n", 242 .{ .header = "battery", .stat = battery, .padding = padding }, 243 ); 244 }, 245 .cpu => { 246 const cpu = sys_info.getCpu(io, allocator) catch continue; 247 defer allocator.free(cpu); 248 249 try stdout_writer.print( 250 "{[header]s: <[padding]}{[stat]s}\n", 251 .{ .header = "cpu", .stat = cpu, .padding = padding }, 252 ); 253 }, 254 .disk => { 255 const disk = sys_info.getDisk(io, allocator) catch continue; 256 defer allocator.free(disk); 257 258 try stdout_writer.print( 259 "{[header]s: <[padding]}{[stat]s}\n", 260 .{ .header = "disk", .stat = disk, .padding = padding }, 261 ); 262 }, 263 } 264 } 265 266 try stdout_writer.flush(); 267 }