Skip to main content

target_tuple_pieces/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![allow(
3    clippy::upper_case_acronyms,
4    clippy::manual_non_exhaustive,
5    clippy::match_like_matches_macro
6)] // Kill clippy for MSRV
7
8use core::fmt::Formatter;
9use core::{fmt::Display, str::FromStr};
10
11///
12/// The result of FromStr::from_str, when parsing a field (other than vendor),
13///  with a value that is not known to the library
14#[derive(Debug, Clone, Copy)]
15pub struct UnknownError;
16
17impl Display for UnknownError {
18    fn fmt(&self, fmt: &mut Formatter) -> core::fmt::Result {
19        fmt.write_str("Unknown or invalid target or component")
20    }
21}
22
23///
24/// The Architecture field of a target tuple
25#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
26#[non_exhaustive]
27pub enum Architecture {
28    Unknown,
29    X86_16(u8),
30    X86_32(u8),
31    X86_64 { microarch: u8 },
32    Arm,
33    ArmBe,
34    Aarch64,
35    Aarch64Be,
36    Aarch64_32,
37    Mips,
38    MipsLE,
39    Mips64,
40    Mips64LE,
41    PowerPC32,
42    PowerPC64,
43    PowerPC64le,
44    RiscV32,
45    RiscV64,
46    Sparc,
47    SparcV9,
48    SparcEL,
49    Wasm32,
50    Wasm64,
51    Wc65c816,
52    M6502,
53    M65C02,
54    SPC700,
55    Clever,
56    HoleyBytes,
57    Skyarch,
58}
59
60impl FromStr for Architecture {
61    type Err = UnknownError;
62
63    fn from_str(s: &str) -> Result<Self, Self::Err> {
64        Ok(match s {
65            "i86" | "i8086" | "i086" => Self::X86_16(0),
66            "i186" => Self::X86_16(1),
67            "i286" => Self::X86_16(2),
68            "i386" => Self::X86_32(3),
69            "i486" => Self::X86_32(4),
70            "i586" => Self::X86_32(5),
71            "i686" => Self::X86_32(6),
72            "i786" => Self::X86_32(7),
73            "amd64" | "x86_64" | "x86_64h" | "x64" => Self::X86_64 { microarch: 1 },
74            "x86_64v2" => Self::X86_64 { microarch: 2 },
75            "x86_64v3" => Self::X86_64 { microarch: 3 },
76            "x86_64v4" => Self::X86_64 { microarch: 4 },
77            "armeb" => Self::ArmBe,
78            "arm" => Self::Arm,
79            "aarch64" | "arm64" | "arm64e" => Self::Aarch64,
80            "aarch64_be" | "arm64_be" => Self::Aarch64Be,
81            "aarch64_32" | "arm64_32" => Self::Aarch64_32,
82            s if s.starts_with("clever") => Self::Clever,
83            "powerpc" | "powerpcspe" | "ppc" | "ppc32" => Self::PowerPC32,
84            "powerpc64" | "ppu" | "ppc64" => Self::PowerPC64,
85            "powerpc64le" | "ppc64le" => Self::PowerPC64le,
86            "mips" | "mipseb" | "mipsallegrex" | "mipsisa32r6" | "mipsr6" => Self::Mips,
87            "mipsel" | "mipsallegrexel" | "mipsisa32r6el" | "mipsr6el" => Self::MipsLE,
88            "mips64" | "mips64eb" | "mipsn32" | "mipsisa64r6" | "mips64r6" | "mipsn32r6" => {
89                Self::Mips64
90            }
91            "mips64el" | "mipsn32el" | "mipsisa64r6el" | "mips64r6el" | "mipsn32r6el" => {
92                Self::Mips64LE
93            }
94            "sparc" => Self::Sparc,
95            "sparcel" => Self::SparcEL,
96            "sparcv9" | "sparc64" => Self::SparcV9,
97            "riscv32" => Self::RiscV32,
98            "riscv64" => Self::RiscV64,
99            "wc65c816" | "65816" | "w65c816" | "65c816" | "w65" => Self::Wc65c816,
100            "6502" | "6502x" | "6502X" => Self::M6502,
101            "65c02" | "65C02" => Self::M65C02,
102            "wasm32" => Self::Wasm32,
103            "wasm64" => Self::Wasm64,
104
105            "spc700" | "spc" => Self::SPC700,
106            "holeybytes" | "hbvm" | "hb" => Self::HoleyBytes,
107            "skyarch" => Self::Skyarch,
108
109            _ => return Err(UnknownError),
110        })
111    }
112}
113
114impl Display for Architecture {
115    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
116        self.canonical_name().fmt(f)
117    }
118}
119
120impl Architecture {
121    /// Parses the Architecture in a "lossy" manner
122    /// This is equivalent to [`Self::from_str`], but returns [`Architecture::Unknown`], instead of an error,
123    ///  on an unknown architecture.
124    /// This is useful (in conjunction with an actual target name)
125    pub fn parse(st: &str) -> Self {
126        Self::from_str(st).unwrap_or(Architecture::Unknown)
127    }
128
129    ///
130    /// Returns the canonical name of the target
131    /// The canonical name, when passed into `[`Self::parse`] will yield an equivalent value,
132    /// Formatting an Architecture yields this string
133    pub fn canonical_name(&self) -> &'static str {
134        match self {
135            Architecture::Unknown => "unknown",
136            Architecture::X86_16(0) => "i86",
137            Architecture::X86_16(1) => "i186",
138            Architecture::X86_16(2..) => "i286",
139            Architecture::X86_32(..=3) => "i386",
140            Architecture::X86_32(4) => "i486",
141            Architecture::X86_32(5) => "i586",
142            Architecture::X86_32(6) => "i686",
143            Architecture::X86_32(7..) => "i786",
144            Architecture::X86_64 {
145                microarch: 0 | 1 | 5..,
146            } => "x86_64",
147            Architecture::X86_64 { microarch: 2 } => "x86_64v2",
148            Architecture::X86_64 { microarch: 3 } => "x86_64v3",
149            Architecture::X86_64 { microarch: 4 } => "x86_64v4",
150            Architecture::Arm => "arm",
151            Architecture::ArmBe => "armeb",
152            Architecture::Aarch64 => "aarch64",
153            Architecture::Aarch64Be => "aarch64_be",
154            Architecture::Aarch64_32 => "aarch64_32",
155            Architecture::Mips => "mips",
156            Architecture::Mips64 => "mips64",
157            Architecture::PowerPC32 => "powerpc",
158            Architecture::PowerPC64 => "powerpc64",
159            Architecture::PowerPC64le => "powerpc64le",
160            Architecture::RiscV32 => "riscv32",
161            Architecture::RiscV64 => "riscv64",
162            Architecture::Sparc => "sparc",
163            Architecture::SparcV9 => "sparcv9",
164            Architecture::SparcEL => "sparcel",
165            Architecture::Wasm32 => "wasm32",
166            Architecture::Wasm64 => "wasm64",
167            Architecture::Wc65c816 => "w65",
168            Architecture::MipsLE => "mipsel",
169            Architecture::Mips64LE => "mips64el",
170            Architecture::M6502 => "6502",
171            Architecture::M65C02 => "6502",
172            Architecture::SPC700 => "spc700",
173            Architecture::Clever => "clever",
174            Architecture::HoleyBytes => "holeybytes",
175            Architecture::Skyarch => "skyarch",
176        }
177    }
178}
179
180///
181/// The Vendor field of a target tuple
182///
183#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
184#[non_exhaustive]
185pub enum Vendor {
186    Unknown = 0,
187    Apple = 1,
188    PC = 2,
189    SCEI = 3,
190    Freescale = 4,
191    IBM = 5,
192    ImaginationTechnologies = 6,
193    MipsTechnologies = 7,
194    NVIDIA = 8,
195    CSR = 9,
196    Myriad = 10,
197    AMD = 11,
198    Mesa = 12,
199    SUSE = 13,
200    OpenEmbedded = 14,
201    WDC = 15,
202}
203
204impl FromStr for Vendor {
205    type Err = core::convert::Infallible;
206
207    fn from_str(s: &str) -> Result<Self, Self::Err> {
208        Ok(match s {
209            "apple" => Self::Apple,
210            "pc" => Self::PC,
211            // "nes" => Self::NES,
212            // "snes" | "snesdev" => Self::SNES,
213            "scei" => Self::SCEI,
214            "fsl" => Self::Freescale,
215            "img" => Self::ImaginationTechnologies,
216            "ibm" => Self::IBM,
217            "mti" => Self::MipsTechnologies,
218            "nvidia" => Self::NVIDIA,
219            "csr" => Self::CSR,
220            "myriad" => Self::Myriad,
221            "amd" => Self::AMD,
222            "mesa" => Self::Mesa,
223            "suse" => Self::SUSE,
224            "oe" => Self::OpenEmbedded,
225            "wdc" => Self::WDC,
226            _ => Self::Unknown,
227        })
228    }
229}
230
231impl Display for Vendor {
232    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
233        self.canonical_name().fmt(f)
234    }
235}
236
237impl Vendor {
238    /// Parses the Vendor in a "lossy" manner
239    /// This is equivalent to [`Self::from_str`].
240    /// Note that an unknown vendor is not considered an error.
241    pub fn parse(s: &str) -> Self {
242        Self::from_str(s).unwrap()
243    }
244
245    ///
246    /// Returns the canonical name of the vendor
247    /// The canonical name, when passed into `[`Self::parse`] will yield an equivalent value,
248    /// Formatting a Vendor yields this string
249    pub fn canonical_name(&self) -> &'static str {
250        match self {
251            Vendor::Apple => "apple",
252            Vendor::PC => "pc",
253            Vendor::Unknown => "unknown",
254            Vendor::SCEI => "scei",
255            Vendor::Freescale => "fsl",
256            Vendor::IBM => "ibm",
257            Vendor::ImaginationTechnologies => "img",
258            Vendor::MipsTechnologies => "mti",
259            Vendor::NVIDIA => "nvidia",
260            Vendor::CSR => "csr",
261            Vendor::Myriad => "myriad",
262            Vendor::AMD => "amd",
263            Vendor::Mesa => "mesa",
264            Vendor::SUSE => "suse",
265            Vendor::OpenEmbedded => "oe",
266            Vendor::WDC => "wdc",
267        }
268    }
269}
270
271///
272/// The Operating System Field of a target tuple
273#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
274#[non_exhaustive]
275pub enum OS {
276    Unknown = 0,
277
278    Ananas = 1,
279    CloudABI = 2,
280    Darwin = 3,
281    DragonFly = 4,
282    FreeBSD = 5,
283    Fuchsia = 6,
284    IOS = 7,
285    KFreeBSD = 8,
286    Linux = 9,
287    Lv2 = 10,
288    MacOSX = 11,
289    NetBSD = 12,
290    OpenBSD = 13,
291    Solaris = 14,
292    Win32 = 15,
293    ZOS = 16,
294    Haiku = 17,
295    Minix = 18,
296    RTEMS = 19,
297    NaCl = 20,
298    AIX = 21,
299    CUDA = 22,
300    NVCL = 23,
301    AMDHSA = 24,
302    PS4 = 25,
303    ELFIAMCU = 26,
304    TvOS = 27,
305    WatchOS = 28,
306    Mesa3D = 29,
307    Contiki = 30,
308    AMDPAL = 31,
309    HermitCore = 32,
310    Hurd = 33,
311    WASI = 34,
312    Emscripten = 35,
313    SNES = 37, // Not an OS, but the currently config.sub places it in the os field
314    NES = 38,  // likewise
315    None = 39, // No OS
316    CleverOS = 40,
317    AbleOS = 41,
318    Lilium = 42,
319}
320
321impl FromStr for OS {
322    type Err = UnknownError;
323
324    fn from_str(s: &str) -> Result<Self, Self::Err> {
325        Ok(match s {
326            x if x.starts_with("ananas") => Self::Ananas,
327            x if x.starts_with("cloudabi") => Self::CloudABI,
328            x if x.starts_with("darwin") => Self::Darwin,
329            x if x.starts_with("dragonfly") => Self::DragonFly,
330            x if x.starts_with("freebsd") => Self::FreeBSD,
331            x if x.starts_with("fuchsia") => Self::Fuchsia,
332            x if x.starts_with("ios") => Self::IOS,
333            x if x.starts_with("kfreebsd") => Self::KFreeBSD,
334            x if x.starts_with("linux") => Self::Linux,
335            x if x.starts_with("lv2") => Self::Lv2,
336            x if x.starts_with("macos") => Self::MacOSX,
337            x if x.starts_with("netbsd") => Self::NetBSD,
338            x if x.starts_with("openbsd") => Self::OpenBSD,
339            x if x.starts_with("solaris") => Self::Solaris,
340            x if x.starts_with("win32") | x.starts_with("windows") => Self::Win32,
341            x if x.starts_with("zos") => Self::ZOS,
342            x if x.starts_with("haiku") => Self::Haiku,
343            x if x.starts_with("minix") => Self::Minix,
344            x if x.starts_with("rtems") => Self::RTEMS,
345            x if x.starts_with("nacl") => Self::NaCl,
346            x if x.starts_with("aix") => Self::AIX,
347            x if x.starts_with("cuda") => Self::CUDA,
348            x if x.starts_with("nvcl") => Self::NVCL,
349            x if x.starts_with("amdhsa") => Self::AMDHSA,
350            x if x.starts_with("ps4") => Self::PS4,
351            x if x.starts_with("elfiamcu") => Self::ELFIAMCU,
352            x if x.starts_with("tvos") => Self::TvOS,
353            x if x.starts_with("watchos") => Self::WatchOS,
354            x if x.starts_with("mesa3d") => Self::Mesa3D,
355            x if x.starts_with("contiki") => Self::Contiki,
356            x if x.starts_with("amdpal") => Self::AMDPAL,
357            x if x.starts_with("hermit") => Self::HermitCore,
358            x if x.starts_with("hurd") => Self::Hurd,
359            x if x.starts_with("wasi") => Self::WASI,
360            x if x.starts_with("emscripten") => Self::Emscripten,
361            x if x.starts_with("snes") => Self::SNES,
362            x if x.starts_with("nes") => Self::NES,
363            x if x.starts_with("cleveros") => Self::CleverOS,
364            x if x.starts_with("ableos") => Self::AbleOS,
365            x if x.starts_with("lilium") => Self::Lilium,
366            "none" => Self::None,
367
368            _ => return Err(UnknownError),
369        })
370    }
371}
372
373impl Display for OS {
374    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
375        self.canonical_name().fmt(f)
376    }
377}
378
379impl OS {
380    /// Parses the OS in a "lossy" manner
381    /// This is equivalent to [`Self::from_str`], except that [`OS::Unknown`] is returned, instead of an error, on an unknown OS Field
382    pub fn parse(s: &str) -> Self {
383        Self::from_str(s).unwrap_or(Self::Unknown)
384    }
385
386    ///
387    /// Returns the canonical name of the operating system
388    /// The canonical name, when passed into `[`Self::parse`] will yield an equivalent value,
389    /// Formatting an OS yields this string
390    pub fn canonical_name(&self) -> &'static str {
391        match self {
392            OS::Unknown => "unknown",
393            OS::Ananas => "ananas",
394            OS::CloudABI => "cloudabi",
395            OS::Darwin => "darwin",
396            OS::DragonFly => "dragonfly",
397            OS::FreeBSD => "freebsd",
398            OS::Fuchsia => "fuchsia",
399            OS::IOS => "ios",
400            OS::KFreeBSD => "kfreebsd",
401            OS::Linux => "linux",
402            OS::Lv2 => "lv2",
403            OS::MacOSX => "macos",
404            OS::NetBSD => "netbsd",
405            OS::OpenBSD => "openbsd",
406            OS::Solaris => "solaris",
407            OS::Win32 => "windows",
408            OS::ZOS => "zos",
409            OS::Haiku => "haiku",
410            OS::Minix => "minix",
411            OS::RTEMS => "rtems",
412            OS::NaCl => "nacl",
413            OS::AIX => "aix",
414            OS::CUDA => "cuda",
415            OS::NVCL => "nvcl",
416            OS::AMDHSA => "amdhsa",
417            OS::PS4 => "ps4",
418            OS::ELFIAMCU => "elfiamcu",
419            OS::TvOS => "tvos",
420            OS::WatchOS => "watchos",
421            OS::Mesa3D => "mesa3d",
422            OS::Contiki => "contiki",
423            OS::AMDPAL => "amdpal",
424            OS::HermitCore => "hermit",
425            OS::Hurd => "hurd",
426            OS::WASI => "wasi",
427            OS::Emscripten => "emscripten",
428            OS::SNES => "snes",
429            OS::NES => "nes",
430            OS::None => "none",
431            OS::CleverOS => "cleveros",
432            OS::AbleOS => "ableos",
433            OS::Lilium => "lilium",
434        }
435    }
436}
437
438///
439/// The Environment field of target tuples
440#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
441pub enum Environment {
442    Unknown = 0,
443    GNU = 1,
444    GNUABIN32 = 2,
445    GNUABI64 = 3,
446    GNUEABI = 4,
447    GNUEABIHF = 5,
448    GNUX32 = 6,
449    CODE16 = 7,
450    EABI = 8,
451    EABIHF = 9,
452    Android = 10,
453    Musl = 11,
454    MuslEABI = 12,
455    MuslEABIHF = 13,
456
457    MSVC = 15,
458    Itanium = 16,
459    Cygnus = 17,
460    CoreCLR = 18,
461    Simulator = 19,
462    MacABI = 20,
463
464    Standard = 23,
465    Kernel = 24,
466}
467
468impl FromStr for Environment {
469    type Err = UnknownError;
470
471    fn from_str(s: &str) -> Result<Self, Self::Err> {
472        Ok(match s {
473            x if x.starts_with("eabihf") => Self::EABIHF,
474            x if x.starts_with("eabi") => Self::EABI,
475            x if x.starts_with("gnuabin32") => Self::GNUABIN32,
476            x if x.starts_with("gnuabi64") => Self::GNUABI64,
477            x if x.starts_with("gnueabihf") => Self::GNUEABIHF,
478            x if x.starts_with("gnueabi") => Self::GNUEABI,
479            x if x.starts_with("gnux32") => Self::GNUX32,
480            x if x.starts_with("gnu") => Self::GNU,
481            x if x.starts_with("code16") => Self::CODE16,
482            x if x.starts_with("android") => Self::Android,
483            x if x.starts_with("musleabihf") => Self::MuslEABIHF,
484            x if x.starts_with("musleabi") => Self::MuslEABI,
485            x if x.starts_with("musl") => Self::Musl,
486            x if x.starts_with("msvc") => Self::MSVC,
487            x if x.starts_with("itanium") => Self::Itanium,
488            x if x.starts_with("cygnus") => Self::Cygnus,
489            x if x.starts_with("coreclr") => Self::CoreCLR,
490            x if x.starts_with("simulator") => Self::Simulator,
491            x if x.starts_with("macabi") => Self::MacABI,
492            x if x.starts_with("std") => Self::Standard,
493            x if x.starts_with("kernel") => Self::Kernel,
494            _ => return Err(UnknownError),
495        })
496    }
497}
498
499impl Display for Environment {
500    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
501        self.canonical_name().fmt(f)
502    }
503}
504
505impl Environment {
506    /// Parses the Environment name in a "lossy" manner
507    /// This is equivalent to [`Self::from_str`], except that [`Environment::Unknown`] is returned, instead of an error, on an unknown OS Field
508    pub fn parse(s: &str) -> Self {
509        Self::from_str(s).unwrap_or(Self::Unknown)
510    }
511
512    ///
513    /// Returns the canonical name of the environment
514    /// The canonical name, when passed into [`Self::parse`] will yield an equivalent value,
515    /// Formatting an Environment yields this string
516    pub fn canonical_name(&self) -> &'static str {
517        match self {
518            Environment::Unknown => "unknown",
519            Environment::GNU => "gnu",
520            Environment::GNUABIN32 => "gnuabin32",
521            Environment::GNUABI64 => "gnuabi64",
522            Environment::GNUEABI => "gnueabi",
523            Environment::GNUEABIHF => "gnueabihf",
524            Environment::GNUX32 => "gnux32",
525            Environment::CODE16 => "code16",
526            Environment::EABI => "eabi",
527            Environment::EABIHF => "eabihf",
528            Environment::Android => "android",
529            Environment::Musl => "musl",
530            Environment::MuslEABI => "musleabi",
531            Environment::MuslEABIHF => "musleabihf",
532            Environment::MSVC => "msvc",
533            Environment::Itanium => "itanium",
534            Environment::Cygnus => "cygnus",
535            Environment::CoreCLR => "coreclr",
536            Environment::Simulator => "simulator",
537            Environment::MacABI => "macabi",
538            Environment::Standard => "std",
539            Environment::Kernel => "kernel",
540        }
541    }
542}
543
544///
545/// The object format used by a target
546#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
547#[non_exhaustive]
548pub enum ObjectFormat {
549    Unknown = 0,
550    XCoff = 1,
551    Coff = 2,
552    Elf = 3,
553    Goff = 4,
554    MachO = 5,
555    Wasm = 6,
556
557    Xo65 = 7,
558    O65 = 8,
559    WlaObj = 9,
560}
561
562impl FromStr for ObjectFormat {
563    type Err = UnknownError;
564
565    fn from_str(s: &str) -> Result<Self, Self::Err> {
566        Ok(match s {
567            x if x.ends_with("xcoff") => Self::XCoff,
568            x if x.ends_with("coff") => Self::Coff,
569            x if x.ends_with("elf") => Self::Elf,
570            x if x.ends_with("goff") => Self::Goff,
571            x if x.ends_with("macho") => Self::MachO,
572            x if x.ends_with("wasm") => Self::Wasm,
573            x if x.ends_with("xo65") => Self::Xo65,
574            x if x.ends_with("o65") => Self::O65,
575            x if x.ends_with("wlaobj") => Self::WlaObj,
576            x if x.ends_with("wla") => Self::WlaObj,
577            _ => return Err(UnknownError),
578        })
579    }
580}
581
582impl Display for ObjectFormat {
583    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
584        self.canonical_name().fmt(f)
585    }
586}
587
588impl ObjectFormat {
589    /// Parses the ObjectFormat name in a "lossy" manner, from the end of the Environment field
590    /// This is equivalent to [`Self::from_str`], except that [`ObjectFormat::Unknown`] is returned, instead of an error, on an unknown OS Field
591    pub fn parse(s: &str) -> Self {
592        Self::from_str(s).unwrap_or(Self::Unknown)
593    }
594
595    ///
596    /// Returns the canonical name of the object format
597    /// The canonical name, when passed into [`Self::parse`] will yield an equivalent value,
598    /// Formatting an ObjectFormat yields this string
599    pub fn canonical_name(&self) -> &'static str {
600        match self {
601            ObjectFormat::Unknown => "unknown",
602            ObjectFormat::XCoff => "xcoff",
603            ObjectFormat::Coff => "coff",
604            ObjectFormat::Elf => "elf",
605            ObjectFormat::Goff => "goff",
606            ObjectFormat::MachO => "macho",
607            ObjectFormat::Wasm => "wasm",
608            ObjectFormat::Xo65 => "xo65",
609            ObjectFormat::O65 => "o65",
610            ObjectFormat::WlaObj => "wlaobj",
611        }
612    }
613}
614
615#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
616pub struct System {
617    os: Option<OS>,
618    env: Option<Environment>,
619    objfmt: Option<ObjectFormat>,
620}
621
622impl System {
623    /// Converts the specified pieces into an object file. At least one must be [`Some`]
624    ///
625    /// ## Panics
626    /// Panics if all of the pieces are [`None`]
627    pub const fn from_pieces(
628        os: Option<OS>,
629        env: Option<Environment>,
630        objfmt: Option<ObjectFormat>,
631    ) -> Self {
632        assert!(os.is_some() || env.is_some() || objfmt.is_some());
633
634        Self { os, env, objfmt }
635    }
636
637    pub const fn from_os(os: OS) -> Self {
638        Self {
639            os: Some(os),
640            env: None,
641            objfmt: None,
642        }
643    }
644
645    pub const fn from_os_env(os: OS, env: Environment) -> Self {
646        Self {
647            os: Some(os),
648            env: Some(env),
649            objfmt: None,
650        }
651    }
652
653    pub const fn from_env(env: Environment) -> Self {
654        Self {
655            os: None,
656            env: Some(env),
657            objfmt: None,
658        }
659    }
660
661    pub const fn from_objfmt(objfmt: ObjectFormat) -> Self {
662        Self {
663            os: None,
664            env: None,
665            objfmt: Some(objfmt),
666        }
667    }
668
669    pub const fn os(&self) -> Option<OS> {
670        self.os
671    }
672
673    pub const fn env(&self) -> Option<Environment> {
674        self.env
675    }
676
677    pub const fn object_format(&self) -> Option<ObjectFormat> {
678        self.objfmt
679    }
680}
681
682impl core::fmt::Display for System {
683    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
684        let mut sep = "";
685        if let Some(os) = self.os {
686            os.fmt(f)?;
687            sep = "-";
688        }
689
690        if let Some(env) = self.env {
691            f.write_str(sep)?;
692            sep = "";
693            env.fmt(f)?;
694        }
695
696        if let Some(objfmt) = self.objfmt {
697            f.write_str(sep)?;
698            objfmt.fmt(f)?;
699        }
700
701        Ok(())
702    }
703}
704
705impl FromStr for System {
706    type Err = UnknownError;
707
708    fn from_str(sys: &str) -> Result<Self, Self::Err> {
709        if let Some((os, senv)) = sys.split_once('-') {
710            let os = os.parse::<OS>()?;
711
712            let env = senv.parse::<Environment>();
713            let objfmt = senv.parse::<ObjectFormat>();
714
715            env.map(|_| ()).or_else(|_| objfmt.map(|_| ()))?;
716
717            Ok(Self {
718                os: Some(os),
719                env: env.ok(),
720                objfmt: objfmt.ok(),
721            })
722        } else if let Ok(os) = sys.parse::<OS>() {
723            Ok(Self {
724                os: Some(os),
725                env: None,
726                objfmt: None,
727            })
728        } else {
729            let env = sys.parse::<Environment>();
730            let objfmt = sys.parse::<ObjectFormat>();
731
732            env.map(|_| ()).or_else(|_| objfmt.map(|_| ()))?;
733
734            Ok(Self {
735                os: None,
736                env: env.ok(),
737                objfmt: objfmt.ok(),
738            })
739        }
740    }
741}