uv_python/
implementation.rs1use 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, serde::Serialize)]
16#[serde(rename_all = "lowercase")]
17pub enum ImplementationName {
18 Pyodide,
19 GraalPy,
20 PyPy,
21 #[default]
22 CPython,
23}
24
25#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash, serde::Serialize)]
26#[serde(untagged)]
27pub enum LenientImplementationName {
28 Unknown(String),
29 Known(ImplementationName),
30}
31
32impl ImplementationName {
33 pub const fn long_name(self) -> &'static str {
35 match self {
36 Self::CPython => "cpython",
37 Self::PyPy => "pypy",
38 Self::GraalPy => "graalpy",
39 Self::Pyodide => "pyodide",
40 }
41 }
42
43 pub const fn short_name(self) -> Option<&'static str> {
45 match self {
46 Self::CPython => Some("cp"),
47 Self::PyPy => Some("pp"),
48 Self::GraalPy => Some("gp"),
49 Self::Pyodide => None,
50 }
51 }
52
53 pub(crate) fn iter_all() -> impl Iterator<Item = Self> {
54 [Self::CPython, Self::PyPy, Self::GraalPy, Self::Pyodide].into_iter()
55 }
56
57 pub(crate) fn pretty(self) -> &'static str {
58 match self {
59 Self::CPython => "CPython",
60 Self::PyPy => "PyPy",
61 Self::GraalPy => "GraalPy",
62 Self::Pyodide => "Pyodide",
63 }
64 }
65
66 pub(crate) fn executable_name(self) -> &'static str {
68 match self {
69 Self::CPython | Self::Pyodide => "python",
70 Self::PyPy | Self::GraalPy => self.long_name(),
71 }
72 }
73
74 fn executable_install_name(self) -> &'static str {
76 match self {
77 Self::Pyodide => "pyodide",
78 _ => self.executable_name(),
79 }
80 }
81
82 pub(crate) fn matches_interpreter(self, interpreter: &Interpreter) -> bool {
83 match self {
84 Self::Pyodide => interpreter.os().is_emscripten(),
85 _ => interpreter
86 .implementation_name()
87 .eq_ignore_ascii_case(self.long_name()),
88 }
89 }
90}
91
92impl LenientImplementationName {
93 pub fn pretty(&self) -> &str {
94 match self {
95 Self::Known(implementation) => implementation.pretty(),
96 Self::Unknown(name) => name,
97 }
98 }
99
100 pub(crate) fn executable_install_name(&self) -> &str {
101 match self {
102 Self::Known(implementation) => implementation.executable_install_name(),
103 Self::Unknown(name) => name,
104 }
105 }
106}
107
108impl<'a> From<&'a LenientImplementationName> for &'a str {
109 fn from(value: &'a LenientImplementationName) -> &'a str {
110 match value {
111 LenientImplementationName::Known(implementation) => implementation.long_name(),
112 LenientImplementationName::Unknown(name) => name,
113 }
114 }
115}
116
117impl FromStr for ImplementationName {
118 type Err = Error;
119
120 fn from_str(s: &str) -> Result<Self, Self::Err> {
124 Self::iter_all()
125 .find(|implementation| {
126 s.eq_ignore_ascii_case(implementation.long_name())
127 || implementation
128 .short_name()
129 .is_some_and(|name| s.eq_ignore_ascii_case(name))
130 })
131 .ok_or_else(|| Error::UnknownImplementation(s.to_string()))
132 }
133}
134
135impl Display for ImplementationName {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.write_str(self.long_name())
138 }
139}
140
141impl From<&str> for LenientImplementationName {
142 fn from(s: &str) -> Self {
143 match ImplementationName::from_str(s) {
144 Ok(implementation) => Self::Known(implementation),
145 Err(_) => Self::Unknown(s.to_string()),
146 }
147 }
148}
149
150impl From<ImplementationName> for LenientImplementationName {
151 fn from(implementation: ImplementationName) -> Self {
152 Self::Known(implementation)
153 }
154}
155
156impl Display for LenientImplementationName {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 Self::Known(implementation) => implementation.fmt(f),
160 Self::Unknown(name) => f.write_str(&name.to_ascii_lowercase()),
161 }
162 }
163}