Skip to main content

uv_python/
implementation.rs

1use std::{
2    fmt::{self, Display},
3    str::FromStr,
4};
5use thiserror::Error;
6
7use crate::Interpreter;
8
9#[derive(Error, Debug)]
10pub enum Error {
11    #[error("Unknown Python implementation `{0}`")]
12    UnknownImplementation(String),
13}
14
15#[derive(Debug, Eq, PartialEq, Clone, Copy, Default, PartialOrd, Ord, Hash)]
16pub enum ImplementationName {
17    Pyodide,
18    GraalPy,
19    PyPy,
20    #[default]
21    CPython,
22}
23
24#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
25pub enum LenientImplementationName {
26    Unknown(String),
27    Known(ImplementationName),
28}
29
30impl ImplementationName {
31    pub(crate) fn short_names() -> impl Iterator<Item = &'static str> {
32        ["cp", "pp", "gp"].into_iter()
33    }
34
35    pub(crate) fn long_names() -> impl Iterator<Item = &'static str> {
36        ["cpython", "pypy", "graalpy", "pyodide"].into_iter()
37    }
38
39    pub(crate) fn iter_all() -> impl Iterator<Item = Self> {
40        [Self::CPython, Self::PyPy, Self::GraalPy, Self::Pyodide].into_iter()
41    }
42
43    pub fn pretty(self) -> &'static str {
44        match self {
45            Self::CPython => "CPython",
46            Self::PyPy => "PyPy",
47            Self::GraalPy => "GraalPy",
48            Self::Pyodide => "Pyodide",
49        }
50    }
51
52    /// The executable name used in distributions of this implementation.
53    pub fn executable_name(self) -> &'static str {
54        match self {
55            Self::CPython | Self::Pyodide => "python",
56            Self::PyPy | Self::GraalPy => self.into(),
57        }
58    }
59
60    /// The name used when installing this implementation as an executable into the bin directory.
61    fn executable_install_name(self) -> &'static str {
62        match self {
63            Self::Pyodide => "pyodide",
64            _ => self.executable_name(),
65        }
66    }
67
68    pub(crate) fn matches_interpreter(self, interpreter: &Interpreter) -> bool {
69        match self {
70            Self::Pyodide => interpreter.os().is_emscripten(),
71            _ => interpreter
72                .implementation_name()
73                .eq_ignore_ascii_case(self.into()),
74        }
75    }
76}
77
78impl LenientImplementationName {
79    pub fn pretty(&self) -> &str {
80        match self {
81            Self::Known(implementation) => implementation.pretty(),
82            Self::Unknown(name) => name,
83        }
84    }
85
86    pub(crate) fn executable_install_name(&self) -> &str {
87        match self {
88            Self::Known(implementation) => implementation.executable_install_name(),
89            Self::Unknown(name) => name,
90        }
91    }
92}
93
94impl From<&ImplementationName> for &'static str {
95    fn from(value: &ImplementationName) -> &'static str {
96        match value {
97            ImplementationName::CPython => "cpython",
98            ImplementationName::PyPy => "pypy",
99            ImplementationName::GraalPy => "graalpy",
100            ImplementationName::Pyodide => "pyodide",
101        }
102    }
103}
104
105impl From<ImplementationName> for &'static str {
106    fn from(value: ImplementationName) -> &'static str {
107        (&value).into()
108    }
109}
110
111impl<'a> From<&'a LenientImplementationName> for &'a str {
112    fn from(value: &'a LenientImplementationName) -> &'a str {
113        match value {
114            LenientImplementationName::Known(implementation) => implementation.into(),
115            LenientImplementationName::Unknown(name) => name,
116        }
117    }
118}
119
120impl FromStr for ImplementationName {
121    type Err = Error;
122
123    /// Parse a Python implementation name from a string.
124    ///
125    /// Supports the full name and the platform compatibility tag style name.
126    fn from_str(s: &str) -> Result<Self, Self::Err> {
127        match s.to_ascii_lowercase().as_str() {
128            "cpython" | "cp" => Ok(Self::CPython),
129            "pypy" | "pp" => Ok(Self::PyPy),
130            "graalpy" | "gp" => Ok(Self::GraalPy),
131            "pyodide" => Ok(Self::Pyodide),
132            _ => Err(Error::UnknownImplementation(s.to_string())),
133        }
134    }
135}
136
137impl Display for ImplementationName {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        f.write_str(self.into())
140    }
141}
142
143impl From<&str> for LenientImplementationName {
144    fn from(s: &str) -> Self {
145        match ImplementationName::from_str(s) {
146            Ok(implementation) => Self::Known(implementation),
147            Err(_) => Self::Unknown(s.to_string()),
148        }
149    }
150}
151
152impl From<ImplementationName> for LenientImplementationName {
153    fn from(implementation: ImplementationName) -> Self {
154        Self::Known(implementation)
155    }
156}
157
158impl Display for LenientImplementationName {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        match self {
161            Self::Known(implementation) => implementation.fmt(f),
162            Self::Unknown(name) => f.write_str(&name.to_ascii_lowercase()),
163        }
164    }
165}