use std::{
fmt::{self, Display},
str::FromStr,
};
use thiserror::Error;
use crate::Interpreter;
#[derive(Error, Debug)]
pub enum Error {
#[error("Unknown Python implementation `{0}`")]
UnknownImplementation(String),
}
#[derive(Debug, Eq, PartialEq, Clone, Copy, Default, PartialOrd, Ord, Hash)]
pub enum ImplementationName {
Pyodide,
GraalPy,
PyPy,
#[default]
CPython,
}
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
pub enum LenientImplementationName {
Unknown(String),
Known(ImplementationName),
}
impl ImplementationName {
pub(crate) const fn long_name(self) -> &'static str {
match self {
Self::CPython => "cpython",
Self::PyPy => "pypy",
Self::GraalPy => "graalpy",
Self::Pyodide => "pyodide",
}
}
pub(crate) const fn short_name(self) -> Option<&'static str> {
match self {
Self::CPython => Some("cp"),
Self::PyPy => Some("pp"),
Self::GraalPy => Some("gp"),
Self::Pyodide => None,
}
}
pub(crate) fn iter_all() -> impl Iterator<Item = Self> {
[Self::CPython, Self::PyPy, Self::GraalPy, Self::Pyodide].into_iter()
}
pub(crate) fn pretty(self) -> &'static str {
match self {
Self::CPython => "CPython",
Self::PyPy => "PyPy",
Self::GraalPy => "GraalPy",
Self::Pyodide => "Pyodide",
}
}
pub(crate) fn executable_name(self) -> &'static str {
match self {
Self::CPython | Self::Pyodide => "python",
Self::PyPy | Self::GraalPy => self.long_name(),
}
}
fn executable_install_name(self) -> &'static str {
match self {
Self::Pyodide => "pyodide",
_ => self.executable_name(),
}
}
pub(crate) fn matches_interpreter(self, interpreter: &Interpreter) -> bool {
match self {
Self::Pyodide => interpreter.os().is_emscripten(),
_ => interpreter
.implementation_name()
.eq_ignore_ascii_case(self.long_name()),
}
}
}
impl LenientImplementationName {
pub fn pretty(&self) -> &str {
match self {
Self::Known(implementation) => implementation.pretty(),
Self::Unknown(name) => name,
}
}
pub(crate) fn executable_install_name(&self) -> &str {
match self {
Self::Known(implementation) => implementation.executable_install_name(),
Self::Unknown(name) => name,
}
}
}
impl<'a> From<&'a LenientImplementationName> for &'a str {
fn from(value: &'a LenientImplementationName) -> &'a str {
match value {
LenientImplementationName::Known(implementation) => implementation.long_name(),
LenientImplementationName::Unknown(name) => name,
}
}
}
impl FromStr for ImplementationName {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::iter_all()
.find(|implementation| {
s.eq_ignore_ascii_case(implementation.long_name())
|| implementation
.short_name()
.is_some_and(|name| s.eq_ignore_ascii_case(name))
})
.ok_or_else(|| Error::UnknownImplementation(s.to_string()))
}
}
impl Display for ImplementationName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.long_name())
}
}
impl From<&str> for LenientImplementationName {
fn from(s: &str) -> Self {
match ImplementationName::from_str(s) {
Ok(implementation) => Self::Known(implementation),
Err(_) => Self::Unknown(s.to_string()),
}
}
}
impl From<ImplementationName> for LenientImplementationName {
fn from(implementation: ImplementationName) -> Self {
Self::Known(implementation)
}
}
impl Display for LenientImplementationName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Known(implementation) => implementation.fmt(f),
Self::Unknown(name) => f.write_str(&name.to_ascii_lowercase()),
}
}
}