mirror of
https://github.com/MichaelMure/git-bug.git
synced 2024-12-17 13:10:17 +03:00
159 lines
3.6 KiB
Go
159 lines
3.6 KiB
Go
package _select
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/MichaelMure/git-bug/cache"
|
|
"github.com/MichaelMure/git-bug/entity"
|
|
)
|
|
|
|
type ErrNoValidId struct {
|
|
typename string
|
|
}
|
|
|
|
func NewErrNoValidId(typename string) *ErrNoValidId {
|
|
return &ErrNoValidId{typename: typename}
|
|
}
|
|
|
|
func (e ErrNoValidId) Error() string {
|
|
return fmt.Sprintf("you must provide a %s id or use the \"select\" command first", e.typename)
|
|
}
|
|
|
|
func IsErrNoValidId(err error) bool {
|
|
_, ok := err.(*ErrNoValidId)
|
|
return ok
|
|
}
|
|
|
|
type Resolver[CacheT cache.CacheEntity] interface {
|
|
Resolve(id entity.Id) (CacheT, error)
|
|
ResolvePrefix(prefix string) (CacheT, error)
|
|
}
|
|
|
|
// Resolve first try to resolve an entity using the first argument of the command
|
|
// line. If it fails, it falls back to the select mechanism.
|
|
//
|
|
// Returns:
|
|
// - the entity if any
|
|
// - the new list of command line arguments with the entity prefix removed if it
|
|
// has been used
|
|
// - an error if the process failed
|
|
func Resolve[CacheT cache.CacheEntity](repo *cache.RepoCache,
|
|
typename string, namespace string, resolver Resolver[CacheT],
|
|
args []string) (CacheT, []string, error) {
|
|
// At first, try to use the first argument as an entity prefix
|
|
if len(args) > 0 {
|
|
cached, err := resolver.ResolvePrefix(args[0])
|
|
|
|
if err == nil {
|
|
return cached, args[1:], nil
|
|
}
|
|
|
|
if !entity.IsErrNotFound(err) {
|
|
return *new(CacheT), nil, err
|
|
}
|
|
}
|
|
|
|
// first arg is not a valid entity prefix, we can safely use the preselected entity if any
|
|
|
|
cached, err := selected(repo, resolver, namespace)
|
|
|
|
// selected entity is invalid
|
|
if entity.IsErrNotFound(err) {
|
|
// we clear the selected bug
|
|
err = Clear(repo, namespace)
|
|
if err != nil {
|
|
return *new(CacheT), nil, err
|
|
}
|
|
return *new(CacheT), nil, NewErrNoValidId(typename)
|
|
}
|
|
|
|
// another error when reading the entity
|
|
if err != nil {
|
|
return *new(CacheT), nil, err
|
|
}
|
|
|
|
// entity is successfully retrieved
|
|
if cached != nil {
|
|
return *cached, args, nil
|
|
}
|
|
|
|
// no selected bug and no valid first argument
|
|
return *new(CacheT), nil, NewErrNoValidId(typename)
|
|
}
|
|
|
|
func selectFileName(namespace string) string {
|
|
return filepath.Join("select", namespace)
|
|
}
|
|
|
|
// Select will select a bug for future use
|
|
func Select(repo *cache.RepoCache, namespace string, id entity.Id) error {
|
|
filename := selectFileName(namespace)
|
|
f, err := repo.LocalStorage().OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = f.Write([]byte(id.String()))
|
|
if err != nil {
|
|
_ = f.Close()
|
|
return err
|
|
}
|
|
|
|
return f.Close()
|
|
}
|
|
|
|
// Clear will clear the selected entity, if any
|
|
func Clear(repo *cache.RepoCache, namespace string) error {
|
|
filename := selectFileName(namespace)
|
|
return repo.LocalStorage().Remove(filename)
|
|
}
|
|
|
|
func selected[CacheT cache.CacheEntity](repo *cache.RepoCache, resolver Resolver[CacheT], namespace string) (*CacheT, error) {
|
|
filename := selectFileName(namespace)
|
|
f, err := repo.LocalStorage().Open(filename)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, nil
|
|
} else {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
buf, err := io.ReadAll(io.LimitReader(f, 100))
|
|
if err != nil {
|
|
_ = f.Close()
|
|
return nil, err
|
|
}
|
|
|
|
err = f.Close()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(buf) >= 100 {
|
|
return nil, fmt.Errorf("the select file should be < 100 bytes")
|
|
}
|
|
|
|
id := entity.Id(buf)
|
|
if err := id.Validate(); err != nil {
|
|
err = repo.LocalStorage().Remove(filename)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error while removing invalid select file")
|
|
}
|
|
|
|
return nil, fmt.Errorf("select file in invalid, removing it")
|
|
}
|
|
|
|
cached, err := resolver.Resolve(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &cached, nil
|
|
}
|