refactor: simplify project structure (#2)
* refactor: remove file structure cruft * refactor: flatten file structure entirely * refactor: combine utils
This commit is contained in:
parent
7a1912c02a
commit
ab711a28b8
16 changed files with 207 additions and 263 deletions
|
@ -1,15 +1,18 @@
|
||||||
package argparse
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
stateManager "v/internal/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Flags struct {
|
||||||
|
Verbose bool
|
||||||
|
}
|
||||||
|
|
||||||
// Command definition for CLI subcommands.
|
// Command definition for CLI subcommands.
|
||||||
type Command struct {
|
type Command struct {
|
||||||
Label string
|
Label string
|
||||||
Handler func([]string, Flags, stateManager.State) error
|
Handler func([]string, Flags, State) error
|
||||||
Usage string
|
Usage string
|
||||||
Description string
|
Description string
|
||||||
}
|
}
|
||||||
|
@ -29,7 +32,7 @@ type CLI struct {
|
||||||
// This specifies a label that is used to route the user input to
|
// This specifies a label that is used to route the user input to
|
||||||
// the right command, a handler that is called when the label is used,
|
// the right command, a handler that is called when the label is used,
|
||||||
// and usage/description details that get included in autogenerated help messaging.
|
// and usage/description details that get included in autogenerated help messaging.
|
||||||
func (c CLI) AddCommand(label string, handler func([]string, Flags, stateManager.State) error, usage string, description string) CLI {
|
func (c CLI) AddCommand(label string, handler func([]string, Flags, State) error, usage string, description string) CLI {
|
||||||
if c.Commands == nil {
|
if c.Commands == nil {
|
||||||
c.Commands = map[string]Command{}
|
c.Commands = map[string]Command{}
|
||||||
c.OrderedCommands = []string{}
|
c.OrderedCommands = []string{}
|
||||||
|
@ -43,7 +46,7 @@ func (c CLI) AddCommand(label string, handler func([]string, Flags, stateManager
|
||||||
|
|
||||||
// Executes one of the registered commands if any match the provided
|
// Executes one of the registered commands if any match the provided
|
||||||
// user arguments.
|
// user arguments.
|
||||||
func (c CLI) Run(args []string, currentState stateManager.State) error {
|
func (c CLI) Run(args []string, currentState State) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
c.Help()
|
c.Help()
|
||||||
return nil
|
return nil
|
||||||
|
@ -56,7 +59,7 @@ func (c CLI) Run(args []string, currentState stateManager.State) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := CollectFlags(args)
|
flags := collectFlags(args)
|
||||||
return c.Commands[command].Handler(args, flags, currentState)
|
return c.Commands[command].Handler(args, flags, currentState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,3 +79,21 @@ func (c CLI) Help() {
|
||||||
|
|
||||||
fmt.Println(helpString)
|
fmt.Println(helpString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Traverses input arguments and extracts flags of
|
||||||
|
// the form --<flag-label>.
|
||||||
|
func collectFlags(args []string) Flags {
|
||||||
|
collected := Flags{}
|
||||||
|
|
||||||
|
for _, arg := range args {
|
||||||
|
if !strings.HasPrefix(arg, "--") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg == "--verbose" {
|
||||||
|
collected.Verbose = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return collected
|
||||||
|
}
|
45
cmd/v.go
45
cmd/v.go
|
@ -1,45 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
argparse "v/internal/argparse"
|
|
||||||
stateManager "v/internal/state"
|
|
||||||
subcommands "v/internal/subcommands"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
Version = "0.0.1"
|
|
||||||
Author = "mcataford <hello@karnov.club>"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Main entrypoint.
|
|
||||||
func main() {
|
|
||||||
args := os.Args[1:]
|
|
||||||
currentState := stateManager.ReadState()
|
|
||||||
|
|
||||||
cli := argparse.CLI{
|
|
||||||
Metadata: map[string]string{
|
|
||||||
"Version": Version,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := cli.AddCommand(
|
|
||||||
"install", subcommands.InstallPython, "v install <version>", "Downloads, builds and installs a new version of Python.",
|
|
||||||
).AddCommand(
|
|
||||||
"uninstall", subcommands.UninstallPython, "v uninstall <version>", "Uninstalls the given Python version.",
|
|
||||||
).AddCommand(
|
|
||||||
"use", subcommands.Use, "v use <version>", "Selects which Python version to use.",
|
|
||||||
).AddCommand(
|
|
||||||
"ls", subcommands.ListVersions, "v ls", "Lists the installed Python versions.",
|
|
||||||
).AddCommand(
|
|
||||||
"where", subcommands.Where, "v where", "Prints the path to the current Python version.",
|
|
||||||
).AddCommand(
|
|
||||||
"which", subcommands.Which, "v which", "Prints the current Python version.",
|
|
||||||
).AddCommand(
|
|
||||||
"init", subcommands.Initialize, "v init", "Initializes the v state.",
|
|
||||||
).Run(args, currentState)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
111
commands.go
Normal file
111
commands.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DIRECTORIES = []string{
|
||||||
|
"cache",
|
||||||
|
"runtimes",
|
||||||
|
"shims",
|
||||||
|
}
|
||||||
|
|
||||||
|
var SHIMS = []string{
|
||||||
|
"python",
|
||||||
|
"python3",
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_PERMISSION = 0775
|
||||||
|
|
||||||
|
func writeShim(shimPath string) error {
|
||||||
|
shimContent := []byte("#!/bin/bash\n$(vm where) $@")
|
||||||
|
if err := os.WriteFile(shimPath, shimContent, DEFAULT_PERMISSION); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets up directories and files used to store downloaded archives,
|
||||||
|
// installed runtimes and metadata.
|
||||||
|
func Initialize(args []string, flags Flags, currentState State) error {
|
||||||
|
stateDirectory := GetStateDirectory()
|
||||||
|
|
||||||
|
os.Mkdir(stateDirectory, DEFAULT_PERMISSION)
|
||||||
|
for _, dir := range DIRECTORIES {
|
||||||
|
os.Mkdir(GetPathFromStateDirectory(dir), DEFAULT_PERMISSION)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, shim := range SHIMS {
|
||||||
|
writeShim(GetPathFromStateDirectory(path.Join("shims", shim)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func UninstallPython(args []string, flags Flags, currentState State) error {
|
||||||
|
runtimePath := GetPathFromStateDirectory(path.Join("runtimes", fmt.Sprintf("py-%s", args[1])))
|
||||||
|
err := os.RemoveAll(runtimePath)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Use(args []string, flags Flags, currentState State) error {
|
||||||
|
version := args[1]
|
||||||
|
if err := ValidateVersion(version); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
availableVersions := GetAvailableVersions()
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, v := range availableVersions {
|
||||||
|
if v == version {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return errors.New("Version not installed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteState(version)
|
||||||
|
fmt.Printf("Now using Python %s\n", version)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func ListVersions(args []string, flags Flags, currentState State) error {
|
||||||
|
runtimesDir := GetPathFromStateDirectory("runtimes")
|
||||||
|
entries, err := os.ReadDir(runtimesDir)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(entries) == 0 {
|
||||||
|
fmt.Println("No versions installed!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range entries {
|
||||||
|
fmt.Println(strings.TrimPrefix(d.Name(), "py-"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Where(args []string, flags Flags, currentState State) error {
|
||||||
|
version := currentState.GlobalVersion
|
||||||
|
tag := VersionStringToStruct(version)
|
||||||
|
withoutPatch := fmt.Sprintf("%s.%s", tag.Major, tag.Minor)
|
||||||
|
fmt.Printf("%s/runtimes/py-%s/bin/python%s\n", GetStateDirectory(), currentState.GlobalVersion, withoutPatch)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Which(args []string, flags Flags, currentState State) error {
|
||||||
|
fmt.Println(currentState.GlobalVersion)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package subcommands
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -8,9 +8,6 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
argparse "v/internal/argparse"
|
|
||||||
stateManager "v/internal/state"
|
|
||||||
util "v/internal/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var pythonReleasesBaseURL = "https://www.python.org/ftp/python"
|
var pythonReleasesBaseURL = "https://www.python.org/ftp/python"
|
||||||
|
@ -27,11 +24,11 @@ type VersionTag struct {
|
||||||
Patch string
|
Patch string
|
||||||
}
|
}
|
||||||
|
|
||||||
func InstallPython(args []string, flags argparse.Flags, currentState stateManager.State) error {
|
func InstallPython(args []string, flags Flags, currentState State) error {
|
||||||
verbose := flags.Verbose
|
verbose := flags.Verbose
|
||||||
version := args[1]
|
version := args[1]
|
||||||
|
|
||||||
if err := validateVersion(version); err != nil {
|
if err := ValidateVersion(version); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,13 +50,13 @@ func InstallPython(args []string, flags argparse.Flags, currentState stateManage
|
||||||
// and stores it at <destination>.
|
// and stores it at <destination>.
|
||||||
func downloadSource(version string, destination string) (PackageMetadata, error) {
|
func downloadSource(version string, destination string) (PackageMetadata, error) {
|
||||||
archiveName := fmt.Sprintf("Python-%s.tgz", version)
|
archiveName := fmt.Sprintf("Python-%s.tgz", version)
|
||||||
archivePath := stateManager.GetPathFromStateDirectory(path.Join("cache", archiveName))
|
archivePath := GetPathFromStateDirectory(path.Join("cache", archiveName))
|
||||||
sourceUrl := fmt.Sprintf("%s/%s/%s", pythonReleasesBaseURL, version, archiveName)
|
sourceUrl := fmt.Sprintf("%s/%s/%s", pythonReleasesBaseURL, version, archiveName)
|
||||||
file, _ := os.Create(archivePath)
|
file, _ := os.Create(archivePath)
|
||||||
|
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
|
|
||||||
dlPrint := util.StartFmtGroup(fmt.Sprintf("Downloading source for Python %s", version))
|
dlPrint := StartFmtGroup(fmt.Sprintf("Downloading source for Python %s", version))
|
||||||
|
|
||||||
dlPrint(fmt.Sprintf("Fetching from %s", sourceUrl))
|
dlPrint(fmt.Sprintf("Fetching from %s", sourceUrl))
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
@ -80,12 +77,12 @@ func downloadSource(version string, destination string) (PackageMetadata, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, error) {
|
func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, error) {
|
||||||
buildPrint := util.StartFmtGroup(fmt.Sprintf("Building from source"))
|
buildPrint := StartFmtGroup(fmt.Sprintf("Building from source"))
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
buildPrint(fmt.Sprintf("Unpacking source for %s", pkgMeta.ArchivePath))
|
buildPrint(fmt.Sprintf("Unpacking source for %s", pkgMeta.ArchivePath))
|
||||||
|
|
||||||
_, untarErr := RunCommand([]string{"tar", "zxvf", pkgMeta.ArchivePath}, stateManager.GetPathFromStateDirectory("cache"), !verbose)
|
_, untarErr := RunCommand([]string{"tar", "zxvf", pkgMeta.ArchivePath}, GetPathFromStateDirectory("cache"), !verbose)
|
||||||
|
|
||||||
if untarErr != nil {
|
if untarErr != nil {
|
||||||
return pkgMeta, untarErr
|
return pkgMeta, untarErr
|
||||||
|
@ -95,7 +92,7 @@ func buildFromSource(pkgMeta PackageMetadata, verbose bool) (PackageMetadata, er
|
||||||
|
|
||||||
buildPrint("Configuring installer")
|
buildPrint("Configuring installer")
|
||||||
|
|
||||||
targetDirectory := stateManager.GetPathFromStateDirectory(path.Join("runtimes", fmt.Sprintf("py-%s", pkgMeta.Version)))
|
targetDirectory := GetPathFromStateDirectory(path.Join("runtimes", fmt.Sprintf("py-%s", pkgMeta.Version)))
|
||||||
|
|
||||||
_, configureErr := RunCommand([]string{"./configure", fmt.Sprintf("--prefix=%s", targetDirectory), "--enable-optimizations"}, unzippedRoot, !verbose)
|
_, configureErr := RunCommand([]string{"./configure", fmt.Sprintf("--prefix=%s", targetDirectory), "--enable-optimizations"}, unzippedRoot, !verbose)
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package argparse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Flags struct {
|
|
||||||
Verbose bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func CollectFlags(args []string) Flags {
|
|
||||||
collected := Flags{}
|
|
||||||
|
|
||||||
for _, arg := range args {
|
|
||||||
if !strings.HasPrefix(arg, "--") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if arg == "--verbose" {
|
|
||||||
collected.Verbose = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return collected
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package subcommands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
argparse "v/internal/argparse"
|
|
||||||
stateManager "v/internal/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
var DIRECTORIES = []string{
|
|
||||||
"cache",
|
|
||||||
"runtimes",
|
|
||||||
"shims",
|
|
||||||
}
|
|
||||||
|
|
||||||
var SHIMS = []string{
|
|
||||||
"python",
|
|
||||||
"python3",
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_PERMISSION = 0775
|
|
||||||
|
|
||||||
func writeShim(shimPath string) error {
|
|
||||||
shimContent := []byte("#!/bin/bash\n$(vm where) $@")
|
|
||||||
if err := os.WriteFile(shimPath, shimContent, DEFAULT_PERMISSION); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets up directories and files used to store downloaded archives,
|
|
||||||
// installed runtimes and metadata.
|
|
||||||
func Initialize(args []string, flags argparse.Flags, currentState stateManager.State) error {
|
|
||||||
stateDirectory := stateManager.GetStateDirectory()
|
|
||||||
|
|
||||||
os.Mkdir(stateDirectory, DEFAULT_PERMISSION)
|
|
||||||
for _, dir := range DIRECTORIES {
|
|
||||||
os.Mkdir(stateManager.GetPathFromStateDirectory(dir), DEFAULT_PERMISSION)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, shim := range SHIMS {
|
|
||||||
writeShim(stateManager.GetPathFromStateDirectory(path.Join("shims", shim)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package subcommands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
argparse "v/internal/argparse"
|
|
||||||
stateManager "v/internal/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ListVersions(args []string, flags argparse.Flags, currentState stateManager.State) error {
|
|
||||||
runtimesDir := stateManager.GetPathFromStateDirectory("runtimes")
|
|
||||||
entries, err := os.ReadDir(runtimesDir)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(entries) == 0 {
|
|
||||||
fmt.Println("No versions installed!")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, d := range entries {
|
|
||||||
fmt.Println(strings.TrimPrefix(d.Name(), "py-"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package subcommands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
argparse "v/internal/argparse"
|
|
||||||
stateManager "v/internal/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
func UninstallPython(args []string, flags argparse.Flags, currentState stateManager.State) error {
|
|
||||||
runtimePath := stateManager.GetPathFromStateDirectory(path.Join("runtimes", fmt.Sprintf("py-%s", args[1])))
|
|
||||||
err := os.RemoveAll(runtimePath)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package subcommands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
argparse "v/internal/argparse"
|
|
||||||
stateManager "v/internal/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Use(args []string, flags argparse.Flags, currentState stateManager.State) error {
|
|
||||||
version := args[1]
|
|
||||||
if err := validateVersion(version); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
availableVersions := stateManager.GetAvailableVersions()
|
|
||||||
|
|
||||||
found := false
|
|
||||||
for _, v := range availableVersions {
|
|
||||||
if v == version {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return errors.New("Version not installed.")
|
|
||||||
}
|
|
||||||
|
|
||||||
stateManager.WriteState(version)
|
|
||||||
fmt.Printf("Now using Python %s\n", version)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package subcommands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
argparse "v/internal/argparse"
|
|
||||||
stateManager "v/internal/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
func versionStringToStruct(version string) VersionTag {
|
|
||||||
splitVersion := strings.Split(version, ".")
|
|
||||||
|
|
||||||
return VersionTag{Major: splitVersion[0], Minor: splitVersion[1], Patch: splitVersion[2]}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Where(args []string, flags argparse.Flags, currentState stateManager.State) error {
|
|
||||||
version := currentState.GlobalVersion
|
|
||||||
tag := versionStringToStruct(version)
|
|
||||||
withoutPatch := fmt.Sprintf("%s.%s", tag.Major, tag.Minor)
|
|
||||||
fmt.Printf("%s/runtimes/py-%s/bin/python%s\n", stateManager.GetStateDirectory(), currentState.GlobalVersion, withoutPatch)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package subcommands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
argparse "v/internal/argparse"
|
|
||||||
stateManager "v/internal/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Which(args []string, flags argparse.Flags, currentState stateManager.State) error {
|
|
||||||
fmt.Println(currentState.GlobalVersion)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func StartFmtGroup(label string) func(string) {
|
|
||||||
fmt.Printf("\033[1m%s\033[0m\n", label)
|
|
||||||
|
|
||||||
return func(message string) {
|
|
||||||
fmt.Printf(" %s\n", message)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +1,3 @@
|
||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
|
||||||
go build cmd/v.go
|
go build .
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package state
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
|
@ -1,13 +1,28 @@
|
||||||
package subcommands
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func validateVersion(version string) error {
|
func StartFmtGroup(label string) func(string) {
|
||||||
|
fmt.Printf("\033[1m%s\033[0m\n", label)
|
||||||
|
|
||||||
|
return func(message string) {
|
||||||
|
fmt.Printf(" %s\n", message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func VersionStringToStruct(version string) VersionTag {
|
||||||
|
splitVersion := strings.Split(version, ".")
|
||||||
|
|
||||||
|
return VersionTag{Major: splitVersion[0], Minor: splitVersion[1], Patch: splitVersion[2]}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateVersion(version string) error {
|
||||||
if splitVersion := strings.Split(version, "."); len(splitVersion) != 3 {
|
if splitVersion := strings.Split(version, "."); len(splitVersion) != 3 {
|
||||||
return errors.New("Invalid version string. Expected format 'a.b.c'.")
|
return errors.New("Invalid version string. Expected format 'a.b.c'.")
|
||||||
}
|
}
|
42
v.go
Normal file
42
v.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Version = "0.0.1"
|
||||||
|
Author = "mcataford <hello@karnov.club>"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main entrypoint.
|
||||||
|
func main() {
|
||||||
|
args := os.Args[1:]
|
||||||
|
currentState := ReadState()
|
||||||
|
|
||||||
|
cli := CLI{
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"Version": Version,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cli.AddCommand(
|
||||||
|
"install", InstallPython, "v install <version>", "Downloads, builds and installs a new version of Python.",
|
||||||
|
).AddCommand(
|
||||||
|
"uninstall", UninstallPython, "v uninstall <version>", "Uninstalls the given Python version.",
|
||||||
|
).AddCommand(
|
||||||
|
"use", Use, "v use <version>", "Selects which Python version to use.",
|
||||||
|
).AddCommand(
|
||||||
|
"ls", ListVersions, "v ls", "Lists the installed Python versions.",
|
||||||
|
).AddCommand(
|
||||||
|
"where", Where, "v where", "Prints the path to the current Python version.",
|
||||||
|
).AddCommand(
|
||||||
|
"which", Which, "v which", "Prints the current Python version.",
|
||||||
|
).AddCommand(
|
||||||
|
"init", Initialize, "v init", "Initializes the v state.",
|
||||||
|
).Run(args, currentState)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue