func (b *BasicPluginManager) IsGitRepoInstalled(ctx context.Context, uri string, commitId string) (bool, string, error) {
	codePath, err := PathForGitReposCode(b.storageDir, uri, commitId)
	if err != nil {
		return false, "", err
	}
	exists, err := util.IsDirExists(codePath)
	return exists, PathForGitRepos(b.storageDir, commitId), err
}
IsGitRepoInstalled is used to check whether the protoc is installed
package pluginmanager
import (
	"context"
	"errors"
	"io/fs"
	"os"
	"strings"
	"sync"
	"github.com/storyicon/powerproto/pkg/consts"
	"github.com/storyicon/powerproto/pkg/util"
	"github.com/storyicon/powerproto/pkg/util/logger"
)
type PluginManager interface {
	GetPluginLatestVersion(ctx context.Context, path string) (string, error)
	ListPluginVersions(ctx context.Context, path string) ([]string, error)
	IsPluginInstalled(ctx context.Context, path string, version string) (bool, string, error)
	InstallPlugin(ctx context.Context, path string, version string) (local string, err error)
	GetPathForPlugin(ctx context.Context, path string, version string) (local string, err error)
	GetGitRepoLatestVersion(ctx context.Context, uri string) (string, error)
	InstallGitRepo(ctx context.Context, uri string, commitId string) (local string, err error)
	IsGitRepoInstalled(ctx context.Context, uri string, commitId string) (bool, string, error)
	GitRepoPath(ctx context.Context, commitId string) (string, error)
	GetProtocLatestVersion(ctx context.Context) (string, error)
	ListProtocVersions(ctx context.Context) ([]string, error)
	IsProtocInstalled(ctx context.Context, version string) (bool, string, error)
	InstallProtoc(ctx context.Context, version string) (local string, err error)
	IncludePath(ctx context.Context) (string, error)
	GetPathForProtoc(ctx context.Context, version string) (string, error)
}
type Config struct {
	StorageDir string `json:"storage"`
}
func NewConfig() *Config {
	return &Config{
		StorageDir: consts.GetHomeDir(),
	}
}
func NewPluginManager(cfg *Config, log logger.Logger) (PluginManager, error) {
	return NewBasicPluginManager(cfg.StorageDir, log)
}
type BasicPluginManager struct {
	logger.Logger
	storageDir   string
	versions     map[string][]string
	versionsLock sync.RWMutex
}
func NewBasicPluginManager(storageDir string, log logger.Logger) (*BasicPluginManager, error) {
	return &BasicPluginManager{
		Logger:     log.NewLogger("pluginmanager"),
		storageDir: storageDir,
		versions:   map[string][]string{},
	}, nil
}
func (b *BasicPluginManager) GetPluginLatestVersion(ctx context.Context, path string) (string, error) {
	ctx, cancel := consts.GetContextWithPerCommandTimeout(ctx)
	defer cancel()
	versions, err := b.ListPluginVersions(ctx, path)
	if err != nil {
		return "", err
	}
	if len(versions) == 0 {
		return "", errors.New("no version list")
	}
	return versions[len(versions)-1], nil
}
func (b *BasicPluginManager) ListPluginVersions(ctx context.Context, path string) ([]string, error) {
	ctx, cancel := consts.GetContextWithPerCommandTimeout(ctx)
	defer cancel()
	b.versionsLock.RLock()
	versions, ok := b.versions[path]
	b.versionsLock.RUnlock()
	if ok {
		return versions, nil
	}
	versions, err := ListsGoPackageVersionsAmbiguously(ctx, b.Logger, path)
	if err != nil {
		return nil, err
	}
	b.versionsLock.Lock()
	b.versions[path] = versions
	b.versionsLock.Unlock()
	return versions, nil
}
func (b *BasicPluginManager) IsPluginInstalled(ctx context.Context, path string, version string) (bool, string, error) {
	return IsPluginInstalled(ctx, b.storageDir, path, version)
}
func (b *BasicPluginManager) GetPathForPlugin(ctx context.Context, path string, version string) (local string, err error) {
	return PathForPlugin(b.storageDir, path, version)
}
func (b *BasicPluginManager) InstallPlugin(ctx context.Context, path string, version string) (local string, err error) {
	return InstallPluginUsingGo(ctx, b.Logger, b.storageDir, path, version)
}
func (b *BasicPluginManager) GetGitRepoLatestVersion(ctx context.Context, url string) (string, error) {
	return GetGitLatestCommitId(ctx, b.Logger, url)
}
func (b *BasicPluginManager) InstallGitRepo(ctx context.Context, uri string, commitId string) (string, error) {
	ctx, cancel := consts.GetContextWithPerCommandTimeout(ctx)
	defer cancel()
	exists, local, err := b.IsGitRepoInstalled(ctx, uri, commitId)
	if err != nil {
		return "", err
	}
	if exists {
		return local, nil
	}
	release, err := GetGithubArchive(ctx, uri, commitId)
	if err != nil {
		return "", err
	}
	defer release.Clear()
	codePath, err := PathForGitReposCode(b.storageDir, uri, commitId)
	if err != nil {
		return "", err
	}
	if err := util.CopyDirectory(release.GetLocalDir(), codePath); err != nil {
		return "", err
	}
	return local, nil
}