-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from techiecaro/cli-completion-local
Cli completion for local and S3
- Loading branch information
Showing
11 changed files
with
829 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package cli | ||
|
||
import ( | ||
"net/url" | ||
"techiecaro/remblob/core" | ||
|
||
"github.com/willabides/kongplete" | ||
) | ||
|
||
type editCmd struct { | ||
SourcePath *url.URL `arg:"" name:"source_path" help:"Location of the file to edit." predictor:"path"` | ||
DestinationPath *url.URL `arg:"" name:"destination_path" optional:"" help:"Final location of the edited file, if different." predictor:"path"` | ||
} | ||
|
||
func (e editCmd) GetDestinationPath() *url.URL { | ||
if e.DestinationPath != nil { | ||
return e.DestinationPath | ||
} | ||
return e.SourcePath | ||
} | ||
|
||
func (e editCmd) Run() error { | ||
return core.Edit(*e.SourcePath, *e.GetDestinationPath()) | ||
} | ||
|
||
type viewCmd struct { | ||
SourcePath *url.URL `arg:"" name:"source_path" help:"Location of the file to view." predictor:"path"` | ||
} | ||
|
||
func (v viewCmd) Run() error { | ||
return core.View(*v.SourcePath) | ||
} | ||
|
||
var Cli struct { | ||
Edit editCmd `cmd help:"Edits a remote blob and optionally stores it elsewhere."` | ||
View viewCmd `cmd help:"Views a remote blob."` | ||
|
||
// Competion | ||
// Use hidden empty command so the Kong does not complain on prompt | ||
Hidden struct{} `cmd:"" help:"A hidden command" hidden:""` | ||
InstallCompletions kongplete.InstallCompletions `cmd:"" help:"install shell completions"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
"net/url" | ||
"os" | ||
"techiecaro/remblob/storage" | ||
|
||
"github.com/alecthomas/kong" | ||
"github.com/posener/complete" | ||
"github.com/willabides/kongplete" | ||
) | ||
|
||
func NewPathPredictor() complete.Predictor { | ||
return PathPredictor(storage.GetFileListerPrefixes()) | ||
} | ||
|
||
type PathPredictor []string | ||
|
||
func (p PathPredictor) Predict(args complete.Args) []string { | ||
|
||
pathPredictions := p.matchFileLister(args.Last) | ||
|
||
return append(p, pathPredictions...) | ||
} | ||
|
||
func (p PathPredictor) matchFileLister(pattern string) []string { | ||
if pattern == "" { | ||
return []string{} | ||
} | ||
|
||
prefixURL, err := url.Parse(pattern) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Can't parse %s\n", prefixURL) | ||
return []string{} | ||
} | ||
|
||
lister := storage.GetFileLister(*prefixURL) | ||
matchesURL := lister(*prefixURL) | ||
matches := make([]string, len(matchesURL)) | ||
for i, match := range matchesURL { | ||
matches[i] = match.String() | ||
} | ||
|
||
return matches | ||
} | ||
|
||
// AddCompletion adds cli completion to an exising Kong parer | ||
func AddCompletion(parser *kong.Kong) { | ||
// Run kongplete.Complete to handle completion requests | ||
kongplete.Complete( | ||
parser, | ||
kongplete.WithPredictor("path", NewPathPredictor()), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package cli_test | ||
|
||
import ( | ||
"os" | ||
"path" | ||
"techiecaro/remblob/cli" | ||
"testing" | ||
|
||
"github.com/posener/complete" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func createTestFileStructure(t *testing.T) string { | ||
dir := t.TempDir() | ||
|
||
var files = []string{"1.txt", "2.txt", "a/a1.txt"} | ||
|
||
for _, name := range files { | ||
fullPath := path.Join(dir, name) | ||
os.MkdirAll(path.Dir(fullPath), 0700) | ||
if _, err := os.Create(fullPath); err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
|
||
return dir | ||
} | ||
|
||
func mustGetCWD(t *testing.T) string { | ||
dir, err := os.Getwd() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
return dir | ||
} | ||
|
||
func TestPathPredictor(t *testing.T) { | ||
cases := []struct { | ||
prefix string | ||
expected []string | ||
}{ | ||
{ | ||
prefix: "", | ||
expected: []string{"./", "file://", "s3://"}, | ||
}, | ||
{ | ||
prefix: ".", | ||
expected: []string{"./", "file://", "s3://", "./1.txt", "./2.txt", "./a"}, | ||
}, | ||
{ | ||
prefix: "a/", | ||
expected: []string{"./", "file://", "s3://", "a/a1.txt"}, | ||
}, | ||
{ | ||
prefix: "./a/", | ||
expected: []string{"./", "file://", "s3://", "./a/a1.txt"}, | ||
}, | ||
{ | ||
prefix: "file://", | ||
expected: []string{"./", "file://", "s3://", "file://1.txt", "file://2.txt", "file://a"}, | ||
}, | ||
{ | ||
prefix: "file://a", | ||
expected: []string{"./", "file://", "s3://", "file://a/a1.txt"}, | ||
}, | ||
} | ||
|
||
for _, tc := range cases { | ||
t.Run(tc.prefix, func(t *testing.T) { | ||
dir := createTestFileStructure(t) | ||
|
||
cwd := mustGetCWD(t) | ||
os.Chdir(dir) | ||
defer os.Chdir(cwd) | ||
|
||
args := complete.Args{ | ||
Last: tc.prefix, | ||
All: []string{"not-in-use"}, | ||
Completed: []string{"not-in-use"}, | ||
LastCompleted: "not-in-use", | ||
} | ||
|
||
predictor := cli.NewPathPredictor() | ||
|
||
suggestions := predictor.Predict(args) | ||
|
||
assert.Equal(t, tc.expected, suggestions, "Invalid prompt") | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package storage_test | ||
|
||
import ( | ||
"techiecaro/remblob/storage" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestGetFileListerPrefixes(t *testing.T) { | ||
prefixes := storage.GetFileListerPrefixes() | ||
|
||
expected := []string{"./", "file://", "s3://"} | ||
|
||
assert.Equal(t, expected, prefixes, "Invalid prefixes") | ||
} |
Oops, something went wrong.