lens

posix system fetch tool
git clone git://brookjeynes.dev/bjeynes/lens.git
Log | Files | Refs | README

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 }