diff --git a/commands.go b/commands.go index 369a764..93c491e 100644 --- a/commands.go +++ b/commands.go @@ -1,10 +1,9 @@ package main import ( - "errors" "fmt" "os" - "strings" + "slices" ) var DIRECTORIES = []string{ @@ -54,6 +53,12 @@ func UninstallPython(args []string, flags Flags, currentState State) error { return err } +func InstallPython(args []string, flags Flags, currentState State) error { + version := args[1] + + return InstallPythonDistribution(version, flags.NoCache, flags.Verbose) +} + func Use(args []string, flags Flags, currentState State) error { version := args[1] if err := ValidateVersion(version); err != nil { @@ -71,7 +76,8 @@ func Use(args []string, flags Flags, currentState State) error { } if !found { - return errors.New("Version not installed.") + fmt.Println("Version not installed. Installing it first.") + InstallPythonDistribution(version, flags.NoCache, flags.Verbose) } WriteState(version) @@ -80,20 +86,19 @@ func Use(args []string, flags Flags, currentState State) error { return nil } func ListVersions(args []string, flags Flags, currentState State) error { - runtimesDir := GetStatePath("runtimes") - entries, err := os.ReadDir(runtimesDir) + installedVersions, err := ListInstalledVersions() if err != nil { return err } - if len(entries) == 0 { + if len(installedVersions) == 0 { fmt.Println("No versions installed!") return nil } - for _, d := range entries { - fmt.Println(strings.TrimPrefix(d.Name(), "py-")) + for _, d := range installedVersions { + fmt.Println(d) } return nil @@ -102,15 +107,20 @@ func ListVersions(args []string, flags Flags, currentState State) error { // Which prints out the system path to the executable being used by `python`. func Which(args []string, flags Flags, currentState State) error { selectedVersion, _ := DetermineSelectedPythonVersion(currentState) + installedVersions, _ := ListInstalledVersions() + isInstalled := slices.Contains(installedVersions, selectedVersion.Version) var printedPath string if selectedVersion.Source == "system" { _, sysPath := DetermineSystemPython() printedPath = fmt.Sprintf("%s (system)", sysPath) - } else { + } else if isInstalled { tag := VersionStringToStruct(selectedVersion.Version) printedPath = GetStatePath("runtimes", fmt.Sprintf("py-%s", selectedVersion.Version), "bin", fmt.Sprintf("python%s", tag.MajorMinor())) + } else { + fmt.Printf("The desired version (%s) is not installed.\n", selectedVersion.Version) + return nil } prefix := "Python path: " @@ -130,6 +140,12 @@ func Which(args []string, flags Flags, currentState State) error { // under "source", if the system Python is used, "system" is returned as a source. func CurrentVersion(args []string, flags Flags, currentState State) error { selectedVersion, _ := DetermineSelectedPythonVersion(currentState) + installedVersions, _ := ListInstalledVersions() + isInstalled := slices.Contains(installedVersions, selectedVersion.Version) + + if !isInstalled { + fmt.Println(Bold(Yellow("WARNING: This version is not installed."))) + } if flags.RawOutput { fmt.Println(selectedVersion.Version) diff --git a/install_python.go b/install_python.go index 9664203..a517eb4 100644 --- a/install_python.go +++ b/install_python.go @@ -30,15 +30,12 @@ func (t VersionTag) MajorMinor() string { return fmt.Sprintf("%s.%s", t.Major, t.Minor) } -func InstallPython(args []string, flags Flags, currentState State) error { - verbose := flags.Verbose - version := args[1] - +func InstallPythonDistribution(version string, noCache bool, verbose bool) error { if err := ValidateVersion(version); err != nil { return err } - packageMetadata, dlerr := downloadSource(version, flags.NoCache) + packageMetadata, dlerr := downloadSource(version, noCache) if dlerr != nil { return dlerr diff --git a/pythonversion.go b/pythonversion.go index 66a0ffd..3111eba 100644 --- a/pythonversion.go +++ b/pythonversion.go @@ -12,6 +12,23 @@ type SelectedVersion struct { Source string } +func ListInstalledVersions() ([]string, error) { + runtimesDir := GetStatePath("runtimes") + entries, err := os.ReadDir(runtimesDir) + + if err != nil { + return []string{}, err + } + + installedVersions := []string{} + + for _, d := range entries { + installedVersions = append(installedVersions, strings.TrimPrefix(d.Name(), "py-")) + } + + return installedVersions, nil +} + // SearchForPythonVersionFile crawls up to the system root to find any // .python-version file that could set the current version. func SearchForPythonVersionFile() (SelectedVersion, bool) { diff --git a/pythonversion_test.go b/pythonversion_test.go index d9c141f..d2c07a0 100644 --- a/pythonversion_test.go +++ b/pythonversion_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "path" + "slices" "testing" ) @@ -131,5 +132,47 @@ func TestSearchForPythonVersionFileReturnsOnRootIfNoneFound(t *testing.T) { if versionFound.Version != "" || found { t.Errorf("Did not expect any result, found %s.", versionFound.Version) } - +} + +func TestListInstalledVersion(t *testing.T) { + defer SetupAndCleanupEnvironment(t)() + + versions := []string{"1.2.3", "4.5.6", "7.8.9"} + + os.Mkdir(GetStatePath("runtimes"), 0750) + for _, version := range versions { + os.Mkdir(GetStatePath("runtimes", "py-"+version), 0750) + } + + installedVersions, _ := ListInstalledVersions() + + if !slices.Equal(installedVersions, versions) { + t.Errorf("Expected %s, got %s.", versions, installedVersions) + } +} + +func TestListInstalledVersionNoVersionsInstalled(t *testing.T) { + defer SetupAndCleanupEnvironment(t)() + + os.Mkdir(GetStatePath("runtimes"), 0750) + + installedVersions, _ := ListInstalledVersions() + + if len(installedVersions) != 0 { + t.Errorf("Expected 0 elements, got %d (%s).", len(installedVersions), installedVersions) + } +} + +func TestListInstalledVersionNoRuntimesDir(t *testing.T) { + defer SetupAndCleanupEnvironment(t)() + + installedVersions, err := ListInstalledVersions() + + if len(installedVersions) != 0 { + t.Errorf("Expected 0 elements, got %d (%s).", len(installedVersions), installedVersions) + } + + if err == nil { + t.Errorf("Expected error to be returned, got nil.") + } } diff --git a/style.go b/style.go index 6404ad2..7161250 100644 --- a/style.go +++ b/style.go @@ -3,10 +3,15 @@ package main import "fmt" const ( - RESET = "\033[0m" - BOLD = "\033[1m" + RESET = "\033[0m" + BOLD = "\033[1m" + YELLOW = "\033[33m" ) +func Yellow(text string) string { + return fmt.Sprintf("%s%s%s", YELLOW, text, RESET) +} + func Bold(text string) string { return fmt.Sprintf("%s%s%s", BOLD, text, RESET) } diff --git a/v.go b/v.go index e10a03d..2e753c1 100644 --- a/v.go +++ b/v.go @@ -5,7 +5,7 @@ import ( ) const ( - Version = "0.0.7" + Version = "0.0.8" Author = "Marc Cataford " Homepage = "https://github.com/mcataford/v" )