Skip to content

Commit 2271b98

Browse files
authored
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
1 parent 2af7660 commit 2271b98

File tree

7 files changed

+144
-1
lines changed

7 files changed

+144
-1
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ fn main() {
2929
println!("{}", info.release().to_string_lossy());
3030
println!("{}", info.version().to_string_lossy());
3131
println!("{}", info.machine().to_string_lossy());
32+
println!("{}", info.processor().to_string_lossy());
3233
println!("{}", info.osname().to_string_lossy());
3334
}
3435
```
@@ -41,6 +42,7 @@ hostname
4142
5.10.0-8-amd64
4243
#1 SMP Debian 5.10.46-4 (2021-08-03)
4344
x86_64
45+
x86_64
4446
GNU/Linux
4547
```
4648

examples/ex.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ fn main() {
1414
println!("{}", info.release().to_string_lossy());
1515
println!("{}", info.version().to_string_lossy());
1616
println!("{}", info.machine().to_string_lossy());
17+
println!("{}", info.processor().to_string_lossy());
1718
println!("{}", info.osname().to_string_lossy());
1819
}

src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ pub trait UNameAPI {
103103
/// The name of the current system's hardware.
104104
fn machine(&self) -> &OsStr;
105105

106+
/// The processor type (architecture) of the current system.
107+
///
108+
/// Maps machine architecture strings to GNU coreutils-compatible processor types.
109+
/// For example, "arm64" may map to "arm", "x86_64" to "x86_64", etc.
110+
/// This provides more semantically meaningful processor information than the
111+
/// raw machine string in some contexts.
112+
fn processor(&self) -> &OsStr;
113+
106114
/// The name of the current OS.
107115
fn osname(&self) -> &OsStr;
108116
}

src/lib_impl.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// "plumbing" setup and connections for `lib.rs`
22

3+
// spell-checker:ignore (jargon) armv
4+
35
#![warn(unused_results)] // enable warnings for unused results
46

57
#[cfg(target_os = "windows")]
@@ -23,6 +25,45 @@ type PathStr = Path;
2325
#[cfg(target_os = "windows")]
2426
type PathString = PathBuf;
2527

28+
//=== platform-specific functions
29+
30+
// map_processor
31+
/// *Returns* processor type mapped from machine architecture string.
32+
///
33+
/// Provides GNU coreutils-compatible processor type mappings from machine architecture strings.
34+
/// This function normalizes architecture names to match the output of GNU coreutils' `uname -p`.
35+
///
36+
/// # Difference from `machine()`
37+
///
38+
/// While [`UNameAPI::machine()`] returns the raw architecture string from the OS (e.g., "arm64" on macOS),
39+
/// `processor()` provides a normalized, cross-platform compatible representation:
40+
///
41+
/// | Platform | machine() | processor() | Reason |
42+
/// |----------|-----------|-------------|--------|
43+
/// | macOS ARM | "arm64" | "arm" | GNU coreutils compatibility |
44+
/// | Linux ARM64 | "aarch64" | "aarch64" | Preserve Linux convention |
45+
/// | Windows x86 | "i386"-"i686" | "i686" | Normalize to common name |
46+
///
47+
/// # Architecture Mappings
48+
///
49+
/// * macOS uses "arm64" for ARM-based Macs → maps to "arm"
50+
/// * Linux uses "aarch64" for ARM64 → passes through as "aarch64"
51+
/// * Various i386/i486/i586/i686 variants → normalized to "i686"
52+
/// * ARMv6/v7/v8 variants → mapped to "arm"
53+
/// * Unknown architectures pass through unchanged (better than returning "unknown")
54+
///
55+
/// ref: <https://github.com/uutils/coreutils/issues/8659>
56+
pub(crate) fn map_processor(machine: &str) -> String {
57+
match machine {
58+
"arm64" => "arm".to_string(),
59+
"aarch64" => "aarch64".to_string(),
60+
"x86_64" | "amd64" => "x86_64".to_string(),
61+
"i386" | "i486" | "i586" | "i686" => "i686".to_string(),
62+
"armv7l" | "armv6l" | "armv8l" => "arm".to_string(),
63+
_ => machine.to_string(),
64+
}
65+
}
66+
2667
//=== platform-specific const
2768

2869
// HOST_OS_NAME * ref: [`uname` info](https://en.wikipedia.org/wiki/Uname)
@@ -73,3 +114,28 @@ mod target;
73114
mod target;
74115

75116
pub use target::*;
117+
118+
//=== Tests
119+
120+
#[test]
121+
fn test_map_processor_mappings() {
122+
// ARM variants
123+
assert_eq!(map_processor("arm64"), "arm");
124+
assert_eq!(map_processor("aarch64"), "aarch64");
125+
assert_eq!(map_processor("armv6l"), "arm");
126+
assert_eq!(map_processor("armv7l"), "arm");
127+
assert_eq!(map_processor("armv8l"), "arm");
128+
129+
// x86 variants
130+
assert_eq!(map_processor("x86_64"), "x86_64");
131+
assert_eq!(map_processor("amd64"), "x86_64");
132+
assert_eq!(map_processor("i386"), "i686");
133+
assert_eq!(map_processor("i486"), "i686");
134+
assert_eq!(map_processor("i586"), "i686");
135+
assert_eq!(map_processor("i686"), "i686");
136+
137+
// Unknown/passthrough architectures
138+
assert_eq!(map_processor("riscv64"), "riscv64");
139+
assert_eq!(map_processor("powerpc64"), "powerpc64");
140+
assert_eq!(map_processor("unknown"), "unknown");
141+
}

src/platform/unix.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,24 @@ pub struct PlatformInfo {
4040
release: OsString,
4141
version: OsString,
4242
machine: OsString,
43+
processor: OsString,
4344
osname: OsString,
4445
}
4546

4647
impl PlatformInfoAPI for PlatformInfo {
4748
// * note: this function *should* never fail
4849
fn new() -> Result<Self, PlatformInfoError> {
4950
let utsname = UTSName(utsname()?);
51+
let machine = oss_from_cstr(&utsname.0.machine);
52+
let processor = OsString::from(crate::lib_impl::map_processor(&machine.to_string_lossy()));
5053
Ok(Self {
5154
utsname,
5255
sysname: oss_from_cstr(&utsname.0.sysname),
5356
nodename: oss_from_cstr(&utsname.0.nodename),
5457
release: oss_from_cstr(&utsname.0.release),
5558
version: oss_from_cstr(&utsname.0.version),
56-
machine: oss_from_cstr(&utsname.0.machine),
59+
machine,
60+
processor,
5761
osname: OsString::from(crate::lib_impl::HOST_OS_NAME),
5862
})
5963
}
@@ -80,6 +84,10 @@ impl UNameAPI for PlatformInfo {
8084
&self.machine
8185
}
8286

87+
fn processor(&self) -> &OsStr {
88+
&self.processor
89+
}
90+
8391
fn osname(&self) -> &OsStr {
8492
&self.osname
8593
}
@@ -222,6 +230,28 @@ fn test_osname() {
222230
assert!(osname.starts_with(crate::lib_impl::HOST_OS_NAME));
223231
}
224232

233+
#[test]
234+
fn test_processor() {
235+
let info = PlatformInfo::new().unwrap();
236+
let processor = info.processor().to_string_lossy();
237+
238+
// Processor should not be empty
239+
assert!(!processor.is_empty());
240+
241+
// On common platforms, verify expected mappings
242+
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
243+
assert_eq!(processor, "arm", "macOS arm64 should map to 'arm'");
244+
245+
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
246+
assert_eq!(processor, "aarch64", "Linux aarch64 should pass through");
247+
248+
#[cfg(target_arch = "x86_64")]
249+
assert_eq!(processor, "x86_64", "x86_64 should pass through");
250+
251+
#[cfg(target_arch = "x86")]
252+
assert_eq!(processor, "i686", "x86 variants should normalize to i686");
253+
}
254+
225255
#[test]
226256
fn structure_clone() {
227257
let info = PlatformInfo::new().unwrap();

src/platform/unknown.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ impl UNameAPI for PlatformInfo {
5151
&self.unknown
5252
}
5353

54+
fn processor(&self) -> &OsStr {
55+
&self.unknown
56+
}
57+
5458
fn osname(&self) -> &OsStr {
5559
&self.unknown
5660
}
@@ -65,6 +69,7 @@ fn test_unknown() {
6569
assert_eq!(platform_info.release().to_string_lossy(), "unknown");
6670
assert_eq!(platform_info.version().to_string_lossy(), "unknown");
6771
assert_eq!(platform_info.machine().to_string_lossy(), "unknown");
72+
assert_eq!(platform_info.processor().to_string_lossy(), "unknown");
6873
assert_eq!(platform_info.osname().to_string_lossy(), "unknown");
6974
}
7075

src/platform/windows.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub struct PlatformInfo {
6969
release: OsString,
7070
version: OsString,
7171
machine: OsString,
72+
processor: OsString,
7273
osname: OsString,
7374
}
7475

@@ -84,6 +85,7 @@ impl PlatformInfoAPI for PlatformInfo {
8485
let release = version_info.release.clone();
8586
let version = version_info.version.clone();
8687
let machine = determine_machine(&system_info);
88+
let processor = OsString::from(crate::lib_impl::map_processor(&machine.to_string_lossy()));
8789
let osname = determine_osname(&version_info);
8890

8991
Ok(Self {
@@ -96,6 +98,7 @@ impl PlatformInfoAPI for PlatformInfo {
9698
release,
9799
version,
98100
machine,
101+
processor,
99102
osname,
100103
})
101104
}
@@ -122,6 +125,10 @@ impl UNameAPI for PlatformInfo {
122125
&self.machine
123126
}
124127

128+
fn processor(&self) -> &OsStr {
129+
&self.processor
130+
}
131+
125132
fn osname(&self) -> &OsStr {
126133
&self.osname
127134
}
@@ -746,6 +753,30 @@ fn test_known_winos_names() {
746753
);
747754
}
748755

756+
#[test]
757+
fn test_processor() {
758+
let info = PlatformInfo::new().unwrap();
759+
let processor = info.processor().to_string_lossy();
760+
761+
// Processor should not be empty
762+
assert!(!processor.is_empty());
763+
764+
// On common Windows platforms, verify expected mappings
765+
#[cfg(target_arch = "x86_64")]
766+
assert_eq!(processor, "x86_64", "Windows x86_64 should pass through");
767+
768+
#[cfg(target_arch = "x86")]
769+
assert_eq!(processor, "i686", "Windows x86 should normalize to i686");
770+
771+
#[cfg(target_arch = "aarch64")]
772+
assert_eq!(
773+
processor, "aarch64",
774+
"Windows ARM64 should pass through as aarch64"
775+
);
776+
777+
println!("Windows processor=[{}]'{}'", processor.len(), processor);
778+
}
779+
749780
#[test]
750781
fn structure_clone() {
751782
let info = PlatformInfo::new().unwrap();

0 commit comments

Comments
 (0)