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)]
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) 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 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 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 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 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}