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    pub fn executable_install_name(self) -> &'static str {
62        match self {
63            Self::Pyodide => "pyodide",
64            _ => self.executable_name(),
65        }
66    }
67
68    pub 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 fn executable_name(&self) -> &str {
87        match self {
88            Self::Known(implementation) => implementation.executable_name(),
89            Self::Unknown(name) => name,
90        }
91    }
92
93    pub fn executable_install_name(&self) -> &str {
94        match self {
95            Self::Known(implementation) => implementation.executable_install_name(),
96            Self::Unknown(name) => name,
97        }
98    }
99}
100
101impl From<&ImplementationName> for &'static str {
102    fn from(value: &ImplementationName) -> &'static str {
103        match value {
104            ImplementationName::CPython => "cpython",
105            ImplementationName::PyPy => "pypy",
106            ImplementationName::GraalPy => "graalpy",
107            ImplementationName::Pyodide => "pyodide",
108        }
109    }
110}
111
112impl From<ImplementationName> for &'static str {
113    fn from(value: ImplementationName) -> &'static str {
114        (&value).into()
115    }
116}
117
118impl<'a> From<&'a LenientImplementationName> for &'a str {
119    fn from(value: &'a LenientImplementationName) -> &'a str {
120        match value {
121            LenientImplementationName::Known(implementation) => implementation.into(),
122            LenientImplementationName::Unknown(name) => name,
123        }
124    }
125}
126
127impl FromStr for ImplementationName {
128    type Err = Error;
129
130    /// Parse a Python implementation name from a string.
131    ///
132    /// Supports the full name and the platform compatibility tag style name.
133    fn from_str(s: &str) -> Result<Self, Self::Err> {
134        match s.to_ascii_lowercase().as_str() {
135            "cpython" | "cp" => Ok(Self::CPython),
136            "pypy" | "pp" => Ok(Self::PyPy),
137            "graalpy" | "gp" => Ok(Self::GraalPy),
138            "pyodide" => Ok(Self::Pyodide),
139            _ => Err(Error::UnknownImplementation(s.to_string())),
140        }
141    }
142}
143
144impl Display for ImplementationName {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        f.write_str(self.into())
147    }
148}
149
150impl From<&str> for LenientImplementationName {
151    fn from(s: &str) -> Self {
152        match ImplementationName::from_str(s) {
153            Ok(implementation) => Self::Known(implementation),
154            Err(_) => Self::Unknown(s.to_string()),
155        }
156    }
157}
158
159impl From<ImplementationName> for LenientImplementationName {
160    fn from(implementation: ImplementationName) -> Self {
161        Self::Known(implementation)
162    }
163}
164
165impl Display for LenientImplementationName {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        match self {
168            Self::Known(implementation) => implementation.fmt(f),
169            Self::Unknown(name) => f.write_str(&name.to_ascii_lowercase()),
170        }
171    }
172}