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    /// Return the full implementation name.
32    pub(crate) const fn long_name(self) -> &'static str {
33        match self {
34            Self::CPython => "cpython",
35            Self::PyPy => "pypy",
36            Self::GraalPy => "graalpy",
37            Self::Pyodide => "pyodide",
38        }
39    }
40
41    /// Return the abbreviated implementation name, if one exists.
42    pub(crate) const fn short_name(self) -> Option<&'static str> {
43        match self {
44            Self::CPython => Some("cp"),
45            Self::PyPy => Some("pp"),
46            Self::GraalPy => Some("gp"),
47            Self::Pyodide => None,
48        }
49    }
50
51    pub(crate) fn iter_all() -> impl Iterator<Item = Self> {
52        [Self::CPython, Self::PyPy, Self::GraalPy, Self::Pyodide].into_iter()
53    }
54
55    pub(crate) fn pretty(self) -> &'static str {
56        match self {
57            Self::CPython => "CPython",
58            Self::PyPy => "PyPy",
59            Self::GraalPy => "GraalPy",
60            Self::Pyodide => "Pyodide",
61        }
62    }
63
64    /// The executable name used in distributions of this implementation.
65    pub(crate) fn executable_name(self) -> &'static str {
66        match self {
67            Self::CPython | Self::Pyodide => "python",
68            Self::PyPy | Self::GraalPy => self.long_name(),
69        }
70    }
71
72    /// The name used when installing this implementation as an executable into the bin directory.
73    fn executable_install_name(self) -> &'static str {
74        match self {
75            Self::Pyodide => "pyodide",
76            _ => self.executable_name(),
77        }
78    }
79
80    pub(crate) fn matches_interpreter(self, interpreter: &Interpreter) -> bool {
81        match self {
82            Self::Pyodide => interpreter.os().is_emscripten(),
83            _ => interpreter
84                .implementation_name()
85                .eq_ignore_ascii_case(self.long_name()),
86        }
87    }
88}
89
90impl LenientImplementationName {
91    pub fn pretty(&self) -> &str {
92        match self {
93            Self::Known(implementation) => implementation.pretty(),
94            Self::Unknown(name) => name,
95        }
96    }
97
98    pub(crate) fn executable_install_name(&self) -> &str {
99        match self {
100            Self::Known(implementation) => implementation.executable_install_name(),
101            Self::Unknown(name) => name,
102        }
103    }
104}
105
106impl<'a> From<&'a LenientImplementationName> for &'a str {
107    fn from(value: &'a LenientImplementationName) -> &'a str {
108        match value {
109            LenientImplementationName::Known(implementation) => implementation.long_name(),
110            LenientImplementationName::Unknown(name) => name,
111        }
112    }
113}
114
115impl FromStr for ImplementationName {
116    type Err = Error;
117
118    /// Parse a Python implementation name from a string.
119    ///
120    /// Supports the full name and the platform compatibility tag style name.
121    fn from_str(s: &str) -> Result<Self, Self::Err> {
122        Self::iter_all()
123            .find(|implementation| {
124                s.eq_ignore_ascii_case(implementation.long_name())
125                    || implementation
126                        .short_name()
127                        .is_some_and(|name| s.eq_ignore_ascii_case(name))
128            })
129            .ok_or_else(|| Error::UnknownImplementation(s.to_string()))
130    }
131}
132
133impl Display for ImplementationName {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        f.write_str(self.long_name())
136    }
137}
138
139impl From<&str> for LenientImplementationName {
140    fn from(s: &str) -> Self {
141        match ImplementationName::from_str(s) {
142            Ok(implementation) => Self::Known(implementation),
143            Err(_) => Self::Unknown(s.to_string()),
144        }
145    }
146}
147
148impl From<ImplementationName> for LenientImplementationName {
149    fn from(implementation: ImplementationName) -> Self {
150        Self::Known(implementation)
151    }
152}
153
154impl Display for LenientImplementationName {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        match self {
157            Self::Known(implementation) => implementation.fmt(f),
158            Self::Unknown(name) => f.write_str(&name.to_ascii_lowercase()),
159        }
160    }
161}