stats.zig (7211B)
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 4 pub fn getBattery(io: std.Io, allocator: std.mem.Allocator) ![]const u8 { 5 if (builtin.os.tag != .linux and builtin.os.tag != .openbsd) return error.UnsupportedOs; 6 7 var dir = try std.Io.Dir.openDirAbsolute(io, "/sys/class/power_supply/", .{ .iterate = true }); 8 defer dir.close(io); 9 10 var smallest_battery: ?usize = null; 11 12 // Walk dir and find the smallest battery. 13 var it = try dir.walk(allocator); 14 defer it.deinit(); 15 while (try it.next(io)) |entry| { 16 if (std.mem.startsWith(u8, entry.basename, "BAT")) { 17 const battery_no = try std.fmt.parseInt(usize, std.mem.trimStart(u8, entry.basename, "BAT"), 10); 18 if (smallest_battery) |smallest| { 19 if (battery_no < smallest) smallest_battery = battery_no; 20 } else { 21 smallest_battery = battery_no; 22 } 23 } 24 } 25 26 if (smallest_battery == null) return error.NoBatteryDetected; 27 28 // Read battery capacity. 29 var battery_capacity_path_buf: [std.Io.Dir.max_path_bytes]u8 = undefined; 30 const battery_capacity_path = try std.fmt.bufPrint( 31 &battery_capacity_path_buf, 32 "/sys/class/power_supply/BAT{d}/capacity", 33 .{smallest_battery.?}, 34 ); 35 const battery_capacity_file = try std.Io.Dir.openFileAbsolute( 36 io, 37 battery_capacity_path, 38 .{ .mode = .read_only }, 39 ); 40 defer battery_capacity_file.close(io); 41 42 var battery_capacity_buf: [1024]u8 = undefined; 43 var battery_capacity_file_reader = battery_capacity_file.reader(io, &.{}); 44 45 const battery_capacity_bytes = try battery_capacity_file_reader.interface.readSliceShort(&battery_capacity_buf); 46 const battery_capacity = try std.fmt.parseInt( 47 usize, 48 std.mem.trim(u8, battery_capacity_buf[0..battery_capacity_bytes], "\n"), 49 10, 50 ); 51 52 // Read battery status. 53 var battery_status_path_buf: [std.Io.Dir.max_path_bytes]u8 = undefined; 54 const battery_status_path = try std.fmt.bufPrint( 55 &battery_status_path_buf, 56 "/sys/class/power_supply/BAT{d}/status", 57 .{smallest_battery.?}, 58 ); 59 const battery_status_file = try std.Io.Dir.openFileAbsolute( 60 io, 61 battery_status_path, 62 .{ .mode = .read_only }, 63 ); 64 defer battery_status_file.close(io); 65 66 var battery_status_buf: [1024]u8 = undefined; 67 var battery_status_file_reader = battery_status_file.reader(io, &.{}); 68 69 const battery_status_bytes = try battery_status_file_reader.interface.readSliceShort(&battery_status_buf); 70 const battery_status = std.mem.trim( 71 u8, 72 battery_status_buf[0..battery_status_bytes], 73 "\n", 74 ); 75 76 var output_buf: [1024]u8 = undefined; 77 const output = try std.fmt.bufPrint( 78 &output_buf, 79 "{d}% ({s})", 80 .{ battery_capacity, battery_status }, 81 ); 82 return try allocator.dupe(u8, output); 83 } 84 85 pub fn getMemory(io: std.Io, allocator: std.mem.Allocator, options: struct { mb: bool }) ![]const u8 { 86 if (builtin.os.tag != .linux and builtin.os.tag != .openbsd) return error.UnsupportedOs; 87 88 const file = try std.Io.Dir.openFileAbsolute(io, "/proc/meminfo", .{ .mode = .read_only }); 89 defer file.close(io); 90 91 var file_buffer: [1024]u8 = undefined; 92 var file_reader = file.reader(io, &file_buffer); 93 94 const dividend: usize = if (options.mb) 1000 else 1024; 95 const suffix = if (options.mb) "MB" else "MiB"; 96 97 const mem_total = lbl: { 98 const line = try file_reader.interface.takeDelimiter('\n') orelse return error.MemAvailableIsNotSpecified; 99 var line_it = std.mem.splitScalar(u8, line, ':'); 100 _ = line_it.next(); // Skip "MemTotal:" 101 const mem_total_with_suffix = line_it.next() orelse return error.MemTotalIsNull; 102 // TODO: Is it safe to assume the suffix will only ever be "kB"? 103 const mem_total_str = std.mem.trim(u8, mem_total_with_suffix, " \tkB"); 104 const mem_total_int = (try std.fmt.parseInt(usize, mem_total_str, 10)) / dividend; 105 break :lbl mem_total_int; 106 }; 107 108 _ = try file_reader.interface.takeDelimiter('\n'); // Skip "MemFree: x" 109 110 const mem_available = lbl: { 111 const line = try file_reader.interface.takeDelimiter('\n') orelse return error.MemAvailableIsNotSpecified; 112 var line_it = std.mem.splitScalar(u8, line, ':'); 113 _ = line_it.next(); // Skip "MemAvailable:" 114 const mem_available_with_suffix = line_it.next() orelse return error.MemAvailableIsNull; 115 // TODO: Is it safe to assume it'll only ever be "kB"? 116 const mem_available_str = std.mem.trim(u8, mem_available_with_suffix, " \tkB"); 117 const mem_available_int = (try std.fmt.parseInt(usize, mem_available_str, 10)) / dividend; 118 break :lbl mem_available_int; 119 }; 120 121 const mem_used = mem_total - mem_available; 122 const mem_percentage: usize = @intFromFloat( 123 (@as(f32, @floatFromInt(mem_used)) / @as(f32, @floatFromInt(mem_total))) * 100, 124 ); 125 126 var buf: [1024]u8 = undefined; 127 const mem_str = try std.fmt.bufPrint(&buf, "{d} / {d} {s} ({d}%)", .{ 128 mem_used, 129 mem_total, 130 suffix, 131 mem_percentage, 132 }); 133 return try allocator.dupe(u8, mem_str); 134 } 135 136 pub fn getUptime() !struct { days: isize, minutes: isize, hours: isize } { 137 switch (builtin.os.tag) { 138 .linux, .openbsd => { 139 var info: std.os.linux.Sysinfo = undefined; 140 const result: std.os.linux.E = @enumFromInt(std.os.linux.sysinfo(&info)); 141 if (result != .SUCCESS) { 142 return error.UnknownUptime; 143 } 144 const uptime = info.uptime; 145 146 const uptime_days = @divTrunc(uptime, 86400); 147 const uptime_hours = @divTrunc(@rem(uptime, 86400), 3600); 148 const uptime_minutes = @divTrunc(@rem(uptime, 3600), 60); 149 150 return .{ 151 .days = uptime_days, 152 .minutes = uptime_minutes, 153 .hours = uptime_hours, 154 }; 155 }, 156 // TODO: upgrade to 0.16.0 157 // .macos => { 158 // var boot_timeval: std.posix.timeval = undefined; 159 // var size: usize = @sizeOf(@TypeOf(boot_timeval)); 160 // const kern_boottime = [_]c_int{ 1, 21 }; // CTL_KERN, KERN_BOOTTIME return void on macos 161 // std.posix.sysctl( 162 // &kern_boottime, 163 // @ptrCast(&boot_timeval), 164 // &size, 165 // null, 166 // 0, 167 // ) catch return error.SysctlFailed; 168 // 169 // const now = std.time.timestamp(); 170 // const boot_time = boot_timeval.sec; 171 // const uptime = now - boot_time; 172 // 173 // const uptime_days = @divTrunc(uptime, 86400); 174 // const uptime_hours = @divTrunc(@rem(uptime, 86400), 3600); 175 // const uptime_minutes = @divTrunc(@rem(uptime, 3600), 60); 176 // 177 // return .{ 178 // .days = uptime_days, 179 // .minutes = uptime_minutes, 180 // .hours = uptime_hours, 181 // }; 182 // }, 183 else => return error.UnsupportedOs, 184 } 185 }