mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-08-16 22:31:03 +02:00
Merge pull request #831 from JakeStanger/feat/sysinfo-overhaul
feat(sysinfo): overhaul to add aggregate/unit/formatting support
This commit is contained in:
commit
7c7b6c7323
14 changed files with 1633 additions and 589 deletions
105
Cargo.lock
generated
105
Cargo.lock
generated
|
@ -472,9 +472,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.4"
|
version = "0.8.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
|
@ -1392,15 +1392,6 @@ version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.2.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -1565,7 +1556,7 @@ dependencies = [
|
||||||
"iana-time-zone-haiku",
|
"iana-time-zone-haiku",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-core",
|
"windows-core 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1664,7 +1655,7 @@ version = "1.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.3.9",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
@ -1964,7 +1955,7 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
|
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.3.9",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
@ -2168,16 +2159,6 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_cpus"
|
|
||||||
version = "1.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi 0.2.6",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.30.3"
|
version = "0.30.3"
|
||||||
|
@ -2519,9 +2500,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.7.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"rayon-core",
|
"rayon-core",
|
||||||
|
@ -2529,14 +2510,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon-core"
|
name = "rayon-core"
|
||||||
version = "1.11.0"
|
version = "1.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
|
||||||
"crossbeam-deque",
|
"crossbeam-deque",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"num_cpus",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3162,17 +3141,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sysinfo"
|
name = "sysinfo"
|
||||||
version = "0.29.11"
|
version = "0.33.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666"
|
checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
|
"memchr",
|
||||||
"ntapi",
|
"ntapi",
|
||||||
"once_cell",
|
|
||||||
"rayon",
|
"rayon",
|
||||||
"winapi",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3965,6 +3943,16 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.57.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core 0.57.0",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
|
@ -3974,17 +3962,60 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.57.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-result 0.1.2",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.57.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote 1.0.38",
|
||||||
|
"syn 2.0.92",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.57.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote 1.0.38",
|
||||||
|
"syn 2.0.92",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-registry"
|
name = "windows-registry"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
|
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-result",
|
"windows-result 0.2.0",
|
||||||
"windows-strings",
|
"windows-strings",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -4000,7 +4031,7 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-result",
|
"windows-result 0.2.0",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -63,7 +63,7 @@ keyboard = ["dep:input", "dep:evdev-rs", "dep:libc", "dep:nix"]
|
||||||
|
|
||||||
launcher = []
|
launcher = []
|
||||||
|
|
||||||
music = ["regex"]
|
music = ["dep:regex"]
|
||||||
"music+all" = ["music", "music+mpris", "music+mpd"]
|
"music+all" = ["music", "music+mpris", "music+mpd"]
|
||||||
"music+mpris" = ["music", "mpris"]
|
"music+mpris" = ["music", "mpris"]
|
||||||
"music+mpd" = ["music", "mpd-utils"]
|
"music+mpd" = ["music", "mpd-utils"]
|
||||||
|
@ -72,7 +72,7 @@ network_manager = ["futures-lite", "futures-signals", "zbus"]
|
||||||
|
|
||||||
notifications = ["zbus"]
|
notifications = ["zbus"]
|
||||||
|
|
||||||
sys_info = ["sysinfo", "regex"]
|
sys_info = ["dep:sysinfo"]
|
||||||
|
|
||||||
tray = ["system-tray"]
|
tray = ["system-tray"]
|
||||||
|
|
||||||
|
@ -148,12 +148,15 @@ libc = { version = "0.2.164", optional = true }
|
||||||
# music
|
# music
|
||||||
mpd-utils = { version = "0.2.1", optional = true }
|
mpd-utils = { version = "0.2.1", optional = true }
|
||||||
mpris = { version = "2.0.1", optional = true }
|
mpris = { version = "2.0.1", optional = true }
|
||||||
|
regex = { version = "1.11.1", default-features = false, features = [
|
||||||
|
"std",
|
||||||
|
], optional = true }
|
||||||
|
|
||||||
# network_manager
|
# network_manager
|
||||||
futures-signals = { version = "0.3.34", optional = true }
|
futures-signals = { version = "0.3.34", optional = true }
|
||||||
|
|
||||||
# sys_info
|
# sys_info
|
||||||
sysinfo = { version = "0.29.11", optional = true }
|
sysinfo = { version = "0.33.1", optional = true }
|
||||||
|
|
||||||
# tray
|
# tray
|
||||||
system-tray = { version = "0.7.0", features = ["dbusmenu-gtk3"], optional = true }
|
system-tray = { version = "0.7.0", features = ["dbusmenu-gtk3"], optional = true }
|
||||||
|
@ -164,9 +167,6 @@ libpulse-binding = { version = "2.28.2", optional = true }
|
||||||
# shared
|
# shared
|
||||||
futures-lite = { version = "2.6.0", optional = true } # network_manager, upower, workspaces
|
futures-lite = { version = "2.6.0", optional = true } # network_manager, upower, workspaces
|
||||||
nix = { version = "0.29.0", optional = true, features = ["event", "fs", "poll"] } # clipboard, input
|
nix = { version = "0.29.0", optional = true, features = ["event", "fs", "poll"] } # clipboard, input
|
||||||
regex = { version = "1.11.1", default-features = false, features = [
|
|
||||||
"std",
|
|
||||||
], optional = true } # music, sys_info
|
|
||||||
zbus = { version = "5.5.0", default-features = false, features = ["tokio"], optional = true } # network_manager, notifications, upower
|
zbus = { version = "5.5.0", default-features = false, features = ["tokio"], optional = true } # network_manager, notifications, upower
|
||||||
swayipc-async = { version = "2.0.1", optional = true } # workspaces, keyboard
|
swayipc-async = { version = "2.0.1", optional = true } # workspaces, keyboard
|
||||||
hyprland = { version = "0.4.0-alpha.3", features = ["silent"], optional = true } # workspaces, keyboard
|
hyprland = { version = "0.4.0-alpha.3", features = ["silent"], optional = true } # workspaces, keyboard
|
||||||
|
|
|
@ -3,6 +3,8 @@ Displays one or more labels containing system information.
|
||||||
Separating information across several labels allows for styling each one independently.
|
Separating information across several labels allows for styling each one independently.
|
||||||
Pango markup is supported.
|
Pango markup is supported.
|
||||||
|
|
||||||
|
Options can be provided in a token to specify operations, units and formatting.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
@ -10,17 +12,17 @@ Pango markup is supported.
|
||||||
|
|
||||||
> Type: `sys_info`
|
> Type: `sys_info`
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|--------------------|--------------------|---------|--------------------------------------------------------------------------------------------------------------------------------|
|
|--------------------|------------------------------------------------------------|----------------|--------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `format` | `string[]` | `null` | Array of strings including formatting tokens. For available tokens see below. |
|
| `format` | `string[]` | `null` | Array of strings including formatting tokens. For available tokens see below. |
|
||||||
| `interval` | `integer` or `Map` | `5` | Seconds between refreshing. Can be a single value for all data or a map of individual refresh values for different data types. |
|
| `interval` | `integer` or `Map` | `5` | Seconds between refreshing. Can be a single value for all data or a map of individual refresh values for different data types. |
|
||||||
| `interval.memory` | `integer` | `5` | Seconds between refreshing memory data |
|
| `interval.memory` | `integer` | `5` | Seconds between refreshing memory data. |
|
||||||
| `interval.cpu` | `integer` | `5` | Seconds between refreshing cpu data |
|
| `interval.cpu` | `integer` | `5` | Seconds between refreshing cpu data. |
|
||||||
| `interval.temps` | `integer` | `5` | Seconds between refreshing temperature data |
|
| `interval.temps` | `integer` | `5` | Seconds between refreshing temperature data. |
|
||||||
| `interval.disks` | `integer` | `5` | Seconds between refreshing disk data |
|
| `interval.disks` | `integer` | `5` | Seconds between refreshing disk data. |
|
||||||
| `interval.network` | `integer` | `5` | Seconds between refreshing network data |
|
| `interval.network` | `integer` | `5` | Seconds between refreshing network data. |
|
||||||
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the labels. |
|
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the labels. |
|
||||||
| `direction` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | How the labels are laid out (not the rotation of an individual label). |
|
| `direction` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | How the labels are laid out (not the rotation of an individual label). |
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>JSON</summary>
|
<summary>JSON</summary>
|
||||||
|
@ -30,12 +32,11 @@ Pango markup is supported.
|
||||||
"end": [
|
"end": [
|
||||||
{
|
{
|
||||||
"format": [
|
"format": [
|
||||||
" {cpu_percent}% | {temp_c:k10temp-Tccd1}°C",
|
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C",
|
||||||
" {memory_used} / {memory_total} GB ({memory_percent}%)",
|
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)",
|
||||||
"| {swap_used} / {swap_total} GB ({swap_percent}%)",
|
" {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s",
|
||||||
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)",
|
" {net_down@enp39s0} / {net_up@enp39s0} Mbps",
|
||||||
" {net_down:enp39s0} / {net_up:enp39s0} Mbps",
|
" {load_average_1} | {load_average_5} | {load_average_15}",
|
||||||
" {load_average:1} | {load_average:5} | {load_average:15}",
|
|
||||||
" {uptime}"
|
" {uptime}"
|
||||||
],
|
],
|
||||||
"interval": {
|
"interval": {
|
||||||
|
@ -60,13 +61,12 @@ Pango markup is supported.
|
||||||
[[end]]
|
[[end]]
|
||||||
type = 'sys_info'
|
type = 'sys_info'
|
||||||
format = [
|
format = [
|
||||||
' {cpu_percent}% | {temp_c:k10temp-Tccd1}°C',
|
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C",
|
||||||
' {memory_used} / {memory_total} GB ({memory_percent}%)',
|
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)",
|
||||||
'| {swap_used} / {swap_total} GB ({swap_percent}%)',
|
" {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s",
|
||||||
' {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)',
|
" {net_down@enp39s0} / {net_up@enp39s0} Mbps",
|
||||||
' {net_down:enp39s0} / {net_up:enp39s0} Mbps',
|
" {load_average_1} | {load_average_5} | {load_average_15}",
|
||||||
' {load_average:1} | {load_average:5} | {load_average:15}',
|
" {uptime}"
|
||||||
' {uptime}',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[end.interval]
|
[end.interval]
|
||||||
|
@ -87,13 +87,12 @@ temps = 5
|
||||||
```yaml
|
```yaml
|
||||||
end:
|
end:
|
||||||
- format:
|
- format:
|
||||||
- ' {cpu_percent}% | {temp_c:k10temp-Tccd1}°C'
|
- " {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C"
|
||||||
- ' {memory_used} / {memory_total} GB ({memory_percent}%)'
|
- " {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)"
|
||||||
- '| {swap_used} / {swap_total} GB ({swap_percent}%)'
|
- " {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s"
|
||||||
- ' {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)'
|
- " {net_down@enp39s0} / {net_up@enp39s0} Mbps"
|
||||||
- ' {net_down:enp39s0} / {net_up:enp39s0} Mbps'
|
- " {load_average_1} | {load_average_5} | {load_average_15}"
|
||||||
- ' {load_average:1} | {load_average:5} | {load_average:15}'
|
- " {uptime}"
|
||||||
- ' {uptime}'
|
|
||||||
interval:
|
interval:
|
||||||
cpu: 1
|
cpu: 1
|
||||||
disks: 300
|
disks: 300
|
||||||
|
@ -121,12 +120,11 @@ end:
|
||||||
interval.networks = 3
|
interval.networks = 3
|
||||||
|
|
||||||
format = [
|
format = [
|
||||||
" {cpu_percent}% | {temp_c:k10temp-Tccd1}°C"
|
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C"
|
||||||
" {memory_used} / {memory_total} GB ({memory_percent}%)"
|
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)"
|
||||||
"| {swap_used} / {swap_total} GB ({swap_percent}%)"
|
" {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s"
|
||||||
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)"
|
" {net_down@enp39s0} / {net_up@enp39s0} Mbps"
|
||||||
" {net_down:enp39s0} / {net_up:enp39s0} Mbps"
|
" {load_average_1} | {load_average_5} | {load_average_15}"
|
||||||
" {load_average:1} | {load_average:5} | {load_average:15}"
|
|
||||||
" {uptime}"
|
" {uptime}"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -138,39 +136,179 @@ end:
|
||||||
|
|
||||||
### Formatting Tokens
|
### Formatting Tokens
|
||||||
|
|
||||||
The following tokens can be used in the `format` configuration option:
|
The below table lists the tokens which can be used in the `format` configuration option.
|
||||||
|
More information about each of these and the additional options can be found further below.
|
||||||
|
|
||||||
| Token | Description |
|
| Token | Default Function | Default Unit | Default Formatting |
|
||||||
|--------------------------|------------------------------------------------------------------------------------|
|
|--------------------------|------------------|--------------|--------------------|
|
||||||
| **CPU** | |
|
| **CPU** | | | |
|
||||||
| `{cpu_percent}` | Total CPU utilisation percentage |
|
| `{cpu_frequency[#core]}` | `mean` | MHz | `.2` |
|
||||||
| **Memory** | |
|
| `{cpu_percent[#core]}` | `mean` | % | `0<2` |
|
||||||
| `{memory_free}` | Memory free in GB. |
|
| **Memory** | | | |
|
||||||
| `{memory_used}` | Memory used in GB. |
|
| `{memory_free}` | N/A | GB | `0<4.1` |
|
||||||
| `{memory_total}` | Memory total in GB. |
|
| `{memory_available}` | N/A | GB | `0<4.1` |
|
||||||
| `{memory_percent}` | Memory utilisation percentage. |
|
| `{memory_used}` | N/A | GB | `0<4.1` |
|
||||||
| `{swap_free}` | Swap free in GB. |
|
| `{memory_total}` | N/A | GB | `0<4.1` |
|
||||||
| `{swap_used}` | Swap used in GB. |
|
| `{memory_percent}` | N/A | GB | `0<4.1` |
|
||||||
| `{swap_total}` | Swap total in GB. |
|
| `{swap_free}` | N/A | GB | `0<4.1` |
|
||||||
| `{swap_percent}` | Swap utilisation percentage. |
|
| `{swap_used}` | N/A | GB | `0<4.1` |
|
||||||
| **Temperature** | |
|
| `{swap_total}` | N/A | GB | `0<4.1` |
|
||||||
| `{temp_c:[sensor]}` | Temperature in degrees C. Replace `[sensor]` with the sensor label. |
|
| `{swap_percent}` | N/A | GB | `0<4.1` |
|
||||||
| `{temp_f:[sensor]}` | Temperature in degrees F. Replace `[sensor]` with the sensor label. |
|
| **Temperature** | | | |
|
||||||
| **Disk** | |
|
| `{temp_c[#sensor]}` | `max` | °C | |
|
||||||
| `{disk_free:[mount]}` | Disk free space in GB. Replace `[mount]` with the disk mountpoint. |
|
| `{temp_f[#sensor]}` | `max` | °F | |
|
||||||
| `{disk_used:[mount]}` | Disk used space in GB. Replace `[mount]` with the disk mountpoint. |
|
| **Disk** | | | |
|
||||||
| `{disk_total:[mount]}` | Disk total space in GB. Replace `[mount]` with the disk mountpoint. |
|
| `{disk_free[#mount]}` | `sum` | GB | |
|
||||||
| `{disk_percent:[mount]}` | Disk utilisation percentage. Replace `[mount]` with the disk mountpoint. |
|
| `{disk_used[#mount]}` | `sum` | GB | |
|
||||||
| **Network** | |
|
| `{disk_total[#mount]}` | `sum` | GB | |
|
||||||
| `{net_down:[adapter]}` | Average network download speed in Mbps. Replace `[adapter]` with the adapter name. |
|
| `{disk_percent[#mount]}` | `sum` | % | |
|
||||||
| `{net_up:[adapter]}` | Average network upload speed in Mbps. Replace `[adapter]` with the adapter name. |
|
| `{disk_read[#mount]}` | `sum` | MB/s | |
|
||||||
| **System** | |
|
| `{disk_write[#mount]}` | `sum` | MB/s | |
|
||||||
| `{load_average:1}` | 1-minute load average. |
|
| **Network** | | | |
|
||||||
| `{load_average:5}` | 5-minute load average. |
|
| `{net_down[#adapter]}` | `sum` | Mb/s | |
|
||||||
| `{load_average:15}` | 15-minute load average. |
|
| `{net_up[#adapter]}` | `sum` | Mb/s | |
|
||||||
| `{uptime}` | System uptime formatted as `HH:mm`. |
|
| **System** | | | |
|
||||||
|
| `{load_average_1}` | N/A | - | `.2` |
|
||||||
|
| `{load_average_5}` | N/A | - | `.2` |
|
||||||
|
| `{load_average_15}` | N/A | - | `.2` |
|
||||||
|
| `{uptime}` | N/A | ??? | ??? |
|
||||||
|
|
||||||
For Intel CPUs, you can typically use `coretemp-Package-id-0` for the temperature sensor. For AMD, you can use `k10temp-Tccd1`.
|
#### Functions and names
|
||||||
|
|
||||||
|
Many of the tokens operate on a value set, as opposed to an individual value:
|
||||||
|
|
||||||
|
- CPU tokens operate on each physical thread.
|
||||||
|
- Temperature tokens operate on each sensor.
|
||||||
|
- Disk tokens operate on each mount.
|
||||||
|
- Network tokens operate on each adapter.
|
||||||
|
|
||||||
|
By default, these will apply a function to the full set to reduce them down to a single value.
|
||||||
|
The list of available functions is shown below:
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-----------------------------------------|
|
||||||
|
| `sum` | Adds each value in the set. |
|
||||||
|
| `min` | Gets the smallest value in the set. |
|
||||||
|
| `max` | Gets the largest value in the set. |
|
||||||
|
| `mean` | Gets the mean average value of the set. |
|
||||||
|
|
||||||
|
It is also possible to get only a single value from the set by specifying a name instead of a function.
|
||||||
|
|
||||||
|
| Token category | Valid name |
|
||||||
|
|----------------|-------------------------------------------------------------------------|
|
||||||
|
| CPU | A CPU thread, eg `cpu0`, `cpu1`, ... |
|
||||||
|
| Temperature | A sensor name, eg `CPUTIN`. These line up with the output of `sensors`. |
|
||||||
|
| Disk | A disk mountpoint, eg `/`, `/home`, ... |
|
||||||
|
| Network | An adapter name, eg `eth0` or `enp30s0`. |
|
||||||
|
|
||||||
|
|
||||||
|
To specify a name or function, use a `@`. For example, to show disk percent for `/home`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"{disk_percent@/home}%"
|
||||||
|
```
|
||||||
|
|
||||||
|
To show total CPU utilization where each core represents 100% (like `htop` etc):
|
||||||
|
|
||||||
|
```json
|
||||||
|
"{cpu_percent@sum}%"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Prefixes and units
|
||||||
|
|
||||||
|
For tokens which return an appropriate unit, you can specify the SI prefix (or unit in some special cases).
|
||||||
|
The following options can be supplied:
|
||||||
|
|
||||||
|
| Name | Value |
|
||||||
|
|---------|-------|
|
||||||
|
| Kilo | `k` |
|
||||||
|
| Mega | `M` |
|
||||||
|
| Giga | `G` |
|
||||||
|
| Tera | `T` |
|
||||||
|
| Peta | `P` |
|
||||||
|
| | |
|
||||||
|
| Kibi | `ki` |
|
||||||
|
| Mebi | `Mi` |
|
||||||
|
| Gibi | `Gi` |
|
||||||
|
| Tebi | `Ti` |
|
||||||
|
| Pebi | `Pi` |
|
||||||
|
| | |
|
||||||
|
| Kilobit | `kb` |
|
||||||
|
| Megabit | `Mb` |
|
||||||
|
| Gigabit | `Gb` |
|
||||||
|
|
||||||
|
To specify a prefix or unit, use a `#`. For example, to show free total disk space in terabytes:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"{disk_free#T} TB"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Formatting
|
||||||
|
|
||||||
|
To control the formatting of the resultant number,
|
||||||
|
a subset of Rust's string formatting is implemented. This includes:
|
||||||
|
|
||||||
|
- Width
|
||||||
|
- Fill/Alignment
|
||||||
|
- Precision
|
||||||
|
|
||||||
|
Formatting is specified with a `:` and MUST be the last part of a token.
|
||||||
|
|
||||||
|
##### Width
|
||||||
|
|
||||||
|
The width controls the minimum string length of the value.
|
||||||
|
Specifying just a width will left-pad the value with `0` until the value reaches the target length.
|
||||||
|
|
||||||
|
The width can be any value from `1-9`. Larger values are not supported.
|
||||||
|
|
||||||
|
For example, to render CPU usage as `045%`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"{cpu_usage:3}%"
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Fill/Alignment
|
||||||
|
|
||||||
|
These options can be used to control the `width` property.
|
||||||
|
|
||||||
|
To specify the fill and alignment, prefix the width with a character and a direction.
|
||||||
|
Fill characters can be any single UTF-8 character EXCEPT 1-9. Alignment must be one of:
|
||||||
|
|
||||||
|
- `<` - Left fill
|
||||||
|
- `^` - Center fill
|
||||||
|
- `>` - Right fill
|
||||||
|
|
||||||
|
For example, to render CPU usage as ` 45%`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"{cpu_usage: <3}%"
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Precision
|
||||||
|
|
||||||
|
The number of decimal places a value is shown to can be controlled using precision.
|
||||||
|
Any value is supported.
|
||||||
|
|
||||||
|
To specify precision, include a `.` followed by the value. If other options are supplied, this MUST come after.
|
||||||
|
|
||||||
|
For example, to render used disk space to 2dp:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"{disk_used:.2} GB"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Combining Options
|
||||||
|
|
||||||
|
Each of the token options can be combined to create more complex solutions.
|
||||||
|
|
||||||
|
Putting it all together, you could show the free disk space on your `/home` partition in terabytes,
|
||||||
|
left-padded with spaces to a min width of 5, and shown to 2dp as follows:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"{disk_used@/home#T: <5.2} TB"
|
||||||
|
```
|
||||||
|
|
||||||
## Styling
|
## Styling
|
||||||
|
|
||||||
|
|
|
@ -55,13 +55,12 @@ let {
|
||||||
interval.networks = 3
|
interval.networks = 3
|
||||||
|
|
||||||
format = [
|
format = [
|
||||||
" {cpu_percent}% | {temp_c:k10temp-Tccd1}°C"
|
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C"
|
||||||
" {memory_used} / {memory_total} GB ({memory_percent}%)"
|
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)"
|
||||||
"| {swap_used} / {swap_total} GB ({swap_percent}%)"
|
" {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s"
|
||||||
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)"
|
" {net_down@enp39s0} / {net_up@enp39s0} Mbps"
|
||||||
" {net_down:enp39s0} / {net_up:enp39s0} Mbps"
|
" {load_average1} | {load_average5} | {load_average15}"
|
||||||
" {load_average:1} | {load_average:5} | {load_average:15}"
|
" {uptime}"
|
||||||
" {uptime}"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://f.jstanger.dev/github/ironbar/schema.json",
|
|
||||||
"anchor_to_edges": true,
|
"anchor_to_edges": true,
|
||||||
"position": "bottom",
|
"position": "bottom",
|
||||||
"icon_theme": "Paper",
|
"icon_theme": "Paper",
|
||||||
|
@ -64,12 +63,11 @@
|
||||||
"networks": 3
|
"networks": 3
|
||||||
},
|
},
|
||||||
"format": [
|
"format": [
|
||||||
" {cpu_percent}% | {temp_c:k10temp-Tccd1}°C",
|
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C",
|
||||||
" {memory_used} / {memory_total} GB ({memory_percent}%)",
|
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)",
|
||||||
"| {swap_used} / {swap_total} GB ({swap_percent}%)",
|
" {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s",
|
||||||
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)",
|
" {net_down@enp39s0} / {net_up@enp39s0} Mbps",
|
||||||
" {net_down:enp39s0} / {net_up:enp39s0} Mbps",
|
" {load_average1} | {load_average5} | {load_average15}",
|
||||||
" {load_average:1} | {load_average:5} | {load_average:15}",
|
|
||||||
" {uptime}"
|
" {uptime}"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -53,12 +53,11 @@ interval = 500
|
||||||
[[end]]
|
[[end]]
|
||||||
type = "sys_info"
|
type = "sys_info"
|
||||||
format = [
|
format = [
|
||||||
" {cpu_percent}% | {temp_c:k10temp-Tccd1}°C",
|
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C",
|
||||||
" {memory_used} / {memory_total} GB ({memory_percent}%)",
|
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)",
|
||||||
"| {swap_used} / {swap_total} GB ({swap_percent}%)",
|
" {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s",
|
||||||
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)",
|
" {net_down@enp39s0} / {net_up@enp39s0} Mbps",
|
||||||
" {net_down:enp39s0} / {net_up:enp39s0} Mbps",
|
" {load_average1} | {load_average5} | {load_average15}",
|
||||||
" {load_average:1} | {load_average:5} | {load_average:15}",
|
|
||||||
" {uptime}",
|
" {uptime}",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
$schema: https://f.jstanger.dev/github/ironbar/schema.json
|
|
||||||
anchor_to_edges: true
|
anchor_to_edges: true
|
||||||
position: bottom
|
position: bottom
|
||||||
icon_theme: Paper
|
icon_theme: Paper
|
||||||
|
@ -44,12 +43,11 @@ end:
|
||||||
disks: 300
|
disks: 300
|
||||||
networks: 3
|
networks: 3
|
||||||
format:
|
format:
|
||||||
- {cpu_percent}% | {temp_c:k10temp-Tccd1}°C
|
- {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C
|
||||||
- {memory_used} / {memory_total} GB ({memory_percent}%)
|
- {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)
|
||||||
- '| {swap_used} / {swap_total} GB ({swap_percent}%)'
|
- {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s
|
||||||
- {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)
|
- {net_down@enp39s0} / {net_up@enp39s0} Mbps
|
||||||
- {net_down:enp39s0} / {net_up:enp39s0} Mbps
|
- {load_average1} | {load_average5} | {load_average15}
|
||||||
- {load_average:1} | {load_average:5} | {load_average:15}
|
|
||||||
- {uptime}
|
- {uptime}
|
||||||
- type: volume
|
- type: volume
|
||||||
format: '{icon} {percentage}%'
|
format: '{icon} {percentage}%'
|
||||||
|
|
|
@ -21,6 +21,8 @@ pub mod networkmanager;
|
||||||
pub mod sway;
|
pub mod sway;
|
||||||
#[cfg(feature = "notifications")]
|
#[cfg(feature = "notifications")]
|
||||||
pub mod swaync;
|
pub mod swaync;
|
||||||
|
#[cfg(feature = "sys_info")]
|
||||||
|
pub mod sysinfo;
|
||||||
#[cfg(feature = "tray")]
|
#[cfg(feature = "tray")]
|
||||||
pub mod tray;
|
pub mod tray;
|
||||||
#[cfg(feature = "upower")]
|
#[cfg(feature = "upower")]
|
||||||
|
@ -54,6 +56,8 @@ pub struct Clients {
|
||||||
network_manager: Option<Arc<networkmanager::Client>>,
|
network_manager: Option<Arc<networkmanager::Client>>,
|
||||||
#[cfg(feature = "notifications")]
|
#[cfg(feature = "notifications")]
|
||||||
notifications: Option<Arc<swaync::Client>>,
|
notifications: Option<Arc<swaync::Client>>,
|
||||||
|
#[cfg(feature = "sys_info")]
|
||||||
|
sys_info: Option<Arc<sysinfo::Client>>,
|
||||||
#[cfg(feature = "tray")]
|
#[cfg(feature = "tray")]
|
||||||
tray: Option<Arc<tray::Client>>,
|
tray: Option<Arc<tray::Client>>,
|
||||||
#[cfg(feature = "upower")]
|
#[cfg(feature = "upower")]
|
||||||
|
@ -185,6 +189,13 @@ impl Clients {
|
||||||
Ok(client)
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sys_info")]
|
||||||
|
pub fn sys_info(&mut self) -> Arc<sysinfo::Client> {
|
||||||
|
self.sys_info
|
||||||
|
.get_or_insert_with(|| Arc::new(sysinfo::Client::new()))
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "tray")]
|
#[cfg(feature = "tray")]
|
||||||
pub fn tray(&mut self) -> ClientResult<tray::Client> {
|
pub fn tray(&mut self) -> ClientResult<tray::Client> {
|
||||||
let client = if let Some(client) = &self.tray {
|
let client = if let Some(client) = &self.tray {
|
||||||
|
|
390
src/clients/sysinfo.rs
Normal file
390
src/clients/sysinfo.rs
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
use crate::modules::sysinfo::Interval;
|
||||||
|
use crate::{lock, register_client};
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use sysinfo::{Components, Disks, LoadAvg, Networks, RefreshKind, System};
|
||||||
|
|
||||||
|
#[repr(u64)]
|
||||||
|
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum Prefix {
|
||||||
|
#[default]
|
||||||
|
None = 1,
|
||||||
|
|
||||||
|
Kilo = 1000,
|
||||||
|
Mega = Prefix::Kilo as u64 * 1000,
|
||||||
|
Giga = Prefix::Mega as u64 * 1000,
|
||||||
|
Tera = Prefix::Giga as u64 * 1000,
|
||||||
|
Peta = Prefix::Tera as u64 * 1000,
|
||||||
|
|
||||||
|
Kibi = 1024,
|
||||||
|
Mebi = Prefix::Kibi as u64 * 1024,
|
||||||
|
Gibi = Prefix::Mebi as u64 * 1024,
|
||||||
|
Tebi = Prefix::Gibi as u64 * 1024,
|
||||||
|
Pebi = Prefix::Tebi as u64 * 1024,
|
||||||
|
|
||||||
|
// # Units
|
||||||
|
// These are special cases
|
||||||
|
// where you'd actually want to do slightly more than a prefix alone.
|
||||||
|
// Included as part of the prefix system for simplicity.
|
||||||
|
KiloBit = 128,
|
||||||
|
MegaBit = Prefix::KiloBit as u64 * 1024,
|
||||||
|
GigaBit = Prefix::MegaBit as u64 * 1024,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Function {
|
||||||
|
None,
|
||||||
|
Sum,
|
||||||
|
Min,
|
||||||
|
Max,
|
||||||
|
Mean,
|
||||||
|
Name(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ValueSet {
|
||||||
|
values: HashMap<Box<str>, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromIterator<(Box<str>, Value)> for ValueSet {
|
||||||
|
fn from_iter<T: IntoIterator<Item = (Box<str>, Value)>>(iter: T) -> Self {
|
||||||
|
Self {
|
||||||
|
values: iter.into_iter().collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValueSet {
|
||||||
|
fn values(&self, prefix: Prefix) -> impl Iterator<Item = f64> + use<'_> {
|
||||||
|
self.values
|
||||||
|
.values()
|
||||||
|
.map(move |v| v.get(prefix))
|
||||||
|
.filter(|v| !v.is_nan())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply(&self, function: &Function, prefix: Prefix) -> f64 {
|
||||||
|
match function {
|
||||||
|
Function::None => 0.0,
|
||||||
|
Function::Sum => self.sum(prefix),
|
||||||
|
Function::Min => self.min(prefix),
|
||||||
|
Function::Max => self.max(prefix),
|
||||||
|
Function::Mean => self.mean(prefix),
|
||||||
|
Function::Name(name) => self
|
||||||
|
.values
|
||||||
|
.get(&Box::from(name.as_str()))
|
||||||
|
.map(|v| v.get(prefix))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sum(&self, prefix: Prefix) -> f64 {
|
||||||
|
self.values(prefix).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn min(&self, prefix: Prefix) -> f64 {
|
||||||
|
self.values(prefix)
|
||||||
|
.min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max(&self, prefix: Prefix) -> f64 {
|
||||||
|
self.values(prefix)
|
||||||
|
.max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mean(&self, prefix: Prefix) -> f64 {
|
||||||
|
self.sum(prefix) / self.values.len() as f64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||||
|
pub struct Value {
|
||||||
|
value: f64,
|
||||||
|
prefix: Prefix,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub fn new(value: f64) -> Self {
|
||||||
|
Self::new_with_prefix(value, Prefix::None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_prefix(value: f64, prefix: Prefix) -> Self {
|
||||||
|
Self { value, prefix }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self, prefix: Prefix) -> f64 {
|
||||||
|
if prefix == self.prefix {
|
||||||
|
self.value
|
||||||
|
} else {
|
||||||
|
let scale = self.prefix as u64 as f64 / prefix as u64 as f64;
|
||||||
|
self.value * scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Client {
|
||||||
|
system: Mutex<System>,
|
||||||
|
disks: Mutex<Disks>,
|
||||||
|
components: Mutex<Components>,
|
||||||
|
networks: Mutex<Networks>,
|
||||||
|
load_average: Mutex<LoadAvg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let refresh_kind = RefreshKind::everything().without_processes();
|
||||||
|
|
||||||
|
let system = System::new_with_specifics(refresh_kind);
|
||||||
|
let disks = Disks::new_with_refreshed_list();
|
||||||
|
let components = Components::new_with_refreshed_list();
|
||||||
|
let networks = Networks::new_with_refreshed_list();
|
||||||
|
let load_average = System::load_average();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
system: Mutex::new(system),
|
||||||
|
disks: Mutex::new(disks),
|
||||||
|
components: Mutex::new(components),
|
||||||
|
networks: Mutex::new(networks),
|
||||||
|
load_average: Mutex::new(load_average),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_cpu(&self) {
|
||||||
|
lock!(self.system).refresh_cpu_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_memory(&self) {
|
||||||
|
lock!(self.system).refresh_memory();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_network(&self) {
|
||||||
|
lock!(self.networks).refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_temps(&self) {
|
||||||
|
lock!(self.components).refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_disks(&self) {
|
||||||
|
lock!(self.disks).refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh_load_average(&self) {
|
||||||
|
*lock!(self.load_average) = System::load_average();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_frequency(&self) -> ValueSet {
|
||||||
|
lock!(self.system)
|
||||||
|
.cpus()
|
||||||
|
.iter()
|
||||||
|
.map(|cpu| {
|
||||||
|
(
|
||||||
|
cpu.name().into(),
|
||||||
|
Value::new_with_prefix(cpu.frequency() as f64, Prefix::Mega),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_percent(&self) -> ValueSet {
|
||||||
|
lock!(self.system)
|
||||||
|
.cpus()
|
||||||
|
.iter()
|
||||||
|
.map(|cpu| (cpu.name().into(), Value::new(cpu.cpu_usage() as f64)))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_free(&self) -> Value {
|
||||||
|
Value::new(lock!(self.system).free_memory() as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_available(&self) -> Value {
|
||||||
|
Value::new(lock!(self.system).available_memory() as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_total(&self) -> Value {
|
||||||
|
Value::new(lock!(self.system).total_memory() as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_used(&self) -> Value {
|
||||||
|
Value::new(lock!(self.system).used_memory() as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_percent(&self) -> Value {
|
||||||
|
let total = lock!(self.system).total_memory() as f64;
|
||||||
|
let used = lock!(self.system).used_memory() as f64;
|
||||||
|
|
||||||
|
Value::new(used / total * 100.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap_free(&self) -> Value {
|
||||||
|
Value::new(lock!(self.system).free_swap() as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap_total(&self) -> Value {
|
||||||
|
Value::new(lock!(self.system).total_swap() as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap_used(&self) -> Value {
|
||||||
|
Value::new(lock!(self.system).used_swap() as f64)
|
||||||
|
}
|
||||||
|
pub fn swap_percent(&self) -> Value {
|
||||||
|
let total = lock!(self.system).total_swap() as f64;
|
||||||
|
let used = lock!(self.system).used_swap() as f64;
|
||||||
|
|
||||||
|
Value::new(used / total * 100.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn temp_c(&self) -> ValueSet {
|
||||||
|
lock!(self.components)
|
||||||
|
.iter()
|
||||||
|
.map(|comp| {
|
||||||
|
(
|
||||||
|
comp.label().into(),
|
||||||
|
Value::new(comp.temperature().unwrap_or_default() as f64),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn temp_f(&self) -> ValueSet {
|
||||||
|
lock!(self.components)
|
||||||
|
.iter()
|
||||||
|
.map(|comp| {
|
||||||
|
(
|
||||||
|
comp.label().into(),
|
||||||
|
Value::new(c_to_f(comp.temperature().unwrap_or_default() as f64)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disk_free(&self) -> ValueSet {
|
||||||
|
lock!(self.disks)
|
||||||
|
.iter()
|
||||||
|
.map(|disk| {
|
||||||
|
(
|
||||||
|
disk.mount_point().to_string_lossy().into(),
|
||||||
|
Value::new(disk.available_space() as f64),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disk_total(&self) -> ValueSet {
|
||||||
|
lock!(self.disks)
|
||||||
|
.iter()
|
||||||
|
.map(|disk| {
|
||||||
|
(
|
||||||
|
disk.mount_point().to_string_lossy().into(),
|
||||||
|
Value::new(disk.total_space() as f64),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disk_used(&self) -> ValueSet {
|
||||||
|
lock!(self.disks)
|
||||||
|
.iter()
|
||||||
|
.map(|disk| {
|
||||||
|
(
|
||||||
|
disk.mount_point().to_string_lossy().into(),
|
||||||
|
Value::new((disk.total_space() - disk.available_space()) as f64),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disk_percent(&self) -> ValueSet {
|
||||||
|
lock!(self.disks)
|
||||||
|
.iter()
|
||||||
|
.map(|disk| {
|
||||||
|
(
|
||||||
|
disk.mount_point().to_string_lossy().into(),
|
||||||
|
Value::new(
|
||||||
|
(disk.total_space() - disk.available_space()) as f64
|
||||||
|
/ disk.total_space() as f64
|
||||||
|
* 100.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disk_read(&self, interval: Interval) -> ValueSet {
|
||||||
|
lock!(self.disks)
|
||||||
|
.iter()
|
||||||
|
.map(|disk| {
|
||||||
|
(
|
||||||
|
disk.mount_point().to_string_lossy().into(),
|
||||||
|
Value::new(disk.usage().read_bytes as f64 / interval.disks() as f64),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disk_write(&self, interval: Interval) -> ValueSet {
|
||||||
|
lock!(self.disks)
|
||||||
|
.iter()
|
||||||
|
.map(|disk| {
|
||||||
|
(
|
||||||
|
disk.mount_point().to_string_lossy().into(),
|
||||||
|
Value::new(disk.usage().written_bytes as f64 / interval.disks() as f64),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn net_down(&self, interval: Interval) -> ValueSet {
|
||||||
|
lock!(self.networks)
|
||||||
|
.iter()
|
||||||
|
.map(|(name, net)| {
|
||||||
|
(
|
||||||
|
name.as_str().into(),
|
||||||
|
Value::new(net.received() as f64 / interval.networks() as f64),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn net_up(&self, interval: Interval) -> ValueSet {
|
||||||
|
lock!(self.networks)
|
||||||
|
.iter()
|
||||||
|
.map(|(name, net)| {
|
||||||
|
(
|
||||||
|
name.as_str().into(),
|
||||||
|
Value::new(net.transmitted() as f64 / interval.networks() as f64),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_average_1(&self) -> Value {
|
||||||
|
Value::new(lock!(self.load_average).one)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_average_5(&self) -> Value {
|
||||||
|
Value::new(lock!(self.load_average).five)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_average_15(&self) -> Value {
|
||||||
|
Value::new(lock!(self.load_average).fifteen)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets system uptime formatted as `HH:mm`.
|
||||||
|
pub fn uptime(&self) -> String {
|
||||||
|
let uptime = System::uptime();
|
||||||
|
let hours = uptime / 3600;
|
||||||
|
format!("{:0>2}:{:0>2}", hours, (uptime % 3600) / 60)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register_client!(Client, sys_info);
|
||||||
|
|
||||||
|
const fn c_to_f(c: f64) -> f64 {
|
||||||
|
c / 5.0 * 9.0 + 32.0
|
||||||
|
}
|
|
@ -1,451 +0,0 @@
|
||||||
use crate::config::{CommonConfig, ModuleOrientation};
|
|
||||||
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
|
|
||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
|
||||||
use crate::{glib_recv, module_impl, send_async, spawn};
|
|
||||||
use color_eyre::Result;
|
|
||||||
use gtk::prelude::*;
|
|
||||||
use gtk::Label;
|
|
||||||
use regex::{Captures, Regex};
|
|
||||||
use serde::Deserialize;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::time::Duration;
|
|
||||||
use sysinfo::{ComponentExt, CpuExt, DiskExt, NetworkExt, RefreshKind, System, SystemExt};
|
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use tokio::time::sleep;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
|
||||||
pub struct SysInfoModule {
|
|
||||||
/// List of strings including formatting tokens.
|
|
||||||
/// For available tokens, see [below](#formatting-tokens).
|
|
||||||
///
|
|
||||||
/// **Required**
|
|
||||||
format: Vec<String>,
|
|
||||||
|
|
||||||
/// Number of seconds between refresh.
|
|
||||||
///
|
|
||||||
/// This can be set as a global interval,
|
|
||||||
/// or passed as an object to customize the interval per-system.
|
|
||||||
///
|
|
||||||
/// **Default**: `5`
|
|
||||||
#[serde(default = "Interval::default")]
|
|
||||||
interval: Interval,
|
|
||||||
|
|
||||||
/// The orientation of text for the labels.
|
|
||||||
///
|
|
||||||
/// **Valid options**: `horizontal`, `vertical`, `h`, `v`
|
|
||||||
/// <br>
|
|
||||||
/// **Default** : `horizontal`
|
|
||||||
#[serde(default)]
|
|
||||||
orientation: ModuleOrientation,
|
|
||||||
|
|
||||||
/// The orientation by which the labels are laid out.
|
|
||||||
///
|
|
||||||
/// **Valid options**: `horizontal`, `vertical`, `h`, `v`
|
|
||||||
/// <br>
|
|
||||||
/// **Default** : `horizontal`
|
|
||||||
direction: Option<ModuleOrientation>,
|
|
||||||
|
|
||||||
/// See [common options](module-level-options#common-options).
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub common: Option<CommonConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Copy, Clone)]
|
|
||||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
|
||||||
pub struct Intervals {
|
|
||||||
/// The number of seconds between refreshing memory data.
|
|
||||||
///
|
|
||||||
/// **Default**: `5`
|
|
||||||
#[serde(default = "default_interval")]
|
|
||||||
memory: u64,
|
|
||||||
|
|
||||||
/// The number of seconds between refreshing CPU data.
|
|
||||||
///
|
|
||||||
/// **Default**: `5`
|
|
||||||
#[serde(default = "default_interval")]
|
|
||||||
cpu: u64,
|
|
||||||
|
|
||||||
/// The number of seconds between refreshing temperature data.
|
|
||||||
///
|
|
||||||
/// **Default**: `5`
|
|
||||||
#[serde(default = "default_interval")]
|
|
||||||
temps: u64,
|
|
||||||
|
|
||||||
/// The number of seconds between refreshing disk data.
|
|
||||||
///
|
|
||||||
/// **Default**: `5`
|
|
||||||
#[serde(default = "default_interval")]
|
|
||||||
disks: u64,
|
|
||||||
|
|
||||||
/// The number of seconds between refreshing network data.
|
|
||||||
///
|
|
||||||
/// **Default**: `5`
|
|
||||||
#[serde(default = "default_interval")]
|
|
||||||
networks: u64,
|
|
||||||
|
|
||||||
/// The number of seconds between refreshing system data.
|
|
||||||
///
|
|
||||||
/// **Default**: `5`
|
|
||||||
#[serde(default = "default_interval")]
|
|
||||||
system: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Copy, Clone)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
|
||||||
pub enum Interval {
|
|
||||||
All(u64),
|
|
||||||
Individual(Intervals),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Interval {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::All(default_interval())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interval {
|
|
||||||
const fn memory(self) -> u64 {
|
|
||||||
match self {
|
|
||||||
Self::All(n) => n,
|
|
||||||
Self::Individual(intervals) => intervals.memory,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn cpu(self) -> u64 {
|
|
||||||
match self {
|
|
||||||
Self::All(n) => n,
|
|
||||||
Self::Individual(intervals) => intervals.cpu,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn temps(self) -> u64 {
|
|
||||||
match self {
|
|
||||||
Self::All(n) => n,
|
|
||||||
Self::Individual(intervals) => intervals.temps,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn disks(self) -> u64 {
|
|
||||||
match self {
|
|
||||||
Self::All(n) => n,
|
|
||||||
Self::Individual(intervals) => intervals.disks,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn networks(self) -> u64 {
|
|
||||||
match self {
|
|
||||||
Self::All(n) => n,
|
|
||||||
Self::Individual(intervals) => intervals.networks,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn system(self) -> u64 {
|
|
||||||
match self {
|
|
||||||
Self::All(n) => n,
|
|
||||||
Self::Individual(intervals) => intervals.system,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn default_interval() -> u64 {
|
|
||||||
5
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum RefreshType {
|
|
||||||
Memory,
|
|
||||||
Cpu,
|
|
||||||
Temps,
|
|
||||||
Disks,
|
|
||||||
Network,
|
|
||||||
System,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Module<gtk::Box> for SysInfoModule {
|
|
||||||
type SendMessage = HashMap<String, String>;
|
|
||||||
type ReceiveMessage = ();
|
|
||||||
|
|
||||||
module_impl!("sysinfo");
|
|
||||||
|
|
||||||
fn spawn_controller(
|
|
||||||
&self,
|
|
||||||
_info: &ModuleInfo,
|
|
||||||
context: &WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
|
||||||
_rx: mpsc::Receiver<Self::ReceiveMessage>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let interval = self.interval;
|
|
||||||
|
|
||||||
let refresh_kind = RefreshKind::everything()
|
|
||||||
.without_processes()
|
|
||||||
.without_users_list();
|
|
||||||
|
|
||||||
let mut sys = System::new_with_specifics(refresh_kind);
|
|
||||||
sys.refresh_components_list();
|
|
||||||
sys.refresh_disks_list();
|
|
||||||
sys.refresh_networks_list();
|
|
||||||
|
|
||||||
let (refresh_tx, mut refresh_rx) = mpsc::channel(16);
|
|
||||||
|
|
||||||
macro_rules! spawn_refresh {
|
|
||||||
($refresh_type:expr, $func:ident) => {{
|
|
||||||
let tx = refresh_tx.clone();
|
|
||||||
spawn(async move {
|
|
||||||
loop {
|
|
||||||
send_async!(tx, $refresh_type);
|
|
||||||
sleep(Duration::from_secs(interval.$func())).await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
spawn_refresh!(RefreshType::Memory, memory);
|
|
||||||
spawn_refresh!(RefreshType::Cpu, cpu);
|
|
||||||
spawn_refresh!(RefreshType::Temps, temps);
|
|
||||||
spawn_refresh!(RefreshType::Disks, disks);
|
|
||||||
spawn_refresh!(RefreshType::Network, networks);
|
|
||||||
spawn_refresh!(RefreshType::System, system);
|
|
||||||
|
|
||||||
let tx = context.tx.clone();
|
|
||||||
spawn(async move {
|
|
||||||
let mut format_info = HashMap::new();
|
|
||||||
|
|
||||||
while let Some(refresh) = refresh_rx.recv().await {
|
|
||||||
match refresh {
|
|
||||||
RefreshType::Memory => refresh_memory_tokens(&mut format_info, &mut sys),
|
|
||||||
RefreshType::Cpu => refresh_cpu_tokens(&mut format_info, &mut sys),
|
|
||||||
RefreshType::Temps => refresh_temp_tokens(&mut format_info, &mut sys),
|
|
||||||
RefreshType::Disks => refresh_disk_tokens(&mut format_info, &mut sys),
|
|
||||||
RefreshType::Network => {
|
|
||||||
refresh_network_tokens(&mut format_info, &mut sys, interval.networks());
|
|
||||||
}
|
|
||||||
RefreshType::System => refresh_system_tokens(&mut format_info, &sys),
|
|
||||||
};
|
|
||||||
|
|
||||||
send_async!(tx, ModuleUpdateEvent::Update(format_info.clone()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_widget(
|
|
||||||
self,
|
|
||||||
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
|
||||||
_info: &ModuleInfo,
|
|
||||||
) -> Result<ModuleParts<gtk::Box>> {
|
|
||||||
let re = Regex::new(r"\{([^}]+)}")?;
|
|
||||||
|
|
||||||
let layout = match self.direction {
|
|
||||||
Some(orientation) => orientation,
|
|
||||||
None => self.orientation,
|
|
||||||
};
|
|
||||||
|
|
||||||
let container = gtk::Box::new(layout.into(), 10);
|
|
||||||
|
|
||||||
let mut labels = Vec::new();
|
|
||||||
|
|
||||||
for format in &self.format {
|
|
||||||
let label = Label::builder().label(format).use_markup(true).build();
|
|
||||||
|
|
||||||
label.add_class("item");
|
|
||||||
label.set_angle(self.orientation.to_angle());
|
|
||||||
|
|
||||||
container.add(&label);
|
|
||||||
labels.push(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let formats = self.format;
|
|
||||||
glib_recv!(context.subscribe(), info => {
|
|
||||||
for (format, label) in formats.iter().zip(labels.clone()) {
|
|
||||||
let format_compiled = re.replace_all(format, |caps: &Captures| {
|
|
||||||
info.get(&caps[1])
|
|
||||||
.unwrap_or(&caps[0].to_string())
|
|
||||||
.to_string()
|
|
||||||
});
|
|
||||||
|
|
||||||
label.set_label_escaped(format_compiled.as_ref());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ModuleParts {
|
|
||||||
widget: container,
|
|
||||||
popup: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh_memory_tokens(format_info: &mut HashMap<String, String>, sys: &mut System) {
|
|
||||||
sys.refresh_memory();
|
|
||||||
|
|
||||||
let total_memory = sys.total_memory();
|
|
||||||
let available_memory = sys.available_memory();
|
|
||||||
|
|
||||||
let actual_used_memory = total_memory - available_memory;
|
|
||||||
let memory_percent = actual_used_memory as f64 / total_memory as f64 * 100.0;
|
|
||||||
|
|
||||||
format_info.insert(
|
|
||||||
String::from("memory_free"),
|
|
||||||
(bytes_to_gigabytes(available_memory)).to_string(),
|
|
||||||
);
|
|
||||||
format_info.insert(
|
|
||||||
String::from("memory_used"),
|
|
||||||
(bytes_to_gigabytes(actual_used_memory)).to_string(),
|
|
||||||
);
|
|
||||||
format_info.insert(
|
|
||||||
String::from("memory_total"),
|
|
||||||
(bytes_to_gigabytes(total_memory)).to_string(),
|
|
||||||
);
|
|
||||||
format_info.insert(
|
|
||||||
String::from("memory_percent"),
|
|
||||||
format!("{memory_percent:0>2.0}"),
|
|
||||||
);
|
|
||||||
|
|
||||||
let used_swap = sys.used_swap();
|
|
||||||
let total_swap = sys.total_swap();
|
|
||||||
|
|
||||||
format_info.insert(
|
|
||||||
String::from("swap_free"),
|
|
||||||
(bytes_to_gigabytes(sys.free_swap())).to_string(),
|
|
||||||
);
|
|
||||||
format_info.insert(
|
|
||||||
String::from("swap_used"),
|
|
||||||
(bytes_to_gigabytes(used_swap)).to_string(),
|
|
||||||
);
|
|
||||||
format_info.insert(
|
|
||||||
String::from("swap_total"),
|
|
||||||
(bytes_to_gigabytes(total_swap)).to_string(),
|
|
||||||
);
|
|
||||||
format_info.insert(
|
|
||||||
String::from("swap_percent"),
|
|
||||||
format!("{:0>2.0}", used_swap as f64 / total_swap as f64 * 100.0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh_cpu_tokens(format_info: &mut HashMap<String, String>, sys: &mut System) {
|
|
||||||
sys.refresh_cpu();
|
|
||||||
|
|
||||||
let cpu_info = sys.global_cpu_info();
|
|
||||||
let cpu_percent = cpu_info.cpu_usage();
|
|
||||||
|
|
||||||
format_info.insert(String::from("cpu_percent"), format!("{cpu_percent:0>2.0}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh_temp_tokens(format_info: &mut HashMap<String, String>, sys: &mut System) {
|
|
||||||
sys.refresh_components();
|
|
||||||
|
|
||||||
let components = sys.components();
|
|
||||||
for component in components {
|
|
||||||
let key = component.label().replace(' ', "-");
|
|
||||||
let temp = component.temperature();
|
|
||||||
|
|
||||||
format_info.insert(format!("temp_c:{key}"), format!("{temp:.0}"));
|
|
||||||
format_info.insert(format!("temp_f:{key}"), format!("{:.0}", c_to_f(temp)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh_disk_tokens(format_info: &mut HashMap<String, String>, sys: &mut System) {
|
|
||||||
sys.refresh_disks();
|
|
||||||
|
|
||||||
for disk in sys.disks() {
|
|
||||||
// replace braces to avoid conflict with regex
|
|
||||||
let key = disk
|
|
||||||
.mount_point()
|
|
||||||
.to_str()
|
|
||||||
.map(|s| s.replace(['{', '}'], ""));
|
|
||||||
|
|
||||||
if let Some(key) = key {
|
|
||||||
let total = disk.total_space();
|
|
||||||
let available = disk.available_space();
|
|
||||||
let used = total - available;
|
|
||||||
|
|
||||||
format_info.insert(
|
|
||||||
format!("disk_free:{key}"),
|
|
||||||
bytes_to_gigabytes(available).to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
format_info.insert(
|
|
||||||
format!("disk_used:{key}"),
|
|
||||||
bytes_to_gigabytes(used).to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
format_info.insert(
|
|
||||||
format!("disk_total:{key}"),
|
|
||||||
bytes_to_gigabytes(total).to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
format_info.insert(
|
|
||||||
format!("disk_percent:{key}"),
|
|
||||||
format!("{:0>2.0}", used as f64 / total as f64 * 100.0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh_network_tokens(
|
|
||||||
format_info: &mut HashMap<String, String>,
|
|
||||||
sys: &mut System,
|
|
||||||
interval: u64,
|
|
||||||
) {
|
|
||||||
sys.refresh_networks();
|
|
||||||
|
|
||||||
for (iface, network) in sys.networks() {
|
|
||||||
format_info.insert(
|
|
||||||
format!("net_down:{iface}"),
|
|
||||||
format!("{:0>2.0}", bytes_to_megabits(network.received()) / interval),
|
|
||||||
);
|
|
||||||
|
|
||||||
format_info.insert(
|
|
||||||
format!("net_up:{iface}"),
|
|
||||||
format!(
|
|
||||||
"{:0>2.0}",
|
|
||||||
bytes_to_megabits(network.transmitted()) / interval
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh_system_tokens(format_info: &mut HashMap<String, String>, sys: &System) {
|
|
||||||
// no refresh required for these tokens
|
|
||||||
|
|
||||||
let load_average = sys.load_average();
|
|
||||||
format_info.insert(
|
|
||||||
String::from("load_average:1"),
|
|
||||||
format!("{:.2}", load_average.one),
|
|
||||||
);
|
|
||||||
|
|
||||||
format_info.insert(
|
|
||||||
String::from("load_average:5"),
|
|
||||||
format!("{:.2}", load_average.five),
|
|
||||||
);
|
|
||||||
|
|
||||||
format_info.insert(
|
|
||||||
String::from("load_average:15"),
|
|
||||||
format!("{:.2}", load_average.fifteen),
|
|
||||||
);
|
|
||||||
|
|
||||||
let uptime = Duration::from_secs(sys.uptime()).as_secs();
|
|
||||||
let hours = uptime / 3600;
|
|
||||||
format_info.insert(
|
|
||||||
String::from("uptime"),
|
|
||||||
format!("{:0>2}:{:0>2}", hours, (uptime % 3600) / 60),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts celsius to fahrenheit.
|
|
||||||
fn c_to_f(c: f32) -> f32 {
|
|
||||||
c * 9.0 / 5.0 + 32.0
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn bytes_to_gigabytes(b: u64) -> u64 {
|
|
||||||
const BYTES_IN_GIGABYTE: u64 = 1_000_000_000;
|
|
||||||
b / BYTES_IN_GIGABYTE
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn bytes_to_megabits(b: u64) -> u64 {
|
|
||||||
const BYTES_IN_MEGABIT: u64 = 125_000;
|
|
||||||
b / BYTES_IN_MEGABIT
|
|
||||||
}
|
|
314
src/modules/sysinfo/mod.rs
Normal file
314
src/modules/sysinfo/mod.rs
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
mod parser;
|
||||||
|
mod renderer;
|
||||||
|
mod token;
|
||||||
|
|
||||||
|
use crate::config::{CommonConfig, ModuleOrientation};
|
||||||
|
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
|
||||||
|
use crate::modules::sysinfo::token::{Part, TokenType};
|
||||||
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
||||||
|
use crate::{clients, glib_recv, module_impl, send_async, spawn, try_send};
|
||||||
|
use color_eyre::Result;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk::Label;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
|
pub struct SysInfoModule {
|
||||||
|
/// List of strings including formatting tokens.
|
||||||
|
/// For available tokens, see [below](#formatting-tokens).
|
||||||
|
///
|
||||||
|
/// **Required**
|
||||||
|
format: Vec<String>,
|
||||||
|
|
||||||
|
/// Number of seconds between refresh.
|
||||||
|
///
|
||||||
|
/// This can be set as a global interval,
|
||||||
|
/// or passed as an object to customize the interval per-system.
|
||||||
|
///
|
||||||
|
/// **Default**: `5`
|
||||||
|
#[serde(default = "Interval::default")]
|
||||||
|
interval: Interval,
|
||||||
|
|
||||||
|
/// The orientation of text for the labels.
|
||||||
|
///
|
||||||
|
/// **Valid options**: `horizontal`, `vertical`, `h`, `v`
|
||||||
|
/// <br>
|
||||||
|
/// **Default** : `horizontal`
|
||||||
|
#[serde(default)]
|
||||||
|
orientation: ModuleOrientation,
|
||||||
|
|
||||||
|
/// The orientation by which the labels are laid out.
|
||||||
|
///
|
||||||
|
/// **Valid options**: `horizontal`, `vertical`, `h`, `v`
|
||||||
|
/// <br>
|
||||||
|
/// **Default** : `horizontal`
|
||||||
|
direction: Option<ModuleOrientation>,
|
||||||
|
|
||||||
|
/// See [common options](module-level-options#common-options).
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub common: Option<CommonConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
|
pub struct Intervals {
|
||||||
|
/// The number of seconds between refreshing memory data.
|
||||||
|
///
|
||||||
|
/// **Default**: `5`
|
||||||
|
#[serde(default = "default_interval")]
|
||||||
|
memory: u64,
|
||||||
|
|
||||||
|
/// The number of seconds between refreshing CPU data.
|
||||||
|
///
|
||||||
|
/// **Default**: `5`
|
||||||
|
#[serde(default = "default_interval")]
|
||||||
|
cpu: u64,
|
||||||
|
|
||||||
|
/// The number of seconds between refreshing temperature data.
|
||||||
|
///
|
||||||
|
/// **Default**: `5`
|
||||||
|
#[serde(default = "default_interval")]
|
||||||
|
temps: u64,
|
||||||
|
|
||||||
|
/// The number of seconds between refreshing disk data.
|
||||||
|
///
|
||||||
|
/// **Default**: `5`
|
||||||
|
#[serde(default = "default_interval")]
|
||||||
|
disks: u64,
|
||||||
|
|
||||||
|
/// The number of seconds between refreshing network data.
|
||||||
|
///
|
||||||
|
/// **Default**: `5`
|
||||||
|
#[serde(default = "default_interval")]
|
||||||
|
networks: u64,
|
||||||
|
|
||||||
|
/// The number of seconds between refreshing system data.
|
||||||
|
///
|
||||||
|
/// **Default**: `5`
|
||||||
|
#[serde(default = "default_interval")]
|
||||||
|
system: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Copy, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
|
pub enum Interval {
|
||||||
|
All(u64),
|
||||||
|
Individual(Intervals),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Interval {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::All(default_interval())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interval {
|
||||||
|
const fn memory(self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::All(n) => n,
|
||||||
|
Self::Individual(intervals) => intervals.memory,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn cpu(self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::All(n) => n,
|
||||||
|
Self::Individual(intervals) => intervals.cpu,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn temps(self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::All(n) => n,
|
||||||
|
Self::Individual(intervals) => intervals.temps,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn disks(self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::All(n) => n,
|
||||||
|
Self::Individual(intervals) => intervals.disks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn networks(self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::All(n) => n,
|
||||||
|
Self::Individual(intervals) => intervals.networks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn system(self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::All(n) => n,
|
||||||
|
Self::Individual(intervals) => intervals.system,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn default_interval() -> u64 {
|
||||||
|
5
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
enum RefreshType {
|
||||||
|
Memory,
|
||||||
|
Cpu,
|
||||||
|
Temps,
|
||||||
|
Disks,
|
||||||
|
Network,
|
||||||
|
System,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenType {
|
||||||
|
fn is_affected_by(self, refresh_type: RefreshType) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::CpuFrequency | Self::CpuPercent => refresh_type == RefreshType::Cpu,
|
||||||
|
Self::MemoryFree
|
||||||
|
| Self::MemoryAvailable
|
||||||
|
| Self::MemoryTotal
|
||||||
|
| Self::MemoryUsed
|
||||||
|
| Self::MemoryPercent
|
||||||
|
| Self::SwapFree
|
||||||
|
| Self::SwapTotal
|
||||||
|
| Self::SwapUsed
|
||||||
|
| Self::SwapPercent => refresh_type == RefreshType::Memory,
|
||||||
|
Self::TempC | Self::TempF => refresh_type == RefreshType::Temps,
|
||||||
|
Self::DiskFree
|
||||||
|
| Self::DiskTotal
|
||||||
|
| Self::DiskUsed
|
||||||
|
| Self::DiskPercent
|
||||||
|
| Self::DiskRead
|
||||||
|
| Self::DiskWrite => refresh_type == RefreshType::Disks,
|
||||||
|
Self::NetDown | Self::NetUp => refresh_type == RefreshType::Network,
|
||||||
|
Self::LoadAverage1 | Self::LoadAverage5 | Self::LoadAverage15 => {
|
||||||
|
refresh_type == RefreshType::System
|
||||||
|
}
|
||||||
|
Self::Uptime => refresh_type == RefreshType::System,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Module<gtk::Box> for SysInfoModule {
|
||||||
|
type SendMessage = (usize, String);
|
||||||
|
type ReceiveMessage = ();
|
||||||
|
|
||||||
|
module_impl!("sysinfo");
|
||||||
|
|
||||||
|
fn spawn_controller(
|
||||||
|
&self,
|
||||||
|
_info: &ModuleInfo,
|
||||||
|
context: &WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
|
_rx: mpsc::Receiver<Self::ReceiveMessage>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let interval = self.interval;
|
||||||
|
|
||||||
|
let client = context.client::<clients::sysinfo::Client>();
|
||||||
|
|
||||||
|
let format_tokens = self
|
||||||
|
.format
|
||||||
|
.iter()
|
||||||
|
.map(|format| parser::parse_input(format.as_str()))
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
for (i, token_set) in format_tokens.iter().enumerate() {
|
||||||
|
let rendered = Part::render_all(token_set, &client, interval);
|
||||||
|
try_send!(context.tx, ModuleUpdateEvent::Update((i, rendered)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (refresh_tx, mut refresh_rx) = mpsc::channel(16);
|
||||||
|
|
||||||
|
macro_rules! spawn_refresh {
|
||||||
|
($refresh_type:expr, $func:ident) => {{
|
||||||
|
let tx = refresh_tx.clone();
|
||||||
|
spawn(async move {
|
||||||
|
loop {
|
||||||
|
send_async!(tx, $refresh_type);
|
||||||
|
sleep(Duration::from_secs(interval.$func())).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn_refresh!(RefreshType::Memory, memory);
|
||||||
|
spawn_refresh!(RefreshType::Cpu, cpu);
|
||||||
|
spawn_refresh!(RefreshType::Temps, temps);
|
||||||
|
spawn_refresh!(RefreshType::Disks, disks);
|
||||||
|
spawn_refresh!(RefreshType::Network, networks);
|
||||||
|
spawn_refresh!(RefreshType::System, system);
|
||||||
|
|
||||||
|
let tx = context.tx.clone();
|
||||||
|
spawn(async move {
|
||||||
|
while let Some(refresh) = refresh_rx.recv().await {
|
||||||
|
match refresh {
|
||||||
|
RefreshType::Memory => client.refresh_memory(),
|
||||||
|
RefreshType::Cpu => client.refresh_cpu(),
|
||||||
|
RefreshType::Temps => client.refresh_temps(),
|
||||||
|
RefreshType::Disks => client.refresh_disks(),
|
||||||
|
RefreshType::Network => client.refresh_network(),
|
||||||
|
RefreshType::System => client.refresh_load_average(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i, token_set) in format_tokens.iter().enumerate() {
|
||||||
|
let is_affected = token_set
|
||||||
|
.iter()
|
||||||
|
.filter_map(|part| {
|
||||||
|
if let Part::Token(token) = part {
|
||||||
|
Some(token)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.any(|t| t.token.is_affected_by(refresh));
|
||||||
|
|
||||||
|
if is_affected {
|
||||||
|
let rendered = Part::render_all(token_set, &client, interval);
|
||||||
|
send_async!(tx, ModuleUpdateEvent::Update((i, rendered)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_widget(
|
||||||
|
self,
|
||||||
|
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||||
|
_info: &ModuleInfo,
|
||||||
|
) -> Result<ModuleParts<gtk::Box>> {
|
||||||
|
let layout = match self.direction {
|
||||||
|
Some(orientation) => orientation,
|
||||||
|
None => self.orientation,
|
||||||
|
};
|
||||||
|
|
||||||
|
let container = gtk::Box::new(layout.into(), 10);
|
||||||
|
|
||||||
|
let mut labels = Vec::new();
|
||||||
|
|
||||||
|
for _ in &self.format {
|
||||||
|
let label = Label::builder().use_markup(true).build();
|
||||||
|
|
||||||
|
label.add_class("item");
|
||||||
|
label.set_angle(self.orientation.to_angle());
|
||||||
|
|
||||||
|
container.add(&label);
|
||||||
|
labels.push(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
glib_recv!(context.subscribe(), data => {
|
||||||
|
let label = &labels[data.0];
|
||||||
|
label.set_label_escaped(&data.1);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(ModuleParts {
|
||||||
|
widget: container,
|
||||||
|
popup: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
460
src/modules/sysinfo/parser.rs
Normal file
460
src/modules/sysinfo/parser.rs
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
use crate::clients::sysinfo::{Function, Prefix};
|
||||||
|
use crate::modules::sysinfo::token::{Alignment, Formatting, Part, Token, TokenType};
|
||||||
|
use color_eyre::{Report, Result};
|
||||||
|
use std::iter::Peekable;
|
||||||
|
use std::str::{Chars, FromStr};
|
||||||
|
|
||||||
|
impl FromStr for TokenType {
|
||||||
|
type Err = Report;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
match s {
|
||||||
|
"cpu_frequency" => Ok(Self::CpuFrequency),
|
||||||
|
"cpu_percent" => Ok(Self::CpuPercent),
|
||||||
|
|
||||||
|
"memory_free" => Ok(Self::MemoryFree),
|
||||||
|
"memory_available" => Ok(Self::MemoryAvailable),
|
||||||
|
"memory_total" => Ok(Self::MemoryTotal),
|
||||||
|
"memory_used" => Ok(Self::MemoryUsed),
|
||||||
|
"memory_percent" => Ok(Self::MemoryPercent),
|
||||||
|
|
||||||
|
"swap_free" => Ok(Self::SwapFree),
|
||||||
|
"swap_total" => Ok(Self::SwapTotal),
|
||||||
|
"swap_used" => Ok(Self::SwapUsed),
|
||||||
|
"swap_percent" => Ok(Self::SwapPercent),
|
||||||
|
|
||||||
|
"temp_c" => Ok(Self::TempC),
|
||||||
|
"temp_f" => Ok(Self::TempF),
|
||||||
|
|
||||||
|
"disk_free" => Ok(Self::DiskFree),
|
||||||
|
"disk_total" => Ok(Self::DiskTotal),
|
||||||
|
"disk_used" => Ok(Self::DiskUsed),
|
||||||
|
"disk_percent" => Ok(Self::DiskPercent),
|
||||||
|
"disk_read" => Ok(Self::DiskRead),
|
||||||
|
"disk_write" => Ok(Self::DiskWrite),
|
||||||
|
|
||||||
|
"net_down" => Ok(Self::NetDown),
|
||||||
|
"net_up" => Ok(Self::NetUp),
|
||||||
|
|
||||||
|
"load_average_1" => Ok(Self::LoadAverage1),
|
||||||
|
"load_average_5" => Ok(Self::LoadAverage5),
|
||||||
|
"load_average_15" => Ok(Self::LoadAverage15),
|
||||||
|
"uptime" => Ok(Self::Uptime),
|
||||||
|
_ => Err(Report::msg(format!("invalid token type: '{s}'"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Function {
|
||||||
|
type Err = ();
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"sum" => Ok(Self::Sum),
|
||||||
|
"min" => Ok(Self::Min),
|
||||||
|
"max" => Ok(Self::Max),
|
||||||
|
"mean" => Ok(Self::Mean),
|
||||||
|
"" => Err(()),
|
||||||
|
_ => Ok(Self::Name(s.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Function {
|
||||||
|
pub(crate) fn default_for(token_type: TokenType) -> Self {
|
||||||
|
match token_type {
|
||||||
|
TokenType::CpuFrequency
|
||||||
|
| TokenType::CpuPercent
|
||||||
|
| TokenType::TempC
|
||||||
|
| TokenType::DiskPercent => Self::Mean,
|
||||||
|
TokenType::DiskFree
|
||||||
|
| TokenType::DiskTotal
|
||||||
|
| TokenType::DiskUsed
|
||||||
|
| TokenType::DiskRead
|
||||||
|
| TokenType::DiskWrite
|
||||||
|
| TokenType::NetDown
|
||||||
|
| TokenType::NetUp => Self::Sum,
|
||||||
|
_ => Self::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Prefix {
|
||||||
|
pub(crate) fn default_for(token_type: TokenType) -> Self {
|
||||||
|
match token_type {
|
||||||
|
TokenType::CpuFrequency
|
||||||
|
| TokenType::MemoryFree
|
||||||
|
| TokenType::MemoryAvailable
|
||||||
|
| TokenType::MemoryTotal
|
||||||
|
| TokenType::MemoryUsed
|
||||||
|
| TokenType::SwapFree
|
||||||
|
| TokenType::SwapTotal
|
||||||
|
| TokenType::SwapUsed
|
||||||
|
| TokenType::DiskFree
|
||||||
|
| TokenType::DiskTotal
|
||||||
|
| TokenType::DiskUsed => Self::Giga,
|
||||||
|
TokenType::DiskRead | TokenType::DiskWrite => Self::Mega,
|
||||||
|
TokenType::NetDown | TokenType::NetUp => Self::MegaBit,
|
||||||
|
_ => Self::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Prefix {
|
||||||
|
type Err = Report;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
match s {
|
||||||
|
"k" => Ok(Prefix::Kilo),
|
||||||
|
"M" => Ok(Prefix::Mega),
|
||||||
|
"G" => Ok(Prefix::Giga),
|
||||||
|
"T" => Ok(Prefix::Tera),
|
||||||
|
"P" => Ok(Prefix::Peta),
|
||||||
|
|
||||||
|
"ki" => Ok(Prefix::Kibi),
|
||||||
|
"Mi" => Ok(Prefix::Mebi),
|
||||||
|
"Gi" => Ok(Prefix::Gibi),
|
||||||
|
"Ti" => Ok(Prefix::Tebi),
|
||||||
|
"Pi" => Ok(Prefix::Pebi),
|
||||||
|
|
||||||
|
"kb" => Ok(Prefix::KiloBit),
|
||||||
|
"Mb" => Ok(Prefix::MegaBit),
|
||||||
|
"Gb" => Ok(Prefix::GigaBit),
|
||||||
|
|
||||||
|
_ => Err(Report::msg(format!("invalid prefix: {s}"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<char> for Alignment {
|
||||||
|
type Error = Report;
|
||||||
|
|
||||||
|
fn try_from(value: char) -> Result<Self> {
|
||||||
|
match value {
|
||||||
|
'<' => Ok(Self::Left),
|
||||||
|
'^' => Ok(Self::Center),
|
||||||
|
'>' => Ok(Self::Right),
|
||||||
|
_ => Err(Report::msg(format!("Unknown alignment: {value}"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formatting {
|
||||||
|
fn default_for(token_type: TokenType) -> Self {
|
||||||
|
match token_type {
|
||||||
|
TokenType::CpuFrequency
|
||||||
|
| TokenType::LoadAverage1
|
||||||
|
| TokenType::LoadAverage5
|
||||||
|
| TokenType::LoadAverage15 => Self {
|
||||||
|
width: 0,
|
||||||
|
fill: '0',
|
||||||
|
align: Alignment::default(),
|
||||||
|
precision: 2,
|
||||||
|
},
|
||||||
|
TokenType::CpuPercent => Self {
|
||||||
|
width: 2,
|
||||||
|
fill: '0',
|
||||||
|
align: Alignment::default(),
|
||||||
|
precision: 0,
|
||||||
|
},
|
||||||
|
TokenType::MemoryFree
|
||||||
|
| TokenType::MemoryAvailable
|
||||||
|
| TokenType::MemoryTotal
|
||||||
|
| TokenType::MemoryUsed
|
||||||
|
| TokenType::MemoryPercent
|
||||||
|
| TokenType::SwapFree
|
||||||
|
| TokenType::SwapTotal
|
||||||
|
| TokenType::SwapUsed
|
||||||
|
| TokenType::SwapPercent => Self {
|
||||||
|
width: 4,
|
||||||
|
fill: '0',
|
||||||
|
align: Alignment::default(),
|
||||||
|
precision: 1,
|
||||||
|
},
|
||||||
|
_ => Self {
|
||||||
|
width: 0,
|
||||||
|
fill: '0',
|
||||||
|
align: Alignment::default(),
|
||||||
|
precision: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_input(input: &str) -> Result<Vec<Part>> {
|
||||||
|
let mut tokens = vec![];
|
||||||
|
|
||||||
|
let mut chars = input.chars().peekable();
|
||||||
|
|
||||||
|
let mut next_char = chars.peek().copied();
|
||||||
|
while let Some(char) = next_char {
|
||||||
|
let token = if char == '{' {
|
||||||
|
chars.next();
|
||||||
|
parse_dynamic(&mut chars)?
|
||||||
|
} else {
|
||||||
|
parse_static(&mut chars)
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.push(token);
|
||||||
|
next_char = chars.peek().copied();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_static(chars: &mut Peekable<Chars>) -> Part {
|
||||||
|
let mut str = String::new();
|
||||||
|
|
||||||
|
let mut next_char = chars.next_if(|&c| c != '{');
|
||||||
|
while let Some(char) = next_char {
|
||||||
|
if char == '{' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
str.push(char);
|
||||||
|
next_char = chars.next_if(|&c| c != '{');
|
||||||
|
}
|
||||||
|
|
||||||
|
Part::Static(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
enum DynamicMode {
|
||||||
|
Token,
|
||||||
|
Name,
|
||||||
|
Prefix,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_dynamic(chars: &mut Peekable<Chars>) -> Result<Part> {
|
||||||
|
let mut mode = DynamicMode::Token;
|
||||||
|
|
||||||
|
let mut token_str = String::new();
|
||||||
|
let mut func_str = String::new();
|
||||||
|
let mut prefix_str = String::new();
|
||||||
|
|
||||||
|
// we don't want to peek here as that would be the same char as the outer loop
|
||||||
|
let mut next_char = chars.next();
|
||||||
|
while let Some(char) = next_char {
|
||||||
|
match char {
|
||||||
|
'}' | ':' => break,
|
||||||
|
'@' => mode = DynamicMode::Name,
|
||||||
|
'#' => mode = DynamicMode::Prefix,
|
||||||
|
_ => match mode {
|
||||||
|
DynamicMode::Token => token_str.push(char),
|
||||||
|
DynamicMode::Name => func_str.push(char),
|
||||||
|
DynamicMode::Prefix => prefix_str.push(char),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
next_char = chars.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let token_type = token_str.parse()?;
|
||||||
|
let mut formatting = Formatting::default_for(token_type);
|
||||||
|
|
||||||
|
if next_char == Some(':') {
|
||||||
|
formatting = parse_formatting(chars, formatting)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = Token {
|
||||||
|
token: token_type,
|
||||||
|
function: func_str
|
||||||
|
.parse()
|
||||||
|
.unwrap_or_else(|()| Function::default_for(token_type)),
|
||||||
|
prefix: prefix_str
|
||||||
|
.parse()
|
||||||
|
.unwrap_or_else(|_| Prefix::default_for(token_type)),
|
||||||
|
formatting,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Part::Token(token))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
enum FormattingMode {
|
||||||
|
WidthFillAlign,
|
||||||
|
Precision,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_formatting(chars: &mut Peekable<Chars>, mut formatting: Formatting) -> Result<Formatting> {
|
||||||
|
let mut width_string = String::new();
|
||||||
|
let mut precision_string = String::new();
|
||||||
|
|
||||||
|
let mut mode = FormattingMode::WidthFillAlign;
|
||||||
|
|
||||||
|
let mut next_char = chars.next();
|
||||||
|
while let Some(char) = next_char {
|
||||||
|
match (char, mode) {
|
||||||
|
('}', _) => break,
|
||||||
|
('.', _) => mode = FormattingMode::Precision,
|
||||||
|
(_, FormattingMode::Precision) => precision_string.push(char),
|
||||||
|
('1'..='9', FormattingMode::WidthFillAlign) => width_string.push(char),
|
||||||
|
('<' | '^' | '>', FormattingMode::WidthFillAlign) => {
|
||||||
|
formatting.align = Alignment::try_from(char)?;
|
||||||
|
}
|
||||||
|
(_, FormattingMode::WidthFillAlign) => formatting.fill = char,
|
||||||
|
};
|
||||||
|
|
||||||
|
next_char = chars.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if !width_string.is_empty() {
|
||||||
|
formatting.width = width_string.parse()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !precision_string.is_empty() {
|
||||||
|
formatting.precision = precision_string.parse()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(formatting)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn static_only() {
|
||||||
|
let tokens = parse_input("hello world").unwrap();
|
||||||
|
println!("{tokens:?}");
|
||||||
|
|
||||||
|
assert_eq!(tokens.len(), 1);
|
||||||
|
assert!(matches!(&tokens[0], Part::Static(str) if str == "hello world"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic() {
|
||||||
|
let tokens = parse_input("{cpu_frequency}").unwrap();
|
||||||
|
println!("{tokens:?}");
|
||||||
|
|
||||||
|
assert_eq!(tokens.len(), 1);
|
||||||
|
|
||||||
|
assert!(matches!(&tokens[0], Part::Token(_)));
|
||||||
|
let Part::Token(token) = tokens.get(0).unwrap() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(token.token, TokenType::CpuFrequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn named() {
|
||||||
|
let tokens = parse_input("{cpu_frequency@cpu0}").unwrap();
|
||||||
|
println!("{tokens:?}");
|
||||||
|
|
||||||
|
assert_eq!(tokens.len(), 1);
|
||||||
|
|
||||||
|
assert!(matches!(&tokens[0], Part::Token(_)));
|
||||||
|
let Part::Token(token) = tokens.get(0).unwrap() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(token.token, TokenType::CpuFrequency);
|
||||||
|
assert!(matches!(&token.function, Function::Name(n) if n == "cpu0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn conversion() {
|
||||||
|
let tokens = parse_input("{cpu_frequency#G}").unwrap();
|
||||||
|
println!("{tokens:?}");
|
||||||
|
|
||||||
|
assert_eq!(tokens.len(), 1);
|
||||||
|
|
||||||
|
assert!(matches!(&tokens[0], Part::Token(_)));
|
||||||
|
let Part::Token(token) = tokens.get(0).unwrap() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(token.token, TokenType::CpuFrequency);
|
||||||
|
assert_eq!(token.prefix, Prefix::Giga);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formatting_basic() {
|
||||||
|
let tokens = parse_input("{cpu_frequency:.2}").unwrap();
|
||||||
|
println!("{tokens:?}");
|
||||||
|
|
||||||
|
assert_eq!(tokens.len(), 1);
|
||||||
|
|
||||||
|
assert!(matches!(&tokens[0], Part::Token(_)));
|
||||||
|
let Part::Token(token) = tokens.get(0).unwrap() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(token.token, TokenType::CpuFrequency);
|
||||||
|
assert_eq!(token.formatting.precision, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formatting_complex() {
|
||||||
|
let tokens = parse_input("{cpu_frequency:0<5.2}").unwrap();
|
||||||
|
println!("{tokens:?}");
|
||||||
|
|
||||||
|
assert_eq!(tokens.len(), 1);
|
||||||
|
|
||||||
|
assert!(matches!(&tokens[0], Part::Token(_)));
|
||||||
|
let Part::Token(token) = tokens.get(0).unwrap() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(token.token, TokenType::CpuFrequency);
|
||||||
|
assert_eq!(token.formatting.fill, '0');
|
||||||
|
assert_eq!(token.formatting.align, Alignment::Left);
|
||||||
|
assert_eq!(token.formatting.width, 5);
|
||||||
|
assert_eq!(token.formatting.precision, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complex() {
|
||||||
|
let tokens = parse_input("{cpu_frequency@cpu0#G:.2}").unwrap();
|
||||||
|
println!("{tokens:?}");
|
||||||
|
|
||||||
|
assert_eq!(tokens.len(), 1);
|
||||||
|
|
||||||
|
assert!(matches!(&tokens[0], Part::Token(_)));
|
||||||
|
let Part::Token(token) = tokens.get(0).unwrap() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(token.token, TokenType::CpuFrequency);
|
||||||
|
assert!(matches!(&token.function, Function::Name(n) if n == "cpu0"));
|
||||||
|
assert_eq!(token.prefix, Prefix::Giga);
|
||||||
|
assert_eq!(token.formatting.precision, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn static_then_token() {
|
||||||
|
let tokens = parse_input("Freq: {cpu_frequency#G:.2}").unwrap();
|
||||||
|
println!("{tokens:?}");
|
||||||
|
|
||||||
|
assert_eq!(tokens.len(), 2);
|
||||||
|
|
||||||
|
assert!(matches!(&tokens[0], Part::Static(str) if str == "Freq: "));
|
||||||
|
|
||||||
|
assert!(matches!(&tokens[1], Part::Token(_)));
|
||||||
|
let Part::Token(token) = tokens.get(1).unwrap() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(token.token, TokenType::CpuFrequency);
|
||||||
|
assert_eq!(token.formatting.precision, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn token_then_static() {
|
||||||
|
let tokens = parse_input("{cpu_frequency#G:.2} GHz").unwrap();
|
||||||
|
println!("{tokens:?}");
|
||||||
|
|
||||||
|
assert_eq!(tokens.len(), 2);
|
||||||
|
|
||||||
|
assert!(matches!(&tokens[0], Part::Token(_)));
|
||||||
|
let Part::Token(token) = tokens.get(0).unwrap() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(token.token, TokenType::CpuFrequency);
|
||||||
|
assert_eq!(token.formatting.precision, 2);
|
||||||
|
|
||||||
|
assert!(matches!(&tokens[1], Part::Static(str) if str == " GHz"));
|
||||||
|
}
|
||||||
|
}
|
91
src/modules/sysinfo/renderer.rs
Normal file
91
src/modules/sysinfo/renderer.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
use super::token::{Alignment, Part, Token, TokenType};
|
||||||
|
use super::Interval;
|
||||||
|
use crate::clients;
|
||||||
|
use crate::clients::sysinfo::{Value, ValueSet};
|
||||||
|
|
||||||
|
pub enum TokenValue {
|
||||||
|
Number(f64),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Part {
|
||||||
|
pub fn render_all(
|
||||||
|
tokens: &[Self],
|
||||||
|
client: &clients::sysinfo::Client,
|
||||||
|
interval: Interval,
|
||||||
|
) -> String {
|
||||||
|
tokens
|
||||||
|
.iter()
|
||||||
|
.map(|part| part.render(client, interval))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self, client: &clients::sysinfo::Client, interval: Interval) -> String {
|
||||||
|
match self {
|
||||||
|
Part::Static(str) => str.clone(),
|
||||||
|
Part::Token(token) => {
|
||||||
|
match token.get(client, interval) {
|
||||||
|
TokenValue::Number(value) => {
|
||||||
|
let fmt = token.formatting;
|
||||||
|
let mut str = format!("{value:.precision$}", precision = fmt.precision);
|
||||||
|
|
||||||
|
// fill/align doesn't support parameterization so we need our own impl
|
||||||
|
let mut add_to_end = fmt.align == Alignment::Right;
|
||||||
|
while str.len() < fmt.width {
|
||||||
|
if add_to_end {
|
||||||
|
str.push(fmt.fill);
|
||||||
|
} else {
|
||||||
|
str.insert(0, fmt.fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.align == Alignment::Center {
|
||||||
|
add_to_end = !add_to_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str
|
||||||
|
}
|
||||||
|
TokenValue::String(value) => value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token {
|
||||||
|
pub fn get(&self, client: &clients::sysinfo::Client, interval: Interval) -> TokenValue {
|
||||||
|
let get = |value: Value| TokenValue::Number(value.get(self.prefix));
|
||||||
|
let apply = |set: ValueSet| TokenValue::Number(set.apply(&self.function, self.prefix));
|
||||||
|
|
||||||
|
match self.token {
|
||||||
|
// Number tokens
|
||||||
|
TokenType::CpuFrequency => apply(client.cpu_frequency()),
|
||||||
|
TokenType::CpuPercent => apply(client.cpu_percent()),
|
||||||
|
TokenType::MemoryFree => get(client.memory_free()),
|
||||||
|
TokenType::MemoryAvailable => get(client.memory_available()),
|
||||||
|
TokenType::MemoryTotal => get(client.memory_total()),
|
||||||
|
TokenType::MemoryUsed => get(client.memory_used()),
|
||||||
|
TokenType::MemoryPercent => get(client.memory_percent()),
|
||||||
|
TokenType::SwapFree => get(client.swap_free()),
|
||||||
|
TokenType::SwapTotal => get(client.swap_total()),
|
||||||
|
TokenType::SwapUsed => get(client.swap_used()),
|
||||||
|
TokenType::SwapPercent => get(client.swap_percent()),
|
||||||
|
TokenType::TempC => apply(client.temp_c()),
|
||||||
|
TokenType::TempF => apply(client.temp_f()),
|
||||||
|
TokenType::DiskFree => apply(client.disk_free()),
|
||||||
|
TokenType::DiskTotal => apply(client.disk_total()),
|
||||||
|
TokenType::DiskUsed => apply(client.disk_used()),
|
||||||
|
TokenType::DiskPercent => apply(client.disk_percent()),
|
||||||
|
TokenType::DiskRead => apply(client.disk_read(interval)),
|
||||||
|
TokenType::DiskWrite => apply(client.disk_write(interval)),
|
||||||
|
TokenType::NetDown => apply(client.net_down(interval)),
|
||||||
|
TokenType::NetUp => apply(client.net_up(interval)),
|
||||||
|
TokenType::LoadAverage1 => get(client.load_average_1()),
|
||||||
|
TokenType::LoadAverage5 => get(client.load_average_5()),
|
||||||
|
TokenType::LoadAverage15 => get(client.load_average_15()),
|
||||||
|
|
||||||
|
// String tokens
|
||||||
|
TokenType::Uptime => TokenValue::String(client.uptime()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
src/modules/sysinfo/token.rs
Normal file
66
src/modules/sysinfo/token.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use crate::clients::sysinfo::{Function, Prefix};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum TokenType {
|
||||||
|
CpuFrequency,
|
||||||
|
CpuPercent,
|
||||||
|
|
||||||
|
MemoryFree,
|
||||||
|
MemoryAvailable,
|
||||||
|
MemoryTotal,
|
||||||
|
MemoryUsed,
|
||||||
|
MemoryPercent,
|
||||||
|
|
||||||
|
SwapFree,
|
||||||
|
SwapTotal,
|
||||||
|
SwapUsed,
|
||||||
|
SwapPercent,
|
||||||
|
|
||||||
|
TempC,
|
||||||
|
TempF,
|
||||||
|
|
||||||
|
DiskFree,
|
||||||
|
DiskTotal,
|
||||||
|
DiskUsed,
|
||||||
|
DiskPercent,
|
||||||
|
DiskRead,
|
||||||
|
DiskWrite,
|
||||||
|
|
||||||
|
NetDown,
|
||||||
|
NetUp,
|
||||||
|
|
||||||
|
LoadAverage1,
|
||||||
|
LoadAverage5,
|
||||||
|
LoadAverage15,
|
||||||
|
Uptime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Token {
|
||||||
|
pub token: TokenType,
|
||||||
|
pub function: Function,
|
||||||
|
pub prefix: Prefix,
|
||||||
|
pub formatting: Formatting,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Part {
|
||||||
|
Static(String),
|
||||||
|
Token(Token),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Formatting {
|
||||||
|
pub width: usize,
|
||||||
|
pub fill: char,
|
||||||
|
pub align: Alignment,
|
||||||
|
pub precision: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
|
pub enum Alignment {
|
||||||
|
#[default]
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right,
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue