master
1package get
2
3/*
4#cgo linux LDFLAGS: ./lib/libsystem.a
5#cgo darwin LDFLAGS: ./lib/libsystem.a -framework IOKit
6#cgo windows LDFLAGS: ./lib/libsystem.a
7#include "../../lib/system.h"
8#include <stdlib.h>
9*/
10import "C"
11import (
12 "fmt"
13 "math"
14 "os/user"
15 "strconv"
16 "strings"
17
18 "github.com/distatus/battery"
19 "github.com/shirou/gopsutil/v4/cpu"
20
21 "github.com/kahnwong/swissknife/configs/color"
22 "github.com/shirou/gopsutil/v4/disk"
23 "github.com/shirou/gopsutil/v4/host"
24 "github.com/shirou/gopsutil/v4/mem"
25)
26
27type batteryStruct struct {
28 BatteryCurrent float64
29 BatteryFull float64
30 BatteryDesignCapacity float64
31 BatteryCycleCount uint64
32 BatteryTimeToEmpty uint64
33}
34
35func SysInfo() error {
36 // host
37 username, err := getUsername()
38 if err != nil {
39 return err
40 }
41 hostInfo, err := getHostInfo()
42 if err != nil {
43 return err
44 }
45 fmt.Printf("%s@%s\n", color.Green(username), color.Green(hostInfo.Hostname))
46 fmt.Println(strings.Repeat("-", len(username)+len(hostInfo.Hostname)+1))
47
48 // os
49 fmt.Printf("%s: %s %s\n", color.Green("OS"), hostInfo.Platform, hostInfo.PlatformVersion)
50
51 // cpu
52 cpuModel, cpuThreads, err := getCpuInfo()
53 if err != nil {
54 return err
55 }
56 fmt.Printf("%s: %s (%v)\n", color.Green("CPU"), cpuModel, cpuThreads)
57
58 // memory
59 memoryUsed, memoryTotal, memoryUsedPercent, err := getMemoryInfo()
60 if err != nil {
61 return err
62 }
63 fmt.Printf("%s: %.2f GB / %v GB (%s)\n",
64 color.Green("Memory"),
65 convertKBtoGB(memoryUsed, true), convertKBtoGB(memoryTotal, false),
66 color.Blue(strconv.Itoa(memoryUsedPercent)+"%"),
67 )
68
69 // disk
70 diskUsed, diskTotal, diskUsedPercent, err := getDiskInfo()
71 if err != nil {
72 return err
73 }
74 fmt.Printf("%s: %v GB / %v GB (%s)\n",
75 color.Green("Disk"),
76 convertKBtoGiB(diskUsed), convertKBtoGiB(diskTotal),
77 color.Blue(strconv.Itoa(diskUsedPercent)+"%"),
78 )
79
80 // battery
81 batteryInfo, err := getBatteryInfo()
82 if err != nil {
83 return err
84 }
85
86 // only print battery info if is a laptop
87 if batteryInfo.BatteryFull > 0 {
88 batteryPercent := convertToPercent(batteryInfo.BatteryCurrent / batteryInfo.BatteryFull)
89 batteryHealth := convertToPercent(batteryInfo.BatteryFull / batteryInfo.BatteryDesignCapacity)
90 batteryFormat := fmt.Sprintf("%v%%", batteryPercent)
91 var batteryPercentStr string
92 if batteryPercent > 80 {
93 batteryPercentStr = color.Green(batteryFormat)
94 } else if batteryPercent > 60 {
95 batteryPercentStr = color.Yellow(batteryFormat)
96 } else if batteryPercent > 30 {
97 batteryPercentStr = color.Red(batteryFormat)
98 } else {
99 batteryPercentStr = batteryFormat
100 }
101
102 // convert BatteryTimeToEmpty from second to hour
103 var batteryTimeToEmptyFormatted string
104 if batteryInfo.BatteryTimeToEmpty > 0 {
105 hours := batteryInfo.BatteryTimeToEmpty / 3600
106 minutes := (batteryInfo.BatteryTimeToEmpty % 3600) / 60
107 batteryTimeToEmptyFormatted = fmt.Sprintf("%02d:%02d", hours, minutes)
108 } else {
109 batteryTimeToEmptyFormatted = "--:--"
110 }
111
112 fmt.Printf(
113 "%s: %s (Health: %s, Cycles: %s, Time Remaining: %s)\n",
114 color.Green("Battery"), batteryPercentStr,
115 color.Blue(strconv.Itoa(batteryHealth)+"%"),
116 color.Blue(strconv.Itoa(int(batteryInfo.BatteryCycleCount))),
117 color.Blue(batteryTimeToEmptyFormatted),
118 )
119 }
120 return nil
121}
122
123// ---- functions ----
124func getUsername() (string, error) {
125 username, err := user.Current()
126 if err != nil {
127 return "", fmt.Errorf("failed to get current user info: %w", err)
128 }
129 return username.Username, nil
130}
131
132func getHostInfo() (*host.InfoStat, error) {
133 hostStat, err := host.Info()
134 if err != nil {
135 return nil, fmt.Errorf("failed to get host info: %w", err)
136 }
137 return hostStat, nil
138}
139
140func getCpuInfo() (string, int, error) {
141 cpuStat, err := cpu.Info()
142 if err != nil {
143 return "", 0, fmt.Errorf("failed to get cpu info: %w", err)
144 }
145 cpuThreads, err := cpu.Counts(true)
146 if err != nil {
147 return "", 0, fmt.Errorf("failed to get cpu threads info: %w", err)
148 }
149
150 return cpuStat[0].ModelName, cpuThreads, nil
151}
152
153func getMemoryInfo() (uint64, uint64, int, error) {
154 vmStat, err := mem.VirtualMemory()
155 if err != nil {
156 return 0, 0, 0, fmt.Errorf("failed to get memory info: %w", err)
157 }
158 return vmStat.Used, vmStat.Total, convertToPercent(float64(vmStat.Used) / float64(vmStat.Total)), nil
159}
160
161func getDiskInfo() (uint64, uint64, int, error) {
162 diskStat, err := disk.Usage("/")
163 if err != nil {
164 return 0, 0, 0, fmt.Errorf("failed to get disk info: %w", err)
165 }
166 return diskStat.Used, diskStat.Total, convertToPercent(float64(diskStat.Used) / float64(diskStat.Total)), nil
167}
168
169func getBatteryInfo() (batteryStruct, error) {
170 // battery
171 batteries, err := battery.GetAll()
172 if err != nil {
173 if !strings.Contains(err.Error(), "no such file or directory") {
174 return batteryStruct{}, fmt.Errorf("error getting battery info: %w", err)
175 }
176 // ignore this happens on [linux on mac devices]
177 }
178
179 //// charge stats
180 var batteryCurrent float64
181 var batteryFull float64
182 var batteryDesignCapacity float64
183 if len(batteries) > 0 {
184 batteryCurrent = batteries[0].Current
185 batteryFull = batteries[0].Full
186 batteryDesignCapacity = batteries[0].Design
187 }
188
189 //// cycle count
190 var batteryCycleCount uint64
191 result := C.battery_cycle_count()
192
193 switch result.error {
194 case C.BATTERY_SUCCESS:
195 batteryCycleCount = uint64(result.cycle_count)
196 case C.BATTERY_NO_BATTERY:
197 batteryCycleCount = 0
198 case C.BATTERY_NO_CYCLE_COUNT:
199 batteryCycleCount = 0
200 case C.BATTERY_MANAGER_ERROR:
201 return batteryStruct{}, fmt.Errorf("battery manager error")
202 default:
203 return batteryStruct{}, fmt.Errorf("unknown error occurred")
204 }
205
206 //// time to empty
207 var batteryTimeToEmpty uint64
208 resultTimeToEmpty := C.battery_time_to_empty()
209
210 switch result.error {
211 case C.BATTERY_TIME_TO_EMPTY_SUCCESS:
212 batteryTimeToEmpty = uint64(resultTimeToEmpty.time_to_empty_seconds)
213 case C.BATTERY_TIME_TO_EMPTY_NO_BATTERY:
214 batteryTimeToEmpty = 0
215 case C.BATTERY_TIME_TO_EMPTY_NO_TIME_TO_EMPTY:
216 batteryTimeToEmpty = 0
217 case C.BATTERY_TIME_TO_EMPTY_MANAGER_ERROR:
218 return batteryStruct{}, fmt.Errorf("battery manager error")
219 default:
220 return batteryStruct{}, fmt.Errorf("unknown error occurred")
221 }
222
223 // return
224 return batteryStruct{
225 BatteryCurrent: batteryCurrent,
226 BatteryFull: batteryFull,
227 BatteryDesignCapacity: batteryDesignCapacity,
228 BatteryCycleCount: batteryCycleCount,
229 BatteryTimeToEmpty: batteryTimeToEmpty,
230 }, nil
231}
232
233// ---- utils ----
234func convertKBtoGiB(v uint64) float64 {
235 return math.Round(float64(v)/float64(1024)/float64(1024)/float64(1024)*100) / 100
236}
237
238func convertKBtoGB(v uint64, isFloat bool) float64 {
239 gb := (float64(v) / float64(1024) / float64(1024) / float64(1000) * 100) / 100
240 if isFloat {
241 return gb
242 } else {
243 return math.Round(gb)
244 }
245}
246
247func convertToPercent(v float64) int {
248 return int(math.Round(v * 100))
249}