#[cfg(not(unix))]
fn main() {
eprintln!("pinky: only available on Unix");
std::process::exit(1);
}
#[cfg(unix)]
use std::process;
#[cfg(unix)]
use coreutils_rs::pinky;
#[cfg(unix)]
const TOOL_NAME: &str = "pinky";
#[cfg(unix)]
const VERSION: &str = env!("CARGO_PKG_VERSION");
#[cfg(unix)]
fn print_help() {
println!("Usage: {} [OPTION]... [USER]...", TOOL_NAME);
println!("Lightweight finger.");
println!();
println!(" -l produce long format output for the specified USERs");
println!(" -b omit the user's home directory and shell in long format");
println!(" -h omit the user's project file in long format");
println!(" -p omit the user's plan file in long format");
println!(" -s do short format output (default)");
println!(" -f omit the column of full names in short format");
println!(" -w omit the user's full name in short format");
println!(" -i omit the user's full name and remote host in short format");
println!(" -q omit the user's full name, remote host and idle time");
println!(" --help display this help and exit");
println!(" --version output version information and exit");
}
#[cfg(unix)]
fn main() {
coreutils_rs::common::reset_sigpipe();
let mut long_format = false;
let mut omit_home_shell = false;
let mut omit_project = false;
let mut omit_plan = false;
let mut omit_heading = false;
let mut omit_fullname = false;
let mut omit_fullname_host = false;
let mut omit_fullname_host_idle = false;
let mut users: Vec<String> = Vec::new();
for arg in std::env::args_os().skip(1) {
let arg = arg.to_string_lossy();
match arg.as_ref() {
"--help" => {
print_help();
return;
}
"--version" => {
println!("{} (fcoreutils) {}", TOOL_NAME, VERSION);
return;
}
s if s.starts_with('-') && s.len() > 1 && !s.starts_with("--") => {
for ch in s[1..].chars() {
match ch {
'l' => long_format = true,
'b' => omit_home_shell = true,
'h' => omit_project = true,
'p' => omit_plan = true,
's' => {} 'f' => omit_heading = true,
'w' => omit_fullname = true,
'i' => omit_fullname_host = true,
'q' => omit_fullname_host_idle = true,
_ => {
eprintln!("{}: invalid option -- '{}'", TOOL_NAME, ch);
eprintln!("Try '{} --help' for more information.", TOOL_NAME);
process::exit(1);
}
}
}
}
_ => users.push(arg.into_owned()),
}
}
let short_format = !long_format;
let config = pinky::PinkyConfig {
long_format,
short_format,
omit_home_shell,
omit_project,
omit_plan,
omit_heading,
omit_fullname,
omit_fullname_host,
omit_fullname_host_idle,
users,
};
let output = pinky::run_pinky(&config);
if !output.is_empty() {
println!("{}", output);
}
process::exit(0);
}
#[cfg(test)]
mod tests {
use std::process::Command;
fn cmd() -> Command {
let mut path = std::env::current_exe().unwrap();
path.pop();
path.pop();
path.push("fpinky");
Command::new(path)
}
#[cfg(unix)]
#[test]
fn test_pinky_runs() {
let output = cmd().output().unwrap();
assert!(
output.status.success(),
"fpinky should exit with code 0, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[cfg(unix)]
#[test]
fn test_pinky_short() {
let output = cmd().output().unwrap();
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
if !stdout.trim().is_empty() {
assert!(
stdout.contains("Login"),
"Short format heading should contain 'Login', got: {}",
stdout
);
}
}
#[cfg(unix)]
#[test]
fn test_pinky_long() {
let whoami = Command::new("whoami").output();
if let Ok(whoami) = whoami
&& whoami.status.success()
{
let username = String::from_utf8_lossy(&whoami.stdout).trim().to_string();
if !username.is_empty() {
let output = cmd().args(["-l", &username]).output().unwrap();
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Login name:"),
"Long format should contain 'Login name:', got: {}",
stdout
);
}
}
}
#[cfg(unix)]
#[test]
fn test_pinky_specific_user() {
let whoami = Command::new("whoami").output();
if let Ok(whoami) = whoami
&& whoami.status.success()
{
let username = String::from_utf8_lossy(&whoami.stdout).trim().to_string();
if !username.is_empty() {
let output = cmd().arg(&username).output().unwrap();
assert!(
output.status.success(),
"pinky should succeed for specific user"
);
}
}
}
#[cfg(unix)]
#[test]
fn test_pinky_matches_gnu_format() {
let gnu = Command::new("pinky").output();
if let Ok(gnu) = gnu {
let ours = cmd().output().unwrap();
assert_eq!(
ours.status.code(),
gnu.status.code(),
"Exit code mismatch: ours={:?} gnu={:?}",
ours.status.code(),
gnu.status.code()
);
let gnu_lines = String::from_utf8_lossy(&gnu.stdout).lines().count();
let our_lines = String::from_utf8_lossy(&ours.stdout).lines().count();
assert_eq!(
our_lines, gnu_lines,
"Line count mismatch: ours={} gnu={}",
our_lines, gnu_lines
);
}
}
#[cfg(unix)]
#[test]
fn test_long_format_via_binary() {
let whoami_output = Command::new("whoami").output();
if let Ok(whoami) = whoami_output
&& whoami.status.success()
{
let username = String::from_utf8_lossy(&whoami.stdout).trim().to_string();
if !username.is_empty() {
let output = cmd().args(["-l", &username]).output().unwrap();
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Login name:"),
"Long format should contain 'Login name:', got: {}",
stdout
);
assert!(
stdout.contains(&username),
"Long format should contain the username"
);
}
}
}
#[cfg(unix)]
#[test]
fn test_long_format_omit_flags() {
let whoami_output = Command::new("whoami").output();
if let Ok(whoami) = whoami_output
&& whoami.status.success()
{
let username = String::from_utf8_lossy(&whoami.stdout).trim().to_string();
if !username.is_empty() {
let output = cmd()
.args(["-l", "-b", "-h", "-p", &username])
.output()
.unwrap();
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("Login name:"));
assert!(
!stdout.contains("Directory:"),
"With -b, should omit directory line"
);
}
}
}
#[cfg(unix)]
#[test]
fn test_pinky_basic() {
let output = cmd().output().unwrap();
assert!(output.status.success());
}
#[cfg(unix)]
#[test]
fn test_pinky_short_format() {
let output = cmd().arg("-s").output().unwrap();
assert!(output.status.success());
}
#[cfg(unix)]
#[test]
fn test_pinky_long_format() {
let user = std::env::var("USER").unwrap_or_else(|_| "root".to_string());
let output = cmd().arg(&user).output().unwrap();
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(!stdout.is_empty());
}
#[cfg(unix)]
#[test]
fn test_pinky_nonexistent_user() {
let output = cmd().arg("nonexistent_user_xyz_12345").output().unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let _ = stdout;
}
}