use crate::{Block, Color, Environment, Icon, IconMode, Pretty, Style, WithStyle as _};
use std::{
ffi::OsStr,
fs::File,
io::{BufRead as _, BufReader},
path::{Path, PathBuf},
};
pub struct Venv {
name: String,
version: String,
}
super::register_block!(Venv);
impl Block for Venv {
fn new(_: &Environment) -> Option<Self> {
let path = PathBuf::from(std::env::var("VIRTUAL_ENV").ok()?);
Some(Venv {
name: venv_name(&path).to_owned(),
version: venv_ver(&path)
.unwrap_or_default()
.unwrap_or("<sys?>".to_owned()),
})
}
}
impl Pretty for Venv {
fn pretty(&self, f: &mut std::fmt::Formatter, mode: IconMode) -> std::fmt::Result {
f.with_style(Color::YELLOW, Style::empty(), |f| {
write!(f, "[{} {}|{}]", self.icon(mode), self.version, self.name)
})
}
}
impl Icon for Venv {
fn icon(&self, mode: IconMode) -> &'static str {
use IconMode::*;
match &mode {
Text => "py",
Icons | MinimalIcons => "",
}
}
}
fn venv_name(path: &Path) -> &str {
path.ancestors()
.filter_map(Path::file_name)
.filter_map(OsStr::to_str)
.find(|name| !["venv", "env", "virtualenv"].contains(name))
.map_or("<venv>", |name| {
["venv", "virtualenv", "env", "-", "_"]
.iter()
.fold(name, |s, suf| s.strip_suffix(suf).unwrap_or(s))
})
}
fn venv_ver(path: &Path) -> std::io::Result<Option<String>> {
Ok(BufReader::new(File::open(path.join("pyvenv.cfg"))?)
.lines()
.find_map(|line| {
Some(
line.ok()?
.strip_prefix("version")?
.trim_start_matches(' ')
.strip_prefix('=')?
.trim_start_matches(' ')
.to_owned(),
)
}))
}