Add processor() method to UNameAPI trait (#96)

* 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
This commit is contained in:
Cả thế giới là Rust
2025-11-27 14:41:41 +01:00
committed by GitHub
parent 2af766085a
commit 2271b98c1b
7 changed files with 144 additions and 1 deletions
+2
View File
@@ -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
```
+1
View File
@@ -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());
}
+8
View File
@@ -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;
}
+66
View File
@@ -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: <https://github.com/uutils/coreutils/issues/8659>
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");
}
+31 -1
View File
@@ -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<Self, PlatformInfoError> {
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();
+5
View File
@@ -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");
}
+31
View File
@@ -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();