1use std::{env, io};
2use std::fs::File;
3use std::io::{BufRead, BufReader, ErrorKind};
4use std::path::{Path, PathBuf};
5
6use crate::{PyenvVersion, PyenvVersionFrom};
7
8trait FlipResult<T, E> {
9 fn flip(self) -> Result<E, T>;
10}
11
12impl<T, E> FlipResult<T, E> for Result<T, E> {
13 fn flip(self) -> Result<E, T> {
14 match self {
15 Ok(t) => Err(t),
16 Err(e) => Ok(e),
17 }
18 }
19}
20
21impl PyenvVersion {
22 pub fn from(from: PyenvVersionFrom) -> impl Fn(String) -> Self {
23 move |version| Self { version, from }
24 }
25}
26
27fn read_python_version_file(path: &Path) -> io::Result<String> {
28 let file = File::open(path)?;
29 let reader = BufReader::new(file);
30 let version = reader.lines().next().ok_or(ErrorKind::NotFound)??;
31 Ok(version)
32}
33
34fn from_local_python_version_file_given_cwd(cwd: &Path) -> Result<io::Error, String> {
35 for dir in cwd.ancestors() {
36 let path = dir.join(".python-version");
37 read_python_version_file(path.as_path()).flip()?;
38 }
39 Ok(ErrorKind::NotFound.into())
40}
41
42fn from_local_python_version_file() -> io::Result<String> {
43 let cwd = env::current_dir()?;
44 let version = from_local_python_version_file_given_cwd(cwd.as_path()).flip()?;
45 Ok(version)
46}
47
48fn global_python_version_file_path(root: &Path) -> PathBuf {
49 root.join("version")
50}
51
52fn from_global_python_version_file(root: &Path) -> io::Result<String> {
53 let path = global_python_version_file_path(root);
54 read_python_version_file(path.as_path())
55}
56
57fn as_result(root: &Path) -> Result<(), PyenvVersion> {
59 use PyenvVersionFrom::*;
60 env::var("PYENV_VERSION").map(PyenvVersion::from(Shell)).flip()?;
61 from_local_python_version_file().map(PyenvVersion::from(Local)).flip()?;
62 from_global_python_version_file(root).map(PyenvVersion::from(Global)).flip()?;
63 Ok(())
64}
65
66pub fn pyenv_version(root: &Path) -> Option<PyenvVersion> {
67 as_result(root).err()
68}