//! The name documents sign new comments and changes with. use std::io; /// The author name from settings, else the account's. pub fn name() -> io::Result { let set = crate::settings::Shared::load()?.author; if !set.is_empty() { return Ok(set); } account() } /// The account's full name, else its login name. pub fn account() -> io::Result { let full = full_name()?; if !full.trim().is_empty() { return Ok(full.trim().to_string()); } let key = if cfg!(windows) { "USERNAME" } else { "USER" }; log::info!("user: no full name; the login name signs"); std::env::var(key).map_err(|error| io::Error::other(format!("no {key}: {error}"))) } #[cfg(target_os = "macos")] fn full_name() -> io::Result { Ok(objc2_foundation::NSFullUserName().to_string()) } #[cfg(target_os = "linux")] fn full_name() -> io::Result { // The first comma field of the passwd comment. let entry = unsafe { libc::getpwuid(libc::getuid()) }; if entry.is_null() { return Err(io::Error::last_os_error()); } let gecos = unsafe { (*entry).pw_gecos }; if gecos.is_null() { return Ok(String::new()); } let text = unsafe { std::ffi::CStr::from_ptr(gecos) }.to_string_lossy(); Ok(text.split(',').next().unwrap_or_default().to_string()) } #[cfg(windows)] fn full_name() -> io::Result { use windows_sys::Win32::Security::Authentication::Identity::{GetUserNameExW, NameDisplay}; let mut size = 0u32; unsafe { GetUserNameExW(NameDisplay, std::ptr::null_mut(), &mut size) }; // Accounts outside a domain have no display name. if size == 0 { return Ok(String::new()); } let mut buffer = vec![0u16; size as usize]; if unsafe { GetUserNameExW(NameDisplay, buffer.as_mut_ptr(), &mut size) } == 0 { return Err(io::Error::last_os_error()); } Ok(String::from_utf16_lossy(&buffer[..size as usize])) } #[cfg(test)] mod tests { #[test] fn the_account_has_a_name() { let name = super::name().expect("a name"); assert!(!name.is_empty()); } }