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