Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .github/assets/rust-cli-sysinfo_help.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified .github/assets/rust-cli-sysinfo_tokens.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rust-cli-sysinfo"
version = "0.7.0"
version = "0.8.0"
edition = "2024"
build = "build.rs"

Expand All @@ -25,4 +25,4 @@
all = "warn"
pedantic = "warn"
cognitive_complexity = "warn"
panic = "warn"
panic = "warn"
2 changes: 1 addition & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub const SEP: &str =

/// Label width for left-aligned labels
///
pub const LABEL_WIDTH: usize = 14;
pub const LABEL_WIDTH: usize = 16;

/// Application version, sourced from Cargo package version
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
Expand Down
7 changes: 5 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@ fn render_labeled(registry: &ServiceRegistry, c: &Colors) {
.max()
.unwrap_or(4);

println!("\n{INDENT}{}{}{}\n{INDENT}{}{}", c.bold, c.cyan, APP_NAME, SEP, c.reset);
println!(
"\n{INDENT}{}{}{}\n{INDENT}{}{}",
c.bold, c.cyan, APP_NAME, SEP, c.reset
);
println!(
"{INDENT}To configure the services displayed, separate each service token with a\n{INDENT}hyphen (-) in the desired order.\n"
);
println!("{INDENT}Available service tokens:\n");
println!("{INDENT}Available service tokens (default order):\n");

for meta in registry.all_meta() {
println!(
Expand Down
2 changes: 1 addition & 1 deletion src/services/cpu_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub fn descriptor(_ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>
token: "CPU",
label: "CPU",
description: "CPU model",
sort_order: 2,
sort_order: 10,
},
Box::new(CpuModelService),
)
Expand Down
2 changes: 1 addition & 1 deletion src/services/cpu_usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ pub fn descriptor(ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>)
token: "CPUU",
label: "CPU usage",
description: "CPU usage %",
sort_order: 7,
sort_order: 100,
},
Box::new(CpuUsageService::new(ctx)),
)
Expand Down
2 changes: 1 addition & 1 deletion src/services/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ pub fn descriptor(ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>)
token: "DSKU",
label: "Disk usage",
description: "Disk usage % (Used/Total)",
sort_order: 9,
sort_order: 120,
},
Box::new(DiskService::new(ctx)),
)
Expand Down
2 changes: 1 addition & 1 deletion src/services/gpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ pub fn descriptor(_ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>
token: "GPU",
label: "GPU(s)",
description: "GPU model(s)",
sort_order: 3,
sort_order: 20,
},
Box::new(GpuService),
)
Expand Down
2 changes: 1 addition & 1 deletion src/services/hostname.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub fn descriptor(_ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>
token: "HST",
label: "Hostname",
description: "System hostname",
sort_order: 1,
sort_order: 60,
},
Box::new(HostnameService),
)
Expand Down
114 changes: 114 additions & 0 deletions src/services/ip_address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use super::prelude::*;
use std::net::UdpSocket;

/// `ActiveIpInfo` contains the primary IP address currently selected by the OS
/// routing table for outbound traffic
#[derive(Default)]
pub struct ActiveIpInfo {
pub address: String,
}

/// Per RFC 5737/3849, the values of`IP4_ADDR` and `IP6_ADDR` are used to determine the active
/// IP address by connecting a UDP socket to a known public address (no packets are sent)
const IP4_ADDR: &str = "1.1.1.1:80";
const IP6_ADDR: &str = "[2606:4700:4700::1111]:80";

/// `ActiveIpService` collects the active local IP address
pub struct ActiveIpService;

/// `active_ip()` attempts to determine the outbound IPv4 or IPv6 address
///
fn active_ip() -> Result<String, AppError> {
// Try IPv4 first
if let Ok(socket) = UdpSocket::bind("0.0.0.0:0")
&& socket.connect(IP4_ADDR).is_ok()
&& let Ok(addr) = socket.local_addr()
{
return Ok(addr.ip().to_string());
}

// Fall back to IPv6
if let Ok(socket) = UdpSocket::bind("[::]:0")
&& socket.connect(IP6_ADDR).is_ok()
&& let Ok(addr) = socket.local_addr()
{
return Ok(addr.ip().to_string());
}

// Give up...
Err(AppError::DataUnavailable(
"Unable to determine active IP address".into(),
))
}

impl Service for ActiveIpService {
type Data = ActiveIpInfo;

/// `collect()` attempts to determine the active IP address by connecting a UDP socket to a known public address
///
fn collect(&self) -> Result<Self::Data, AppError> {
Ok(ActiveIpInfo {
address: active_ip()?,
})
}

/// `render()` displays the active IP address
///
fn render(&self, label: &str, data: &Self::Data, c: &Colors) -> Result<(), AppError> {
print_row(label, &data.address, &Threshold::None, c);

Ok(())
}
}

/// `descriptor()` is this service's registration point
///
pub fn descriptor(_ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>) {
(
ServiceMeta {
token: "IP",
label: "IP Address",
description: "Active IP address",
sort_order: 50,
},
Box::new(ActiveIpService),
)
}

#[cfg(test)]
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
mod tests {
use super::*;
use crate::presentation::colors::Colors;
use std::net::IpAddr;

/// `collect_returns_ok()` asserts that the `collect()` method returns a successful result
///
#[test]
fn collect_returns_ok() {
let result = ActiveIpService.collect();
assert!(result.is_ok());
}

/// `active_ip_is_valid()` asserts that the active IP address is a valid IP address
///
#[test]
fn active_ip_is_valid() {
let data = ActiveIpService.collect().unwrap();

let ip = data.address;

assert!(ip.parse::<IpAddr>().is_ok());
}

/// `render_does_not_panic()` asserts that the `render()` method won't panic
///
#[test]
fn render_does_not_panic() {
let data = ActiveIpService.collect().unwrap();

ActiveIpService
.render("IP", &data, &Colors::new(false))
.unwrap();
}
}
2 changes: 1 addition & 1 deletion src/services/kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub fn descriptor(_ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>
token: "KNL",
label: "Kernel",
description: "Linux kernel version",
sort_order: 4,
sort_order: 40,
},
Box::new(KernelService),
)
Expand Down
2 changes: 1 addition & 1 deletion src/services/load_avg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub fn descriptor(_ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>
token: "LOAD",
label: "Load averages",
description: "Load averages (1m, 5m, 15m)",
sort_order: 6,
sort_order: 90,
},
Box::new(LoadAvgService),
)
Expand Down
2 changes: 1 addition & 1 deletion src/services/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ pub fn descriptor(_ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>
token: "RAMU",
label: "Memory usage",
description: "Memory usage % (Used/Total)",
sort_order: 8,
sort_order: 110,
},
Box::new(MemoryService),
)
Expand Down
2 changes: 1 addition & 1 deletion src/services/os_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn descriptor(_ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>
token: "OS",
label: "OS",
description: "Operating system name and version",
sort_order: 0,
sort_order: 30,
},
Box::new(OsService),
)
Expand Down
2 changes: 1 addition & 1 deletion src/services/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub fn descriptor(_ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>
token: "UPT",
label: "Uptime",
description: "System uptime",
sort_order: 5,
sort_order: 80,
},
Box::new(UptimeService),
)
Expand Down
2 changes: 1 addition & 1 deletion src/services/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub fn descriptor(_ctx: &ServiceContext) -> (ServiceMeta, Box<dyn ErasedService>
token: "USR",
label: "User(s)",
description: "Current users",
sort_order: 10,
sort_order: 70,
},
Box::new(UsersService),
)
Expand Down