Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support parse legacy files #188

Merged
merged 7 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions cmd/commands/activate.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,27 @@ func activateCmd(ctx *cli.Context) error {
}
manager := internal.NewSdkManager()
defer manager.Close()
tvs, err := toolset.NewMultiToolVersions([]string{
manager.PathMeta.HomePath,
manager.PathMeta.WorkingDirectory,
})

workToolVersion, err := toolset.NewToolVersion(manager.PathMeta.WorkingDirectory)
if err != nil {
return err
}

if err = manager.ParseLegacyFile(func(sdkname, version string) {
if _, ok := workToolVersion.Record[sdkname]; !ok {
workToolVersion.Record[sdkname] = version
}
}); err != nil {
return err
}
homeToolVersion, err := toolset.NewToolVersion(manager.PathMeta.HomePath)
if err != nil {
return err
}
envKeys, err := manager.EnvKeys(tvs)
envKeys, err := manager.EnvKeys(toolset.MultiToolVersions{
workToolVersion,
homeToolVersion,
})
if err != nil {
return err
}
Expand Down
213 changes: 120 additions & 93 deletions cmd/commands/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,134 +19,161 @@ package commands
import (
"encoding/json"
"fmt"
"github.com/version-fox/vfox/internal/toolset"

"github.com/urfave/cli/v2"
"github.com/version-fox/vfox/internal"
"github.com/version-fox/vfox/internal/env"
"github.com/version-fox/vfox/internal/shell"
"github.com/version-fox/vfox/internal/toolset"
)

var Env = &cli.Command{
Name: "env",
Hidden: true,
Name: "env",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "shell",
Aliases: []string{"s"},
Usage: "shell",
Usage: "shell name",
},
&cli.BoolFlag{
Name: "cleanup",
Aliases: []string{"c"},
Usage: "cleanup temp file",
Usage: "cleanup old temp files",
},
&cli.BoolFlag{
Name: "json",
Aliases: []string{"j"},
Usage: "get envs as json",
Usage: "output json format",
},
},
Action: envCmd,
}

func envCmd(ctx *cli.Context) error {
if ctx.IsSet("json") {
type SDKs map[string]map[string]*string
data := struct {
IsHookEnv bool `json:"is_hook_env"`
Paths []string `json:"paths"`
SDKs SDKs `json:"sdks"`
}{
IsHookEnv: env.IsHookEnv(),
Paths: []string{},
SDKs: make(SDKs),
}
manager := internal.NewSdkManager()
defer manager.Close()
tvs, err := toolset.NewMultiToolVersions([]string{
manager.PathMeta.WorkingDirectory,
manager.PathMeta.CurTmpPath,
manager.PathMeta.HomePath,
})
if err != nil {
return err
}
tvs.FilterTools(func(name, version string) bool {
if lookupSdk, err := manager.LookupSdk(name); err == nil {
if keys, err := lookupSdk.EnvKeys(internal.Version(version)); err == nil {
data.SDKs[lookupSdk.Plugin.Name] = keys.Variables
data.Paths = append(data.Paths, keys.Paths.Slice()...)
return true
}
}
return false
})
jsonData, err := json.Marshal(data)
if err != nil {
return err
}
fmt.Println(string(jsonData))
return nil
return outputJSON()
} else if ctx.IsSet("cleanup") {
manager := internal.NewSdkManager()
defer manager.Close()
// Clean up the old temp files, before today.
manager.CleanTmp()
return nil
return cleanTmp()
} else {
shellName := ctx.String("shell")
if shellName == "" {
return cli.Exit("shell name is required", 1)
}
s := shell.NewShell(shellName)
if s == nil {
return fmt.Errorf("unknow target shell %s", shellName)
}
manager := internal.NewSdkManager()
defer manager.Close()
return envFlag(ctx)
}
}

tvs, err := toolset.NewMultiToolVersions([]string{
manager.PathMeta.WorkingDirectory,
manager.PathMeta.CurTmpPath,
manager.PathMeta.HomePath,
})
if err != nil {
return err
func outputJSON() error {
type SDKs map[string]map[string]*string
data := struct {
IsHookEnv bool `json:"is_hook_env"`
Paths []string `json:"paths"`
SDKs SDKs `json:"sdks"`
}{
IsHookEnv: env.IsHookEnv(),
Paths: []string{},
SDKs: make(SDKs),
}
manager := internal.NewSdkManager()
defer manager.Close()
tvs, err := toolset.NewMultiToolVersions([]string{
manager.PathMeta.WorkingDirectory,
manager.PathMeta.CurTmpPath,
manager.PathMeta.HomePath,
})
if err != nil {
return err
}
tvs.FilterTools(func(name, version string) bool {
if lookupSdk, err := manager.LookupSdk(name); err == nil {
if keys, err := lookupSdk.EnvKeys(internal.Version(version)); err == nil {
data.SDKs[lookupSdk.Plugin.Name] = keys.Variables
data.Paths = append(data.Paths, keys.Paths.Slice()...)
return true
}
}
return false
})
jsonData, err := json.Marshal(data)
if err != nil {
return err
}
fmt.Println(string(jsonData))
return nil
}

envKeys, err := manager.EnvKeys(tvs)
if err != nil {
return err
}
func cleanTmp() error {
manager := internal.NewSdkManager()
defer manager.Close()
// Clean up the old temp files, before today.
manager.CleanTmp()
return nil
}

exportEnvs := make(env.Vars)
for k, v := range envKeys.Variables {
exportEnvs[k] = v
}
sdkPaths := envKeys.Paths
func envFlag(ctx *cli.Context) error {
shellName := ctx.String("shell")
if shellName == "" {
return cli.Exit("shell name is required", 1)
}
s := shell.NewShell(shellName)
if s == nil {
return fmt.Errorf("unknow target shell %s", shellName)
}
manager := internal.NewSdkManager()
defer manager.Close()

// Takes the complement of previousPaths and sdkPaths, and removes the complement from osPaths.
previousPaths := env.NewPaths(env.PreviousPaths)
for _, pp := range previousPaths.Slice() {
if sdkPaths.Contains(pp) {
previousPaths.Remove(pp)
}
envKeys, err := aggregateEnvKeys(manager)
if err != nil {
return err
}

exportEnvs := make(env.Vars)
for k, v := range envKeys.Variables {
exportEnvs[k] = v
}
sdkPaths := envKeys.Paths

// Takes the complement of previousPaths and sdkPaths, and removes the complement from osPaths.
previousPaths := env.NewPaths(env.PreviousPaths)
for _, pp := range previousPaths.Slice() {
if sdkPaths.Contains(pp) {
previousPaths.Remove(pp)
}
osPaths := env.NewPaths(env.OsPaths)
if previousPaths.Len() != 0 {
for _, pp := range previousPaths.Slice() {
osPaths.Remove(pp)
}
}
osPaths := env.NewPaths(env.OsPaths)
if previousPaths.Len() != 0 {
for _, pp := range previousPaths.Slice() {
osPaths.Remove(pp)
}
// Set the sdk paths to the new previous paths.
newPreviousPathStr := sdkPaths.String()
exportEnvs[env.PreviousPathsFlag] = &newPreviousPathStr
}
// Set the sdk paths to the new previous paths.
newPreviousPathStr := sdkPaths.String()
exportEnvs[env.PreviousPathsFlag] = &newPreviousPathStr

pathStr := sdkPaths.Merge(osPaths).String()
exportEnvs["PATH"] = &pathStr
exportStr := s.Export(exportEnvs)
fmt.Println(exportStr)
return nil
pathStr := sdkPaths.Merge(osPaths).String()
exportEnvs["PATH"] = &pathStr
exportStr := s.Export(exportEnvs)
fmt.Println(exportStr)
return nil
}

func aggregateEnvKeys(manager *internal.Manager) (*env.Envs, error) {
workToolVersion, err := toolset.NewToolVersion(manager.PathMeta.WorkingDirectory)
if err != nil {
return nil, err
}

if err = manager.ParseLegacyFile(func(sdkname, version string) {
if _, ok := workToolVersion.Record[sdkname]; !ok {
workToolVersion.Record[sdkname] = version
}
}); err != nil {
return nil, err
}
tvs, err := toolset.NewMultiToolVersions([]string{
manager.PathMeta.CurTmpPath,
manager.PathMeta.HomePath,
})
if err != nil {
return nil, err
}
// Add the working directory to the first
tvs = append(toolset.MultiToolVersions{workToolVersion}, tvs...)

return manager.EnvKeys(tvs)
}
12 changes: 12 additions & 0 deletions internal/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package internal

import (
"fmt"
lua "github.com/yuin/gopher-lua"
)

type LuaCheckSum struct {
Expand Down Expand Up @@ -154,6 +155,16 @@ type EnvKeysHookResultItem struct {
Value string `luai:"value"`
}

type ParseLegacyFileHookCtx struct {
Filepath string `luai:"filepath"`
Filename string `luai:"filename"`
GetInstalledVersions lua.LGFunction `luai:"getInstalledVersions"`
}

type ParseLegacyFileResult struct {
Version string `luai:"version"`
}

type LuaPluginInfo struct {
Name string `luai:"name"`
Version string `luai:"version"`
Expand All @@ -164,6 +175,7 @@ type LuaPluginInfo struct {
License string `luai:"license"`
MinRuntimeVersion string `luai:"minRuntimeVersion"`
Notes []string `luai:"notes"`
LegacyFilenames []string `luai:"legacyFilenames"`
}

// LuaRuntime represents the runtime information of the Lua environment.
Expand Down
13 changes: 12 additions & 1 deletion internal/luai/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@ func Marshal(state *lua.LState, v any) (lua.LValue, error) {
if err != nil {
return nil, err
}
if lf, ok := sub.(*lua.LFunction); ok {
state.SetField(table, tag, lf)
} else {
table.RawSetString(tag, sub)
}

table.RawSetString(tag, sub)
}
return table, nil
case reflect.String:
Expand Down Expand Up @@ -111,6 +115,13 @@ func Marshal(state *lua.LState, v any) (lua.LValue, error) {

}
return table, nil
case reflect.Func:
if reflected.Type().ConvertibleTo(reflect.TypeOf(lua.LGFunction(nil))) {
lf := reflected.Convert(reflect.TypeOf(lua.LGFunction(nil))).Interface().(lua.LGFunction)
return state.NewFunction(lf), nil
} else {
return nil, errors.New("marshal: unsupported function type " + reflected.Type().String())
}
default:
return nil, errors.New("marshal: unsupported type " + reflected.Kind().String() + " for reflected ")
}
Expand Down
36 changes: 36 additions & 0 deletions internal/luai/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,39 @@ func TestCases(t *testing.T) {
})
}
}

func TestEncodeFunc(t *testing.T) {

teardownSuite := setupSuite(t)
defer teardownSuite(t)

t.Run("EncodeFunc", func(t *testing.T) {
testdata := struct {
Func1 func(*lua.LState) int
Func2 func(*lua.LState) int `luai:"f2"`
}{
Func1: lua.LGFunction(func(L *lua.LState) int {
L.Push(lua.LString("hello, world"))
return 1
}),
Func2: lua.LGFunction(func(L *lua.LState) int {
L.Push(lua.LString("good"))
return 1
}),
}
L := NewLuaVM()
defer L.Close()

table, err := Marshal(L.Instance, testdata)
if err != nil {
t.Fatalf("marshal map failed: %v", err)
}
L.Instance.SetGlobal("m", table)
if err := L.Instance.DoString(`
assert(m:Func1() == "hello, world")
assert(m:f2() == "good")
`); err != nil {
t.Errorf("map test failed: %v", err)
}
})
}
Loading
Loading