Skip to content

'arduino-cli lib' command arguments are now case insensitive #628

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

Merged
merged 5 commits into from
Apr 8, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
48 changes: 0 additions & 48 deletions cli/globals/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,51 +86,3 @@ func ParseReferenceArg(arg string, parseArch bool) (*ReferenceArg, error) {

return ret, nil
}

// LibraryReferenceArg is a command line argument that reference a library.
type LibraryReferenceArg struct {
Name string
Version string
}

func (r *LibraryReferenceArg) String() string {
if r.Version != "" {
return r.Name + "@" + r.Version
}
return r.Name
}

// ParseLibraryReferenceArg parse a command line argument that reference a
// library in the form "LibName@Version" or just "LibName".
func ParseLibraryReferenceArg(arg string) (*LibraryReferenceArg, error) {
tokens := strings.SplitN(arg, "@", 2)

ret := &LibraryReferenceArg{}
// TODO: check library Name constraints
// TODO: check library Version constraints
if tokens[0] == "" {
return nil, fmt.Errorf("invalid empty library name")
}
ret.Name = tokens[0]
if len(tokens) > 1 {
if tokens[1] == "" {
return nil, fmt.Errorf("invalid empty library version: %s", arg)
}
ret.Version = tokens[1]
}
return ret, nil
}

// ParseLibraryReferenceArgs is a convenient wrapper that operates on a slice of strings and
// calls ParseLibraryReferenceArg for each of them. It returns at the first invalid argument.
func ParseLibraryReferenceArgs(args []string) ([]*LibraryReferenceArg, error) {
ret := []*LibraryReferenceArg{}
for _, arg := range args {
if reference, err := ParseLibraryReferenceArg(arg); err == nil {
ret = append(ret, reference)
} else {
return nil, err
}
}
return ret, nil
}
45 changes: 0 additions & 45 deletions cli/globals/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,6 @@ var goodCores = []struct {
{"arduino:[email protected]", &globals.ReferenceArg{"arduino", "avr", "1.6.20"}},
}

var goodLibs = []struct {
in string
expected *globals.LibraryReferenceArg
}{
{"mylib", &globals.LibraryReferenceArg{"mylib", ""}},
{"[email protected]", &globals.LibraryReferenceArg{"mylib", "1.0"}},
}

var badCores = []struct {
in string
expected *globals.ReferenceArg
Expand All @@ -53,18 +45,7 @@ var badCores = []struct {
{"", nil},
}

var badLibs = []struct {
in string
expected *globals.LibraryReferenceArg
}{
{"", nil},
{"mylib@", nil},
}

func TestArgsStringify(t *testing.T) {
for _, lib := range goodLibs {
require.Equal(t, lib.in, lib.expected.String())
}
for _, core := range goodCores {
require.Equal(t, core.in, core.expected.String())
}
Expand All @@ -84,32 +65,6 @@ func TestParseReferenceArgCores(t *testing.T) {
}
}

func TestParseReferenceArgLibs(t *testing.T) {
for _, tt := range goodLibs {
actual, err := globals.ParseLibraryReferenceArg(tt.in)
assert.Nil(t, err, "Testing good arg '%s'", tt.in)
assert.Equal(t, tt.expected, actual, "Testing good arg '%s'", tt.in)
}
for _, tt := range badLibs {
res, err := globals.ParseLibraryReferenceArg(tt.in)
require.Nil(t, res, "Testing bad arg '%s'", tt.in)
require.NotNil(t, err, "Testing bad arg '%s'", tt.in)
}
}

func TestParseLibraryReferenceArgs(t *testing.T) {
args := []string{}
for _, tt := range goodLibs {
args = append(args, tt.in)
}
refs, err := globals.ParseLibraryReferenceArgs(args)
require.Nil(t, err)
require.Len(t, refs, len(goodLibs))
for i, tt := range goodLibs {
assert.Equal(t, tt.expected, refs[i])
}
}

func TestParseArgs(t *testing.T) {
input := []string{}
for _, tt := range goodCores {
Expand Down
111 changes: 111 additions & 0 deletions cli/lib/args.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].

package lib

import (
"context"
"fmt"
"strings"

"github.com/arduino/arduino-cli/commands/lib"
rpc "github.com/arduino/arduino-cli/rpc/commands"
)

// LibraryReferenceArg is a command line argument that reference a library.
type LibraryReferenceArg struct {
Name string
Version string
}

func (r *LibraryReferenceArg) String() string {
if r.Version != "" {
return r.Name + "@" + r.Version
}
return r.Name
}

// ParseLibraryReferenceArg parse a command line argument that reference a
// library in the form "LibName@Version" or just "LibName".
func ParseLibraryReferenceArg(arg string) (*LibraryReferenceArg, error) {
tokens := strings.SplitN(arg, "@", 2)

ret := &LibraryReferenceArg{}
// TODO: check library Name constraints
// TODO: check library Version constraints
if tokens[0] == "" {
return nil, fmt.Errorf("invalid empty library name")
}
ret.Name = tokens[0]
if len(tokens) > 1 {
if tokens[1] == "" {
return nil, fmt.Errorf("invalid empty library version: %s", arg)
}
ret.Version = tokens[1]
}
return ret, nil
}

// ParseLibraryReferenceArgs is a convenient wrapper that operates on a slice of strings and
// calls ParseLibraryReferenceArg for each of them. It returns at the first invalid argument.
func ParseLibraryReferenceArgs(args []string) ([]*LibraryReferenceArg, error) {
ret := []*LibraryReferenceArg{}
for _, arg := range args {
if reference, err := ParseLibraryReferenceArg(arg); err == nil {
ret = append(ret, reference)
} else {
return nil, err
}
}
return ret, nil
}

// ParseLibraryReferenceArgAndAdjustCase parse a command line argument that reference a
// library and possibly adjust the case of the name to match a library in the index
func ParseLibraryReferenceArgAndAdjustCase(instance *rpc.Instance, arg string) (*LibraryReferenceArg, error) {
libRef, err := ParseLibraryReferenceArg(arg)
res, err := lib.LibrarySearch(context.Background(), &rpc.LibrarySearchReq{
Instance: instance,
Query: libRef.Name,
})
if err != nil {
return nil, err
}

candidates := []*rpc.SearchedLibrary{}
for _, foundLib := range res.GetLibraries() {
if strings.ToLower(foundLib.GetName()) == strings.ToLower(libRef.Name) {
candidates = append(candidates, foundLib)
}
}
if len(candidates) == 1 {
libRef.Name = candidates[0].GetName()
}
return libRef, nil
}

// ParseLibraryReferenceArgsAndAdjustCase is a convenient wrapper that operates on a slice of
// strings and calls ParseLibraryReferenceArgAndAdjustCase for each of them. It returns at the first invalid argument.
func ParseLibraryReferenceArgsAndAdjustCase(instance *rpc.Instance, args []string) ([]*LibraryReferenceArg, error) {
ret := []*LibraryReferenceArg{}
for _, arg := range args {
if reference, err := ParseLibraryReferenceArgAndAdjustCase(instance, arg); err == nil {
ret = append(ret, reference)
} else {
return nil, err
}
}
return ret, nil
}
71 changes: 71 additions & 0 deletions cli/lib/args_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].

package lib

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var goodLibs = []struct {
in string
expected *LibraryReferenceArg
}{
{"mylib", &LibraryReferenceArg{"mylib", ""}},
{"[email protected]", &LibraryReferenceArg{"mylib", "1.0"}},
}

var badLibs = []struct {
in string
expected *LibraryReferenceArg
}{
{"", nil},
{"mylib@", nil},
}

func TestArgsStringify(t *testing.T) {
for _, lib := range goodLibs {
require.Equal(t, lib.in, lib.expected.String())
}
}

func TestParseReferenceArgLibs(t *testing.T) {
for _, tt := range goodLibs {
actual, err := ParseLibraryReferenceArg(tt.in)
assert.Nil(t, err, "Testing good arg '%s'", tt.in)
assert.Equal(t, tt.expected, actual, "Testing good arg '%s'", tt.in)
}
for _, tt := range badLibs {
res, err := ParseLibraryReferenceArg(tt.in)
require.Nil(t, res, "Testing bad arg '%s'", tt.in)
require.NotNil(t, err, "Testing bad arg '%s'", tt.in)
}
}

func TestParseLibraryReferenceArgs(t *testing.T) {
args := []string{}
for _, tt := range goodLibs {
args = append(args, tt.in)
}
refs, err := ParseLibraryReferenceArgs(args)
require.Nil(t, err)
require.Len(t, refs, len(goodLibs))
for i, tt := range goodLibs {
assert.Equal(t, tt.expected, refs[i])
}
}
5 changes: 2 additions & 3 deletions cli/lib/check_deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (

"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/cli/feedback"
"github.com/arduino/arduino-cli/cli/globals"
"github.com/arduino/arduino-cli/cli/instance"
"github.com/arduino/arduino-cli/commands/lib"
rpc "github.com/arduino/arduino-cli/rpc/commands"
Expand All @@ -45,13 +44,13 @@ func initDepsCommand() *cobra.Command {
}

func runDepsCommand(cmd *cobra.Command, args []string) {
libRef, err := globals.ParseLibraryReferenceArg(args[0])
instance := instance.CreateInstanceIgnorePlatformIndexErrors()
libRef, err := ParseLibraryReferenceArgAndAdjustCase(instance, args[0])
if err != nil {
feedback.Errorf("Arguments error: %v", err)
os.Exit(errorcodes.ErrBadArgument)
}

instance := instance.CreateInstanceIgnorePlatformIndexErrors()
deps, err := lib.LibraryResolveDependencies(context.Background(), &rpc.LibraryResolveDependenciesReq{
Instance: instance,
Name: libRef.Name,
Expand Down
2 changes: 1 addition & 1 deletion cli/lib/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func initDownloadCommand() *cobra.Command {

func runDownloadCommand(cmd *cobra.Command, args []string) {
instance := instance.CreateInstanceIgnorePlatformIndexErrors()
refs, err := globals.ParseLibraryReferenceArgs(args)
refs, err := ParseLibraryReferenceArgsAndAdjustCase(instance, args)
if err != nil {
feedback.Errorf("Invalid argument passed: %v", err)
os.Exit(errorcodes.ErrBadArgument)
Expand Down
2 changes: 1 addition & 1 deletion cli/lib/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ var installFlags struct {

func runInstallCommand(cmd *cobra.Command, args []string) {
instance := instance.CreateInstanceIgnorePlatformIndexErrors()
libRefs, err := globals.ParseLibraryReferenceArgs(args)
libRefs, err := ParseLibraryReferenceArgsAndAdjustCase(instance, args)
if err != nil {
feedback.Errorf("Arguments error: %v", err)
os.Exit(errorcodes.ErrBadArgument)
Expand Down
3 changes: 1 addition & 2 deletions cli/lib/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (

"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/cli/feedback"
"github.com/arduino/arduino-cli/cli/globals"
"github.com/arduino/arduino-cli/cli/instance"
"github.com/arduino/arduino-cli/cli/output"
"github.com/arduino/arduino-cli/commands/lib"
Expand All @@ -46,7 +45,7 @@ func runUninstallCommand(cmd *cobra.Command, args []string) {
logrus.Info("Executing `arduino lib uninstall`")

instance := instance.CreateInstanceIgnorePlatformIndexErrors()
refs, err := globals.ParseLibraryReferenceArgs(args)
refs, err := ParseLibraryReferenceArgsAndAdjustCase(instance, args)
if err != nil {
feedback.Errorf("Invalid argument passed: %v", err)
os.Exit(errorcodes.ErrBadArgument)
Expand Down
22 changes: 22 additions & 0 deletions test/test_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,28 @@ def test_uninstall_spaces(run_command):
assert result.ok
assert len(json.loads(result.stdout)) == 0

def test_lib_ops_caseinsensitive(run_command):
"""
This test is supposed to (un)install the following library,
As you can see the name is all caps:

Name: "PCM"
Author: David Mellis <[email protected]>, Michael Smith <[email protected]>
Maintainer: David Mellis <[email protected]>
Sentence: Playback of short audio samples.
Paragraph: These samples are encoded directly in the Arduino sketch as an array of numbers.
Website: http://highlowtech.org/?p=1963
Category: Signal Input/Output
Architecture: avr
Types: Contributed
Versions: [1.0.0]
"""
key = 'pcm'
assert run_command("lib install {}".format(key))
assert run_command("lib uninstall {}".format(key))
result = run_command("lib list --format json")
assert result.ok
assert len(json.loads(result.stdout)) == 0

def test_search(run_command):
assert run_command("lib update-index")
Expand Down