From 5c4574e001cdb129061d01a516d226a3176b3890 Mon Sep 17 00:00:00 2001 From: Julian Popescu Date: Mon, 19 Feb 2024 19:13:37 +0100 Subject: [PATCH] create venv symlink on 'init_venv' --- src/dirs.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/src/dirs.rs b/src/dirs.rs index a537e8b..bf0a549 100644 --- a/src/dirs.rs +++ b/src/dirs.rs @@ -2,6 +2,13 @@ use crate::error::{self, Result}; use aqora_config::PyProject; use aqora_runner::python::PyEnv; use std::path::{Path, PathBuf}; +use thiserror::Error; + +const AQORA_DIRNAME: &str = ".aqora"; +const DATA_DIRNAME: &str = "data"; +const VENV_DIRNAME: &str = "venv"; +const PYPROJECT_FILENAME: &str = "pyproject.toml"; +const USE_CASE_FILENAME: &str = "use_case.toml"; pub async fn config_dir() -> Result { let mut path = dirs::data_dir().or_else(dirs::config_dir).ok_or_else(|| { @@ -25,25 +32,25 @@ pub async fn config_dir() -> Result { } pub fn project_config_dir(project_dir: impl AsRef) -> PathBuf { - project_dir.as_ref().join(".aqora") + project_dir.as_ref().join(AQORA_DIRNAME) } pub fn project_venv_dir(project_dir: impl AsRef) -> PathBuf { - project_config_dir(project_dir).join("venv") + project_config_dir(project_dir).join(VENV_DIRNAME) } pub fn project_data_dir(project_dir: impl AsRef, kind: impl ToString) -> PathBuf { project_config_dir(project_dir) - .join("data") + .join(DATA_DIRNAME) .join(kind.to_string()) } pub fn pyproject_path(project_dir: impl AsRef) -> PathBuf { - project_dir.as_ref().join("pyproject.toml") + project_dir.as_ref().join(PYPROJECT_FILENAME) } pub fn project_use_case_toml_path(project_dir: impl AsRef) -> PathBuf { - project_data_dir(project_dir, "use_case.toml") + project_data_dir(project_dir, USE_CASE_FILENAME) } pub async fn read_pyproject(project_dir: impl AsRef) -> Result { @@ -68,9 +75,37 @@ pub async fn read_pyproject(project_dir: impl AsRef) -> Result }) } +#[derive(Debug, Error)] +enum SymlinkError { + #[error("Failed to create symlink from {0} to {1}: {2}")] + CreateSymlink(PathBuf, PathBuf, std::io::Error), + #[error("{0} directory already exists. Symlink to {1} could not be created.")] + VenvDirExists(PathBuf, PathBuf), +} + +fn create_venv_symlink(project_dir: impl AsRef) -> Result<(), SymlinkError> { + let symlink_dir = project_dir.as_ref().join(VENV_DIRNAME); + let venv_dir = [AQORA_DIRNAME, VENV_DIRNAME].iter().collect::(); + + if symlink_dir.exists() { + if symlink_dir.read_link().ok().as_ref() != Some(&venv_dir) { + return Err(SymlinkError::VenvDirExists(symlink_dir, venv_dir)); + } + return Ok(()); + } + + #[cfg(unix)] + use std::os::unix::fs::symlink; + #[cfg(windows)] + use std::os::windows::fs::symlink_dir as symlink; + + symlink(&venv_dir, &symlink_dir) + .map_err(|err| SymlinkError::CreateSymlink(symlink_dir, venv_dir, err)) +} + pub async fn init_venv(project_dir: impl AsRef) -> Result { let venv_dir = project_venv_dir(&project_dir); - PyEnv::init(&venv_dir).await.map_err(|e| { + let env = PyEnv::init(&venv_dir).await.map_err(|e| { error::user( &format!("Failed to setup virtualenv: {}", e), &format!( @@ -78,5 +113,9 @@ pub async fn init_venv(project_dir: impl AsRef) -> Result { venv_dir.display() ), ) - }) + })?; + if let Err(err) = create_venv_symlink(&project_dir) { + eprintln!("WARN: {err}"); + } + Ok(env) }