1use std::io;
2use std::path::{Path, PathBuf};
3
4use thiserror::Error;
5
6use uv_fs::Simplified;
7use uv_python::{Interpreter, PythonEnvironment};
8
9pub use virtualenv::{ClearNonVirtualenv, OnExisting, RemovalReason};
10
11mod virtualenv;
12
13#[derive(Debug, Error)]
14pub enum Error {
15 #[error(transparent)]
16 Io(#[from] io::Error),
17 #[error(
18 "Could not find a suitable Python executable for the virtual environment based on the interpreter: {0}"
19 )]
20 NotFound(String),
21 #[error(transparent)]
22 Python(#[from] uv_python::managed::Error),
23 #[error("A {name} already exists at: {}", path.user_display())]
24 Exists {
25 name: &'static str,
27 path: PathBuf,
29 },
30 #[error("uv will not clear a directory that is not a virtual environment")]
31 ClearNonVirtualenv {
32 path: PathBuf,
34 },
35 #[error("Virtual environment path is not valid UTF-8: {}", path.user_display())]
36 NonUtf8Path {
37 path: PathBuf,
39 },
40}
41
42impl uv_errors::Hint for Error {
43 fn hints(&self) -> uv_errors::Hints<'_> {
44 match self {
45 Self::Exists { name, .. } => uv_errors::Hints::from(format!(
46 "Use the `--clear` flag or set `UV_VENV_CLEAR=1` to replace the existing {name}",
47 )),
48 Self::ClearNonVirtualenv { .. } => uv_errors::Hints::from(
49 "Use the `--force` flag to remove the existing directory anyway",
50 ),
51 _ => uv_errors::Hints::none(),
52 }
53 }
54}
55
56#[derive(Debug)]
58pub enum Prompt {
59 CurrentDirectoryName,
61 Static(String),
63 None,
66}
67
68impl Prompt {
69 pub fn from_args(prompt: Option<String>) -> Self {
71 match prompt {
72 Some(prompt) if prompt == "." => Self::CurrentDirectoryName,
73 Some(prompt) => Self::Static(prompt),
74 None => Self::None,
75 }
76 }
77}
78
79#[expect(clippy::fn_params_excessive_bools)]
81pub fn create_venv(
82 location: &Path,
83 interpreter: Interpreter,
84 prompt: Prompt,
85 system_site_packages: bool,
86 on_existing: OnExisting,
87 relocatable: bool,
88 seed: bool,
89 upgradeable: bool,
90) -> Result<PythonEnvironment, Error> {
91 let virtualenv = virtualenv::create(
93 location,
94 &interpreter,
95 prompt,
96 system_site_packages,
97 on_existing,
98 relocatable,
99 seed,
100 upgradeable,
101 )?;
102
103 let interpreter = interpreter.with_virtualenv(virtualenv);
105 Ok(PythonEnvironment::from_interpreter(interpreter))
106}