use std::fmt::Display; use std::ops::Deref; use std::{fmt, str::FromStr}; use thiserror::Error; #[derive(Error, Debug)] pub enum Error { #[error("Unknown operating system: {0}")] UnknownOs(String), #[error("Unknown architecture: {0}")] UnknownArch(String), #[error("Unknown libc environment: {0}")] UnknownLibc(String), } #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct Arch(pub(crate) target_lexicon::Architecture); #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct Os(pub(crate) target_lexicon::OperatingSystem); #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Libc { Some(target_lexicon::Environment), None, } impl Libc { pub(crate) fn from_env() -> Self { match std::env::consts::OS { // TODO(zanieb): On Linux, we use the uv target host to determine the libc variant // but we should only use this as a fallback and should instead inspect the // machine's `/bin/sh` (or similar). "linux" => Self::Some(target_lexicon::Environment::Gnu), "windows" | "macos" => Self::None, // Use `None` on platforms without explicit support. _ => Self::None, } } } impl FromStr for Libc { type Err = Error; fn from_str(s: &str) -> Result { match s { "gnu" => Ok(Self::Some(target_lexicon::Environment::Gnu)), "musl" => Ok(Self::Some(target_lexicon::Environment::Musl)), "none" => Ok(Self::None), _ => Err(Error::UnknownLibc(s.to_string())), } } } impl Os { pub fn from_env() -> Self { Self(target_lexicon::HOST.operating_system) } } impl Arch { pub fn from_env() -> Self { Self(target_lexicon::HOST.architecture) } } impl Display for Libc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Some(env) => write!(f, "{env}"), Self::None => write!(f, "none"), } } } impl Display for Os { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &**self { target_lexicon::OperatingSystem::Darwin => write!(f, "macos"), inner => write!(f, "{inner}"), } } } impl Display for Arch { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &**self { target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686) => { write!(f, "x86") } inner => write!(f, "{inner}"), } } } impl FromStr for Os { type Err = Error; fn from_str(s: &str) -> Result { let inner = match s { "macos" => target_lexicon::OperatingSystem::Darwin, _ => target_lexicon::OperatingSystem::from_str(s) .map_err(|()| Error::UnknownOs(s.to_string()))?, }; if matches!(inner, target_lexicon::OperatingSystem::Unknown) { return Err(Error::UnknownOs(s.to_string())); } Ok(Self(inner)) } } impl FromStr for Arch { type Err = Error; fn from_str(s: &str) -> Result { let inner = match s { // Allow users to specify "x86" as a shorthand for the "i686" variant, they should not need // to specify the exact architecture and this variant is what we have downloads for. "x86" => target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686), _ => target_lexicon::Architecture::from_str(s) .map_err(|()| Error::UnknownArch(s.to_string()))?, }; if matches!(inner, target_lexicon::Architecture::Unknown) { return Err(Error::UnknownArch(s.to_string())); } Ok(Self(inner)) } } impl Deref for Arch { type Target = target_lexicon::Architecture; fn deref(&self) -> &Self::Target { &self.0 } } impl Deref for Os { type Target = target_lexicon::OperatingSystem; fn deref(&self) -> &Self::Target { &self.0 } } impl From<&platform_tags::Arch> for Arch { fn from(value: &platform_tags::Arch) -> Self { match value { platform_tags::Arch::Aarch64 => Self(target_lexicon::Architecture::Aarch64( target_lexicon::Aarch64Architecture::Aarch64, )), platform_tags::Arch::Armv6L => Self(target_lexicon::Architecture::Arm( target_lexicon::ArmArchitecture::Armv6, )), platform_tags::Arch::Armv7L => Self(target_lexicon::Architecture::Arm( target_lexicon::ArmArchitecture::Armv7, )), platform_tags::Arch::S390X => Self(target_lexicon::Architecture::S390x), platform_tags::Arch::Powerpc64 => Self(target_lexicon::Architecture::Powerpc64), platform_tags::Arch::Powerpc64Le => Self(target_lexicon::Architecture::Powerpc64le), platform_tags::Arch::X86 => Self(target_lexicon::Architecture::X86_32( target_lexicon::X86_32Architecture::I686, )), platform_tags::Arch::X86_64 => Self(target_lexicon::Architecture::X86_64), } } } impl From<&platform_tags::Os> for Libc { fn from(value: &platform_tags::Os) -> Self { match value { platform_tags::Os::Manylinux { .. } => Self::Some(target_lexicon::Environment::Gnu), platform_tags::Os::Musllinux { .. } => Self::Some(target_lexicon::Environment::Musl), _ => Self::None, } } } impl From<&platform_tags::Os> for Os { fn from(value: &platform_tags::Os) -> Self { match value { platform_tags::Os::Dragonfly { .. } => Self(target_lexicon::OperatingSystem::Dragonfly), platform_tags::Os::FreeBsd { .. } => Self(target_lexicon::OperatingSystem::Freebsd), platform_tags::Os::Haiku { .. } => Self(target_lexicon::OperatingSystem::Haiku), platform_tags::Os::Illumos { .. } => Self(target_lexicon::OperatingSystem::Illumos), platform_tags::Os::Macos { .. } => Self(target_lexicon::OperatingSystem::Darwin), platform_tags::Os::Manylinux { .. } | platform_tags::Os::Musllinux { .. } => { Self(target_lexicon::OperatingSystem::Linux) } platform_tags::Os::NetBsd { .. } => Self(target_lexicon::OperatingSystem::Netbsd), platform_tags::Os::OpenBsd { .. } => Self(target_lexicon::OperatingSystem::Openbsd), platform_tags::Os::Windows => Self(target_lexicon::OperatingSystem::Windows), } } }