Skip to content

Commit

Permalink
Support for fish
Browse files Browse the repository at this point in the history
+ Support for fish shell
+ command `rsvenv install`
  • Loading branch information
Shtsh committed Apr 19, 2024
1 parent 3367eea commit 1c4351e
Show file tree
Hide file tree
Showing 16 changed files with 283 additions and 77 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ Cargo.lock
*.pdb

.idea
.vscode

6 changes: 2 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rsvenv"
version = "0.3.0"
version = "0.4.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand All @@ -18,9 +18,7 @@ serde_derive = "1.0.197"
shellexpand = "3.1.0"
simplelog = { version = "0.12.2", features = ["paris"] }
sysinfo = "0.30.7"
tinytemplate = "1.2.1"

[dev-dependencies]
cfg-if = "1.0.0"
mockall = "0.12.1"
tempfile = "3.10.1"

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ and allows to use any python binary to create a new one.

In order to use automatic virtual environment activation it is necessary to add to the end of ~/.zshrc (or ~/.bashrc)
```bash
eval "$(rsvenv init)"
rsvenv install
```

To see available virtual environments
Expand All @@ -41,7 +41,7 @@ The created virtual environment name will be python_version/venv_name

After this it is possible to use this venv in current directory
```bash
rsvenv use venv_name
rsvenv use python_version/venv_name
```
This command will create file .python-virtualenv that contains venv name

Expand All @@ -57,4 +57,4 @@ It is possible to configure parameters via environment variables
| Variable | Possible values | Default value | Description |
|------------------|----------------|--------------|-----------------------------------------------------------------------------------|
| $RSVENV_VERBOSITY | i32 0..3 | 1 | How verbose the program must be: 0 - no messages, 1 - Info level, 2 - Debug level |
| $RSVENV_PATH | String | "~/.rsenv" | Directory to store all the virtual environments|
| $RSVENV_PATH | String | "~/.rsvenv" | Directory to store all the virtual environments|
5 changes: 5 additions & 0 deletions src/arguments.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clap::{Parser, Subcommand};
use error_stack::Result;
use simplelog::debug;

use crate::errors::CommandExecutionError;

Expand All @@ -12,6 +13,8 @@ pub struct Cli {

#[derive(Subcommand)]
pub enum Commands {
#[clap(name = "install", about = "Configure the shell to use RSVENV")]
Install(crate::commands::install::Command),
#[clap(name = "init", about = "Configure the shell environment for pyenv")]
Init(crate::commands::init::Command),
#[clap(
Expand Down Expand Up @@ -41,6 +44,7 @@ pub enum Commands {

impl Commands {
pub fn execute(&self) -> Result<(), CommandExecutionError> {
debug!("executing command");
match self {
Commands::Init(command) => command.execute(),
Commands::Activate(command) => command.execute(),
Expand All @@ -50,6 +54,7 @@ impl Commands {
Commands::Use(command) => command.execute(),
Commands::Create(command) => command.execute(),
Commands::Delete(command) => command.execute(),
Commands::Install(command) => command.execute(),
}
}
}
Expand Down
23 changes: 18 additions & 5 deletions src/commands/activate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clap::Parser;
use error_stack::{Report, Result};
use error_stack::{Report, Result, ResultExt};
use simplelog::{debug, error};

use crate::{
Expand All @@ -14,7 +14,7 @@ pub struct Command {
}

fn try_activate(v: VirtualEnvironment, venv: &String) -> Result<(), VirtualEnvError> {
if v.kind.list().contains(venv) {
if v.list().contains(venv) {
if let Err(e) = v.activate(Some(venv)) {
error!("{e}");
return Err(e);
Expand All @@ -29,13 +29,26 @@ fn try_activate(v: VirtualEnvironment, venv: &String) -> Result<(), VirtualEnvEr

impl Command {
pub fn execute(&self) -> Result<(), CommandExecutionError> {
let context = CommandExecutionError {
command: "activate".into(),
};
if let Err(e) = VirtualEnvironment::deactivate(true) {
debug!("{e}");
};
if try_activate(VirtualEnvironment { kind: &Rsenv }, &self.virtualenv).is_ok() {
if try_activate(
VirtualEnvironment::new(&Rsenv).change_context(context.clone())?,
&self.virtualenv,
)
.is_ok()
{
return Ok(());
}
if try_activate(VirtualEnvironment { kind: &Pyenv }, &self.virtualenv).is_ok() {
if try_activate(
VirtualEnvironment::new(&Pyenv).change_context(context)?,
&self.virtualenv,
)
.is_ok()
{
return Ok(());
}
error!(
Expand All @@ -45,6 +58,6 @@ impl Command {
Err(Report::new(CommandExecutionError {
command: "activate".into(),
})
.attach_printable(format!("Unable to activate vitualenv {}", self.virtualenv)))
.attach_printable(format!("Unable to activate virtualenv {}", self.virtualenv)))
}
}
1 change: 1 addition & 0 deletions src/commands/chdir_hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ impl Command {
if std::env::var("VIRTUAL_ENV").is_err() {
return false;
}
debug!("VIRTUAL_ENV presents -> Virtual environment is active");
match std::env::var("RSVENV_ACTIVATE_PATH") {
Ok(value) => {
let activated_in_path = Path::new(&value);
Expand Down
4 changes: 2 additions & 2 deletions src/commands/init.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::errors::CommandExecutionError;
use crate::shell::{detect_shell, Hook};
use crate::shell::SupportedShell;
use clap::Parser;
use error_stack::{Result, ResultExt};
use std::io;
Expand All @@ -13,7 +13,7 @@ impl Command {
let error_context = CommandExecutionError {
command: "init".into(),
};
let shell = detect_shell()
let shell = SupportedShell::new()
.change_context(error_context.clone())
.attach("Unable to detect current shell")?;

Expand Down
64 changes: 64 additions & 0 deletions src/commands/install.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::fs::{read_to_string, OpenOptions};
use std::io::Write;

use crate::errors::CommandExecutionError;
use crate::shell::SupportedShell;
use clap::Parser;
use error_stack::{Report, Result, ResultExt};
use simplelog::{debug, error, info};

#[derive(Debug, Parser)]
pub struct Command {}

fn find_occurence(filename: &str, substr: &str) -> Result<bool, CommandExecutionError> {
for line in read_to_string(filename)
.change_context(CommandExecutionError {
command: "install".into(),
})
.attach_printable("Unable to read config file")?
.lines()
{
if line.contains(substr) {
return Ok(true);
}
}
Ok(false)
}

impl Command {
pub fn execute(&self) -> Result<(), CommandExecutionError> {
let error_context = CommandExecutionError {
command: "install".into(),
};
let shell = SupportedShell::new()
.change_context(error_context.clone())
.attach("Unable to detect current shell")?;

let config_path = shell
.get_config_path()
.change_context(error_context.clone())?;

debug!("Installing to {config_path}");

let init_line = shell.get_init_command();

if find_occurence(&config_path, init_line)? {
error!("RSVENV already installed to config");
return Err(Report::new(CommandExecutionError {
command: "install".into(),
}));
};
let mut file = OpenOptions::new()
.append(true)
.open(config_path)
.change_context(error_context.clone())
.attach("Unable to open config file for appending")?;

file.write_all(init_line.as_bytes())
.change_context(error_context)
.attach_printable("Error when writing to the file")?;

info!("Successfully modified shell config. Please restart you session");
Ok(())
}
}
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ pub mod create;
pub mod deactivate;
pub mod delete;
pub mod init;
pub mod install;
pub mod list;
pub mod use_command;
2 changes: 1 addition & 1 deletion src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl Settings {
pub fn new() -> Result<Self, ConfigError> {
let settings = Config::builder()
.set_default("verbosity", 1)?
.set_default("path", "~/.rsenv".to_string())?
.set_default("path", "~/.rsvenv".to_string())?
.add_source(Environment::with_prefix("RSVENV"))
.build()?;
settings.try_deserialize()
Expand Down
6 changes: 5 additions & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use core::fmt;

use error_stack::Context;

#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum VirtualEnvError {
VenvBuildError,
NotVirtualEnv(String),
Expand All @@ -12,6 +12,8 @@ pub enum VirtualEnvError {
IOError,
ConfigurationError,
IncorrectName,
TemplateRenderError,
ShellDetectionError,
}

impl fmt::Display for VirtualEnvError {
Expand All @@ -29,6 +31,8 @@ impl fmt::Display for VirtualEnvError {
VirtualEnvError::ConfigurationError => "Configuration error".to_owned(),
VirtualEnvError::CreatingError => "Error while creating virtual environment".to_owned(),
VirtualEnvError::IncorrectName => "Incorrect virtual environment name".to_owned(),
VirtualEnvError::TemplateRenderError => "Unable to render template".to_owned(),
VirtualEnvError::ShellDetectionError => "Unable to detect current shell".to_owned(),
};
f.write_str(&data)
}
Expand Down
15 changes: 15 additions & 0 deletions src/shell/bash.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub const CONFIG: &str = "~/.bashrc";

pub const HOOK: &str = r#"
declare -f -F rsvenv > /dev/null && unset -f rsvenv
Expand Down Expand Up @@ -34,3 +36,16 @@ if ! [[ "\${PROMPT_COMMAND-}" =~ _rs_venv_virtualenv_hook ]]; then
PROMPT_COMMAND="_rs_venv_virtualenv_hook;\${PROMPT_COMMAND-}"
fi
"#;

pub static ACTIVATE_TEMPLATE: &str = r#"
source {activate_path}
export RSVENV_ACTIVATE_PATH={current_directory}
"#;

pub static DEACTIVATE_TEMPLATE: &str = r#"
unset RSVENV_DEACTIVATE_PATH
deactivate
{{ if forced }}export RSVENV_DEACTIVATE_PATH=$RSVENV_ACTIVATE_PATH{{ endif }}
"#;

pub static INIT_COMMAND: &str = r#"eval "$(rsvenv init)""#;
35 changes: 35 additions & 0 deletions src/shell/fish.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
pub const CONFIG: &str = "~/.config/fish/config.fish";

pub const HOOK: &str = r#"
if set -q rsvenv;
set -e rsvenv
end
set -g RS_VENV_PATH (which rsvenv)
function _rs_venv_virtualenv_hook --on-event fish_prompt;
$RS_VENV_PATH hook | source
end
function rsvenv
set eval_commands activate deactivate delete use
if contains $argv[1] $eval_commands
$RS_VENV_PATH $argv | source
else
$RS_VENV_PATH $argv
end
end
"#;

pub static ACTIVATE_TEMPLATE: &str = r#"
source {activate_path}
set -gx RSVENV_ACTIVATE_PATH {current_directory}
"#;

pub static DEACTIVATE_TEMPLATE: &str = r#"
set -e RSVENV_DEACTIVATE_PATH
deactivate
{{ if forced }}set -gx RSVENV_DEACTIVATE_PATH $RSVENV_ACTIVATE_PATH{{ endif }}
"#;

pub static INIT_COMMAND: &str = "status --is-interactive; and source (rsvenv init |psub)";
Loading

0 comments on commit 1c4351e

Please sign in to comment.