From 7a0de2b097e59a2541a3c55998a96d56db0d3e0f Mon Sep 17 00:00:00 2001 From: Jeron Aldaron Lau Date: Thu, 17 Aug 2023 17:42:12 -0500 Subject: [PATCH] Fallible for `username()`, `realname()` and `_os` varaints --- src/fake.rs | 14 +++++++------- src/fallible.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 14 ++++++++++---- src/unix.rs | 25 +++++++++++++------------ src/web.rs | 16 ++++++++-------- src/windows.rs | 23 ++++++++++++----------- 6 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 src/fallible.rs diff --git a/src/fake.rs b/src/fake.rs index eb3a897..40cb977 100644 --- a/src/fake.rs +++ b/src/fake.rs @@ -13,13 +13,13 @@ pub(crate) fn lang() -> impl Iterator { } #[inline(always)] -pub(crate) fn username_os() -> OsString { - username().into() +pub(crate) fn username_os() -> Result { + Ok(username()?.into()) } #[inline(always)] -pub(crate) fn realname_os() -> OsString { - realname().into() +pub(crate) fn realname_os() -> Result { + Ok(realname()?.into()) } #[inline(always)] @@ -33,12 +33,12 @@ pub(crate) fn distro_os() -> Option { } #[inline(always)] -pub(crate) fn username() -> String { - "anonymous".to_string() +pub(crate) fn username() -> Result { + Ok("anonymous".to_string()) } #[inline(always)] -pub(crate) fn realname() -> String { +pub(crate) fn realname() -> Result { "Anonymous".to_string() } diff --git a/src/fallible.rs b/src/fallible.rs new file mode 100644 index 0000000..b95808e --- /dev/null +++ b/src/fallible.rs @@ -0,0 +1,39 @@ +//! Fallible versions of the whoami APIs. +//! +//! Some of the functions in the root module will return "Unknown" on error. +//! This might not be desirable in some situations. The functions in this +//! module all return a [`Result`]. + +use std::ffi::OsString; + +use crate::{platform, Result}; + +/// Get the user's username. +/// +/// On unix-systems this differs from [`realname()`] most notably in that spaces +/// are not allowed. +#[inline(always)] +pub fn username() -> Result { + platform::username() +} + +/// Get the user's username. +/// +/// On unix-systems this differs from [`realname()`] most notably in that spaces +/// are not allowed. +#[inline(always)] +pub fn username_os() -> Result { + platform::username_os() +} + +/// Get the user's real (full) name. +#[inline(always)] +pub fn realname() -> Result { + platform::realname() +} + +/// Get the user's real (full) name. +#[inline(always)] +pub fn realname_os() -> Result { + platform::realname_os() +} diff --git a/src/lib.rs b/src/lib.rs index f27acd5..4bfa47f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,10 @@ html_favicon_url = "https://raw.githubusercontent.com/ardaku/whoami/stable/res/icon.svg" )] +const DEFAULT_USERNAME: &str = "Unknown"; + +pub mod fallible; + #[allow(unsafe_code)] // Unix #[cfg_attr( @@ -395,7 +399,7 @@ pub fn arch() -> Arch { /// are not allowed. #[inline(always)] pub fn username() -> String { - platform::username() + fallible::username().unwrap_or_else(|_| DEFAULT_USERNAME.to_owned()) } /// Get the user's username. @@ -404,19 +408,21 @@ pub fn username() -> String { /// are not allowed. #[inline(always)] pub fn username_os() -> OsString { - platform::username_os() + fallible::username_os() + .unwrap_or_else(|_| DEFAULT_USERNAME.to_owned().into()) } /// Get the user's real (full) name. #[inline(always)] pub fn realname() -> String { - platform::realname() + fallible::realname().unwrap_or_else(|_| DEFAULT_USERNAME.to_owned()) } /// Get the user's real (full) name. #[inline(always)] pub fn realname_os() -> OsString { - platform::realname_os() + fallible::realname_os() + .unwrap_or_else(|_| DEFAULT_USERNAME.to_owned().into()) } /// Get the device name (also known as "Pretty Name"). diff --git a/src/unix.rs b/src/unix.rs index a1e262c..b18c052 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -1,6 +1,7 @@ use std::{ borrow::Cow, ffi::{c_void, CStr, OsString}, + io::{Error, ErrorKind}, mem, os::{ raw::{c_char, c_int}, @@ -16,7 +17,7 @@ use std::{ ptr::null_mut, }; -use crate::{Arch, DesktopEnv, Platform}; +use crate::{Arch, DesktopEnv, Platform, Result}; #[repr(C)] struct PassWd { @@ -192,7 +193,7 @@ fn os_from_cfstring(string: *mut c_void) -> OsString { // This function must allocate, because a slice or Cow would still // reference `passwd` which is dropped when this function returns. #[inline(always)] -fn getpwuid(real: bool) -> OsString { +fn getpwuid(real: bool) -> Result { const BUF_SIZE: usize = 16_384; // size from the man page let mut buffer = mem::MaybeUninit::<[u8; BUF_SIZE]>::uninit(); let mut passwd = mem::MaybeUninit::::uninit(); @@ -209,20 +210,20 @@ fn getpwuid(real: bool) -> OsString { ); if ret != 0 { - return "Unknown".to_string().into(); + return Err(Error::last_os_error()); } let _passwd = _passwd.assume_init(); if _passwd.is_null() { - return "Unknown".to_string().into(); + return Err(Error::new(ErrorKind::NotFound, "Missing record")); } passwd.assume_init() }; // Extract names. - if real { + Ok(if real { let string = os_from_cstring_gecos(passwd.pw_gecos); let result = if let Some(string) = string { Ok(string) @@ -232,14 +233,14 @@ fn getpwuid(real: bool) -> OsString { fancy_fallback_os(result) } else { os_from_cstring(passwd.pw_name) - } + }) } -pub(crate) fn username() -> String { - string_from_os(username_os()) +pub(crate) fn username() -> Result { + Ok(string_from_os(username_os()?)) } -pub(crate) fn username_os() -> OsString { +pub(crate) fn username_os() -> Result { getpwuid(false) } @@ -285,11 +286,11 @@ fn fancy_fallback_os(result: Result) -> OsString { } } -pub(crate) fn realname() -> String { - string_from_os(realname_os()) +pub(crate) fn realname() -> Result { + Ok(string_from_os(realname_os()?)) } -pub(crate) fn realname_os() -> OsString { +pub(crate) fn realname_os() -> Result { getpwuid(true) } diff --git a/src/web.rs b/src/web.rs index 0cc24c5..108c2de 100644 --- a/src/web.rs +++ b/src/web.rs @@ -53,13 +53,13 @@ pub(crate) fn lang() -> impl Iterator { } #[inline(always)] -pub(crate) fn username_os() -> OsString { - username().into() +pub(crate) fn username_os() -> Result { + Ok(username()?.into()) } #[inline(always)] -pub(crate) fn realname_os() -> OsString { - realname().into() +pub(crate) fn realname_os() -> Result { + Ok(realname()?.into()) } #[inline(always)] @@ -73,13 +73,13 @@ pub(crate) fn distro_os() -> Option { } #[inline(always)] -pub(crate) fn username() -> String { - "anonymous".to_string() +pub(crate) fn username() -> Result { + Ok("anonymous".to_string()) } #[inline(always)] -pub(crate) fn realname() -> String { - "Anonymous".to_string() +pub(crate) fn realname() -> Result { + Ok("Anonymous".to_string()) } pub(crate) fn devicename() -> String { diff --git a/src/windows.rs b/src/windows.rs index 20d731d..aaef892 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,6 +1,7 @@ use std::{ convert::TryInto, ffi::OsString, + io::Error, mem::MaybeUninit, os::{ raw::{c_char, c_int, c_uchar, c_ulong, c_ushort, c_void}, @@ -9,7 +10,7 @@ use std::{ ptr, }; -use crate::{Arch, DesktopEnv, Platform}; +use crate::{Arch, DesktopEnv, Platform, Result}; #[repr(C)] struct OsVersionInfoEx { @@ -109,11 +110,11 @@ fn string_from_os(string: OsString) -> String { } } -pub(crate) fn username() -> String { - string_from_os(username_os()) +pub(crate) fn username() -> Result { + Ok(string_from_os(username_os()?)) } -pub(crate) fn username_os() -> OsString { +pub(crate) fn username_os() -> Result { // Step 1. Retreive the entire length of the username let mut size = 0; let success = unsafe { @@ -130,7 +131,7 @@ pub(crate) fn username_os() -> OsString { let fail = unsafe { GetUserNameW(name.as_mut_ptr().cast(), &mut size) == 0 }; if fail { - return "unknown".to_string().into(); + return Err(Error::last_os_error()); } debug_assert_eq!(orig_size, size); unsafe { @@ -140,16 +141,16 @@ pub(crate) fn username_os() -> OsString { debug_assert_eq!(terminator, Some(0u16)); // Step 3. Convert to Rust String - OsString::from_wide(&name) + Ok(OsString::from_wide(&name)) } #[inline(always)] -pub(crate) fn realname() -> String { - string_from_os(realname_os()) +pub(crate) fn realname() -> Result { + Ok(string_from_os(realname_os()?)) } #[inline(always)] -pub(crate) fn realname_os() -> OsString { +pub(crate) fn realname_os() -> Result { // Step 1. Retrieve the entire length of the username let mut buf_size = 0; let success = unsafe { @@ -180,7 +181,7 @@ pub(crate) fn realname_os() -> OsString { ) == 0 }; if fail { - return "Unknown".to_string().into(); + return Err(Error::last_os_error()); } debug_assert_eq!(buf_size, name_len + 1); unsafe { @@ -188,7 +189,7 @@ pub(crate) fn realname_os() -> OsString { } // Step 3. Convert to Rust String - OsString::from_wide(&name) + Ok(OsString::from_wide(&name)) } #[inline(always)]