use std::io;
use std::path::{Path, PathBuf};
use thiserror::Error;
use uv_fs::Simplified;
use uv_python::{Interpreter, PythonEnvironment};
pub use virtualenv::{ClearNonVirtualenv, OnExisting, RemovalReason};
mod virtualenv;
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Io(#[from] io::Error),
#[error(
"Could not find a suitable Python executable for the virtual environment based on the interpreter: {0}"
)]
NotFound(String),
#[error(transparent)]
Python(#[from] uv_python::managed::Error),
#[error("A {name} already exists at: {}", path.user_display())]
Exists {
name: &'static str,
path: PathBuf,
},
#[error("uv will not clear a directory that is not a virtual environment")]
ClearNonVirtualenv {
path: PathBuf,
},
}
impl uv_errors::Hint for Error {
fn hints(&self) -> uv_errors::Hints<'_> {
match self {
Self::Exists { name, .. } => uv_errors::Hints::from(format!(
"Use the `--clear` flag or set `UV_VENV_CLEAR=1` to replace the existing {name}",
)),
Self::ClearNonVirtualenv { .. } => uv_errors::Hints::from(
"Use the `--force` flag to remove the existing directory anyway",
),
_ => uv_errors::Hints::none(),
}
}
}
#[derive(Debug)]
pub enum Prompt {
CurrentDirectoryName,
Static(String),
None,
}
impl Prompt {
pub fn from_args(prompt: Option<String>) -> Self {
match prompt {
Some(prompt) if prompt == "." => Self::CurrentDirectoryName,
Some(prompt) => Self::Static(prompt),
None => Self::None,
}
}
}
#[expect(clippy::fn_params_excessive_bools)]
pub fn create_venv(
location: &Path,
interpreter: Interpreter,
prompt: Prompt,
system_site_packages: bool,
on_existing: OnExisting,
relocatable: bool,
seed: bool,
upgradeable: bool,
) -> Result<PythonEnvironment, Error> {
let virtualenv = virtualenv::create(
location,
&interpreter,
prompt,
system_site_packages,
on_existing,
relocatable,
seed,
upgradeable,
)?;
let interpreter = interpreter.with_virtualenv(virtualenv);
Ok(PythonEnvironment::from_interpreter(interpreter))
}