From 2271b98c1bbb81f8b7a84559cbc01eb8428b8486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=E1=BA=A3=20th=E1=BA=BF=20gi=E1=BB=9Bi=20l=C3=A0=20Rust?= <90588855+naoNao89@users.noreply.github.com> Date: Thu, 27 Nov 2025 20:41:41 +0700 Subject: [PATCH] Add processor() method to UNameAPI trait (#96) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add processor() method to UNameAPI trait Implements processor type mapping to provide GNU coreutils-compatible processor information, addressing uutils/coreutils#8659. Changes: - Add processor() method to UNameAPI trait with comprehensive docs - Implement map_processor() helper in lib_impl for arch-to-processor mapping - Add processor field and implementation to all platform modules (Unix, Windows, unknown) - Include platform-specific tests verifying correct processor mappings - Update example code to demonstrate processor() usage - Add workspace declaration to Cargo.toml to prevent parent workspace inheritance Processor mapping behavior: - macOS: arm64 → arm - Linux: aarch64 → aarch64 (passthrough) - x86_64/amd64 → x86_64 - i386/i486/i586/i686 → i686 - ARMv6/v7/v8 variants → arm - Unknown architectures pass through unchanged (better than "unknown") This provides the foundation for uutils/coreutils to migrate from its local processor mapping implementation, consolidating platform knowledge in the appropriate abstraction layer. Ref: uutils/coreutils#8659 * docs: update README to include processor() method Add processor() to example code and expected output as requested by @sylvestre * docs: clarify processor() vs machine() difference and add comprehensive tests Address review feedback: - Remove incomplete archive.is placeholder - Add detailed documentation explaining how processor() differs from machine() - Include comparison table showing platform-specific behavior - Add comprehensive unit test for map_processor() covering all branches - Improves test coverage from 66.66% to 100% in lib_impl.rs --- README.md | 2 ++ examples/ex.rs | 1 + src/lib.rs | 8 +++++ src/lib_impl.rs | 66 +++++++++++++++++++++++++++++++++++++++++ src/platform/unix.rs | 32 +++++++++++++++++++- src/platform/unknown.rs | 5 ++++ src/platform/windows.rs | 31 +++++++++++++++++++ 7 files changed, 144 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 216e3d0..ede0582 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ fn main() { println!("{}", info.release().to_string_lossy()); println!("{}", info.version().to_string_lossy()); println!("{}", info.machine().to_string_lossy()); + println!("{}", info.processor().to_string_lossy()); println!("{}", info.osname().to_string_lossy()); } ``` @@ -41,6 +42,7 @@ hostname 5.10.0-8-amd64 #1 SMP Debian 5.10.46-4 (2021-08-03) x86_64 +x86_64 GNU/Linux ``` diff --git a/examples/ex.rs b/examples/ex.rs index 4318aa0..f347011 100644 --- a/examples/ex.rs +++ b/examples/ex.rs @@ -14,5 +14,6 @@ fn main() { println!("{}", info.release().to_string_lossy()); println!("{}", info.version().to_string_lossy()); println!("{}", info.machine().to_string_lossy()); + println!("{}", info.processor().to_string_lossy()); println!("{}", info.osname().to_string_lossy()); } diff --git a/src/lib.rs b/src/lib.rs index 4b01286..4a89d87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,6 +103,14 @@ pub trait UNameAPI { /// The name of the current system's hardware. fn machine(&self) -> &OsStr; + /// The processor type (architecture) of the current system. + /// + /// Maps machine architecture strings to GNU coreutils-compatible processor types. + /// For example, "arm64" may map to "arm", "x86_64" to "x86_64", etc. + /// This provides more semantically meaningful processor information than the + /// raw machine string in some contexts. + fn processor(&self) -> &OsStr; + /// The name of the current OS. fn osname(&self) -> &OsStr; } diff --git a/src/lib_impl.rs b/src/lib_impl.rs index f147aee..c7d51b5 100644 --- a/src/lib_impl.rs +++ b/src/lib_impl.rs @@ -1,5 +1,7 @@ // "plumbing" setup and connections for `lib.rs` +// spell-checker:ignore (jargon) armv + #![warn(unused_results)] // enable warnings for unused results #[cfg(target_os = "windows")] @@ -23,6 +25,45 @@ type PathStr = Path; #[cfg(target_os = "windows")] type PathString = PathBuf; +//=== platform-specific functions + +// map_processor +/// *Returns* processor type mapped from machine architecture string. +/// +/// Provides GNU coreutils-compatible processor type mappings from machine architecture strings. +/// This function normalizes architecture names to match the output of GNU coreutils' `uname -p`. +/// +/// # Difference from `machine()` +/// +/// While [`UNameAPI::machine()`] returns the raw architecture string from the OS (e.g., "arm64" on macOS), +/// `processor()` provides a normalized, cross-platform compatible representation: +/// +/// | Platform | machine() | processor() | Reason | +/// |----------|-----------|-------------|--------| +/// | macOS ARM | "arm64" | "arm" | GNU coreutils compatibility | +/// | Linux ARM64 | "aarch64" | "aarch64" | Preserve Linux convention | +/// | Windows x86 | "i386"-"i686" | "i686" | Normalize to common name | +/// +/// # Architecture Mappings +/// +/// * macOS uses "arm64" for ARM-based Macs → maps to "arm" +/// * Linux uses "aarch64" for ARM64 → passes through as "aarch64" +/// * Various i386/i486/i586/i686 variants → normalized to "i686" +/// * ARMv6/v7/v8 variants → mapped to "arm" +/// * Unknown architectures pass through unchanged (better than returning "unknown") +/// +/// ref: +pub(crate) fn map_processor(machine: &str) -> String { + match machine { + "arm64" => "arm".to_string(), + "aarch64" => "aarch64".to_string(), + "x86_64" | "amd64" => "x86_64".to_string(), + "i386" | "i486" | "i586" | "i686" => "i686".to_string(), + "armv7l" | "armv6l" | "armv8l" => "arm".to_string(), + _ => machine.to_string(), + } +} + //=== platform-specific const // HOST_OS_NAME * ref: [`uname` info](https://en.wikipedia.org/wiki/Uname) @@ -73,3 +114,28 @@ mod target; mod target; pub use target::*; + +//=== Tests + +#[test] +fn test_map_processor_mappings() { + // ARM variants + assert_eq!(map_processor("arm64"), "arm"); + assert_eq!(map_processor("aarch64"), "aarch64"); + assert_eq!(map_processor("armv6l"), "arm"); + assert_eq!(map_processor("armv7l"), "arm"); + assert_eq!(map_processor("armv8l"), "arm"); + + // x86 variants + assert_eq!(map_processor("x86_64"), "x86_64"); + assert_eq!(map_processor("amd64"), "x86_64"); + assert_eq!(map_processor("i386"), "i686"); + assert_eq!(map_processor("i486"), "i686"); + assert_eq!(map_processor("i586"), "i686"); + assert_eq!(map_processor("i686"), "i686"); + + // Unknown/passthrough architectures + assert_eq!(map_processor("riscv64"), "riscv64"); + assert_eq!(map_processor("powerpc64"), "powerpc64"); + assert_eq!(map_processor("unknown"), "unknown"); +} diff --git a/src/platform/unix.rs b/src/platform/unix.rs index c2904f0..aa866f5 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -40,6 +40,7 @@ pub struct PlatformInfo { release: OsString, version: OsString, machine: OsString, + processor: OsString, osname: OsString, } @@ -47,13 +48,16 @@ impl PlatformInfoAPI for PlatformInfo { // * note: this function *should* never fail fn new() -> Result { let utsname = UTSName(utsname()?); + let machine = oss_from_cstr(&utsname.0.machine); + let processor = OsString::from(crate::lib_impl::map_processor(&machine.to_string_lossy())); Ok(Self { utsname, sysname: oss_from_cstr(&utsname.0.sysname), nodename: oss_from_cstr(&utsname.0.nodename), release: oss_from_cstr(&utsname.0.release), version: oss_from_cstr(&utsname.0.version), - machine: oss_from_cstr(&utsname.0.machine), + machine, + processor, osname: OsString::from(crate::lib_impl::HOST_OS_NAME), }) } @@ -80,6 +84,10 @@ impl UNameAPI for PlatformInfo { &self.machine } + fn processor(&self) -> &OsStr { + &self.processor + } + fn osname(&self) -> &OsStr { &self.osname } @@ -222,6 +230,28 @@ fn test_osname() { assert!(osname.starts_with(crate::lib_impl::HOST_OS_NAME)); } +#[test] +fn test_processor() { + let info = PlatformInfo::new().unwrap(); + let processor = info.processor().to_string_lossy(); + + // Processor should not be empty + assert!(!processor.is_empty()); + + // On common platforms, verify expected mappings + #[cfg(all(target_arch = "aarch64", target_os = "macos"))] + assert_eq!(processor, "arm", "macOS arm64 should map to 'arm'"); + + #[cfg(all(target_arch = "aarch64", target_os = "linux"))] + assert_eq!(processor, "aarch64", "Linux aarch64 should pass through"); + + #[cfg(target_arch = "x86_64")] + assert_eq!(processor, "x86_64", "x86_64 should pass through"); + + #[cfg(target_arch = "x86")] + assert_eq!(processor, "i686", "x86 variants should normalize to i686"); +} + #[test] fn structure_clone() { let info = PlatformInfo::new().unwrap(); diff --git a/src/platform/unknown.rs b/src/platform/unknown.rs index aa01ffe..47f7397 100644 --- a/src/platform/unknown.rs +++ b/src/platform/unknown.rs @@ -51,6 +51,10 @@ impl UNameAPI for PlatformInfo { &self.unknown } + fn processor(&self) -> &OsStr { + &self.unknown + } + fn osname(&self) -> &OsStr { &self.unknown } @@ -65,6 +69,7 @@ fn test_unknown() { assert_eq!(platform_info.release().to_string_lossy(), "unknown"); assert_eq!(platform_info.version().to_string_lossy(), "unknown"); assert_eq!(platform_info.machine().to_string_lossy(), "unknown"); + assert_eq!(platform_info.processor().to_string_lossy(), "unknown"); assert_eq!(platform_info.osname().to_string_lossy(), "unknown"); } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 47068e6..ded87b6 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -69,6 +69,7 @@ pub struct PlatformInfo { release: OsString, version: OsString, machine: OsString, + processor: OsString, osname: OsString, } @@ -84,6 +85,7 @@ impl PlatformInfoAPI for PlatformInfo { let release = version_info.release.clone(); let version = version_info.version.clone(); let machine = determine_machine(&system_info); + let processor = OsString::from(crate::lib_impl::map_processor(&machine.to_string_lossy())); let osname = determine_osname(&version_info); Ok(Self { @@ -96,6 +98,7 @@ impl PlatformInfoAPI for PlatformInfo { release, version, machine, + processor, osname, }) } @@ -122,6 +125,10 @@ impl UNameAPI for PlatformInfo { &self.machine } + fn processor(&self) -> &OsStr { + &self.processor + } + fn osname(&self) -> &OsStr { &self.osname } @@ -746,6 +753,30 @@ fn test_known_winos_names() { ); } +#[test] +fn test_processor() { + let info = PlatformInfo::new().unwrap(); + let processor = info.processor().to_string_lossy(); + + // Processor should not be empty + assert!(!processor.is_empty()); + + // On common Windows platforms, verify expected mappings + #[cfg(target_arch = "x86_64")] + assert_eq!(processor, "x86_64", "Windows x86_64 should pass through"); + + #[cfg(target_arch = "x86")] + assert_eq!(processor, "i686", "Windows x86 should normalize to i686"); + + #[cfg(target_arch = "aarch64")] + assert_eq!( + processor, "aarch64", + "Windows ARM64 should pass through as aarch64" + ); + + println!("Windows processor=[{}]'{}'", processor.len(), processor); +} + #[test] fn structure_clone() { let info = PlatformInfo::new().unwrap();