consolidate argument parsing

parent 5a4f31d6
......@@ -18,9 +18,6 @@
package librariesindex
import (
"fmt"
"strings"
semver "go.bug.st/relaxed-semver"
)
......@@ -36,23 +33,3 @@ func (r *Reference) String() string {
}
return r.Name + "@" + r.Version.String()
}
// ParseArgs parses a sequence of "item@version" tokens and returns a Name-Version slice.
//
// If version is not present it is assumed as "latest" version.
func ParseArgs(args []string) ([]*Reference, error) {
res := []*Reference{}
for _, item := range args {
tokens := strings.SplitN(item, "@", 2)
if len(tokens) == 2 {
version, err := semver.Parse(tokens[1])
if err != nil {
return nil, fmt.Errorf("invalid version %s: %s", version, err)
}
res = append(res, &Reference{Name: tokens[0], Version: version})
} else {
res = append(res, &Reference{Name: tokens[0]})
}
}
return res, nil
}
/*
* This file is part of arduino-cli.
*
* Copyright 2018 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 license@arduino.cc.
*/
package core
import (
"fmt"
"os"
"strings"
"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/common/formatter"
)
type platformReferenceArg struct {
Package string
Architecture string
Version string
}
func (pl *platformReferenceArg) String() string {
if pl.Version != "" {
return pl.Package + ":" + pl.Architecture + "@" + pl.Version
}
return pl.Package + ":" + pl.Architecture
}
// parsePlatformReferenceArgs parses a sequence of "packager:arch@version" tokens and
// returns a platformReferenceArg slice.
func parsePlatformReferenceArgs(args []string) []*platformReferenceArg {
ret := []*platformReferenceArg{}
for _, arg := range args {
reference, err := parsePlatformReferenceArg(arg)
if err != nil {
formatter.PrintError(err, "Invalid item "+arg)
os.Exit(errorcodes.ErrBadArgument)
}
ret = append(ret, reference)
}
return ret
}
func parsePlatformReferenceArg(arg string) (*platformReferenceArg, error) {
split := strings.SplitN(arg, "@", 2)
arg = split[0]
version := ""
if len(split) > 1 {
version = split[1]
}
split = strings.Split(arg, ":")
if len(split) != 2 {
return nil, fmt.Errorf("invalid item %s", arg)
}
return &platformReferenceArg{
Package: split[0],
Architecture: split[1],
Version: version,
}, nil
}
/*
* This file is part of arduino-cli.
*
* Copyright 2018 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 license@arduino.cc.
*/
package core
import (
"testing"
"github.com/stretchr/testify/require"
semver "go.bug.st/relaxed-semver"
)
func TestParsePlatformReferenceArgs(t *testing.T) {
valid := func(arg, pack, arch, ver string) {
version, _ := semver.Parse(ver) // use nil in case of error
ref, err := parsePlatformReferenceArg(arg)
require.NoError(t, err)
require.Equal(t, pack, ref.Package)
require.Equal(t, arch, ref.Architecture)
require.Equal(t, version.String(), ref.Version)
}
invalid := func(arg string) {
_, err := parsePlatformReferenceArg(arg)
require.Error(t, err)
}
valid("arduino:avr", "arduino", "avr", "-")
valid("arduino:avr@1.6.20", "arduino", "avr", "1.6.20")
valid("arduino:avr@", "arduino", "avr", "")
invalid("avr")
invalid("arduino:avr:avr")
invalid("arduino@1.6.20:avr")
invalid("avr@1.6.20")
invalid("arduino:avr:avr@1.6.20")
}
......@@ -50,11 +50,16 @@ func runDownloadCommand(cmd *cobra.Command, args []string) {
instance := instance.CreateInstance()
logrus.Info("Executing `arduino core download`")
platformsRefs := parsePlatformReferenceArgs(args)
platformsRefs, err := globals.ParseReferenceArgs(args, true)
if err != nil {
formatter.PrintError(err, "Invalid argument passed")
os.Exit(errorcodes.ErrBadArgument)
}
for i, platformRef := range platformsRefs {
platformDownloadreq := &rpc.PlatformDownloadReq{
Instance: instance,
PlatformPackage: platformRef.Package,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
Version: platformRef.Version,
}
......
......@@ -51,12 +51,16 @@ func runInstallCommand(cmd *cobra.Command, args []string) {
instance := instance.CreateInstance()
logrus.Info("Executing `arduino core install`")
platformsRefs := parsePlatformReferenceArgs(args)
platformsRefs, err := globals.ParseReferenceArgs(args, true)
if err != nil {
formatter.PrintError(err, "Invalid argument passed")
os.Exit(errorcodes.ErrBadArgument)
}
for _, platformRef := range platformsRefs {
plattformInstallReq := &rpc.PlatformInstallReq{
Instance: instance,
PlatformPackage: platformRef.Package,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
Version: platformRef.Version,
}
......
......@@ -22,6 +22,7 @@ import (
"os"
"github.com/arduino/arduino-cli/cli/errorcodes"
"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/core"
......@@ -46,7 +47,11 @@ func runUninstallCommand(cmd *cobra.Command, args []string) {
instance := instance.CreateInstance()
logrus.Info("Executing `arduino core uninstall`")
platformsRefs := parsePlatformReferenceArgs(args)
platformsRefs, err := globals.ParseReferenceArgs(args, true)
if err != nil {
formatter.PrintError(err, "Invalid argument passed")
os.Exit(errorcodes.ErrBadArgument)
}
for _, platformRef := range platformsRefs {
if platformRef.Version != "" {
......@@ -57,7 +62,7 @@ func runUninstallCommand(cmd *cobra.Command, args []string) {
for _, platformRef := range platformsRefs {
_, err := core.PlatformUninstall(context.Background(), &rpc.PlatformUninstallReq{
Instance: instance,
PlatformPackage: platformRef.Package,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
}, output.NewTaskProgressCB())
if err != nil {
......
......@@ -72,7 +72,12 @@ func runUpgradeCommand(cmd *cobra.Command, args []string) {
// proceed upgrading, if anything is upgradable
exitErr := false
platformsRefs := parsePlatformReferenceArgs(args)
platformsRefs, err := globals.ParseReferenceArgs(args, true)
if err != nil {
formatter.PrintError(err, "Invalid argument passed")
os.Exit(errorcodes.ErrBadArgument)
}
for i, platformRef := range platformsRefs {
if platformRef.Version != "" {
formatter.PrintErrorMessage(("Invalid item " + args[i]))
......@@ -82,7 +87,7 @@ func runUpgradeCommand(cmd *cobra.Command, args []string) {
r := &rpc.PlatformUpgradeReq{
Instance: instance,
PlatformPackage: platformRef.Package,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
}
......
// This file is part of arduino-cli.
//
// Copyright 2019 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 license@arduino.cc.
package globals
import (
"fmt"
"strings"
)
// ReferenceArg represents a reference item (core or library) passed to the CLI
// interface
type ReferenceArg struct {
PackageName string
Architecture string
Version string
}
func (r *ReferenceArg) String() string {
if r.Version != "" {
return r.PackageName + ":" + r.Architecture + "@" + r.Version
}
return r.PackageName + ":" + r.Architecture
}
// ParseReferenceArgs is a convenient wrapper that operates on a slice of strings and
// calls ParseReferenceArg for each of them. It returns at the first invalid argument.
func ParseReferenceArgs(args []string, parseArch bool) ([]*ReferenceArg, error) {
ret := []*ReferenceArg{}
for _, arg := range args {
reference, err := ParseReferenceArg(arg, parseArch)
if err != nil {
return nil, err
}
ret = append(ret, reference)
}
return ret, nil
}
// ParseReferenceArg parses a string and return a ReferenceArg object. If `parseArch` is passed,
// the method also tries to parse the architecture bit, i.e. string must be in the form
// "packager:arch@version", useful to represent a platform (or core) name.
func ParseReferenceArg(arg string, parseArch bool) (*ReferenceArg, error) {
ret := &ReferenceArg{}
toks := strings.SplitN(arg, "@", 2)
ret.PackageName = toks[0]
if len(toks) > 1 {
ret.Version = toks[1]
}
if parseArch {
toks = strings.Split(ret.PackageName, ":")
if len(toks) != 2 {
return nil, fmt.Errorf("invalid item %s", arg)
}
ret.PackageName = toks[0]
ret.Architecture = toks[1]
}
return ret, nil
}
/*
* This file is part of arduino-cli.
*
* Copyright 2018 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 license@arduino.cc.
*/
package globals_test
import (
"testing"
"github.com/arduino/arduino-cli/cli/globals"
"github.com/stretchr/testify/assert"
)
var goodCores = []struct {
in string
expected *globals.ReferenceArg
}{
{"arduino:avr", &globals.ReferenceArg{"arduino", "avr", ""}},
{"arduino:avr@1.6.20", &globals.ReferenceArg{"arduino", "avr", "1.6.20"}},
{"arduino:avr@", &globals.ReferenceArg{"arduino", "avr", ""}},
}
var goodLibs = []struct {
in string
expected *globals.ReferenceArg
}{
{"mylib", &globals.ReferenceArg{"mylib", "", ""}},
{"mylib@1.0", &globals.ReferenceArg{"mylib", "", "1.0"}},
{"mylib@", &globals.ReferenceArg{"mylib", "", ""}},
}
var badCores = []struct {
in string
expected *globals.ReferenceArg
}{
{"arduino:avr:avr", nil},
{"arduino@1.6.20:avr", nil},
{"arduino:avr:avr@1.6.20", nil},
}
func TestParseReferenceArgCores(t *testing.T) {
for _, tt := range goodCores {
actual, err := globals.ParseReferenceArg(tt.in, true)
assert.Nil(t, err)
assert.Equal(t, tt.expected, actual)
}
for _, tt := range badCores {
actual, err := globals.ParseReferenceArg(tt.in, true)
assert.NotNil(t, err)
assert.Equal(t, tt.expected, actual)
}
// library refs are not good as core's
for _, tt := range goodLibs {
_, err := globals.ParseReferenceArg(tt.in, true)
assert.NotNil(t, err)
}
}
func TestParseReferenceArgLibs(t *testing.T) {
for _, tt := range goodLibs {
actual, err := globals.ParseReferenceArg(tt.in, false)
assert.Nil(t, err)
assert.Equal(t, tt.expected, actual)
}
// good libs are bad when requiring Arch to be present
for _, tt := range goodLibs {
_, err := globals.ParseReferenceArg(tt.in, true)
assert.NotNil(t, err)
}
}
func TestParseArgs(t *testing.T) {
input := []string{}
for _, tt := range goodCores {
input = append(input, tt.in)
}
refs, err := globals.ParseReferenceArgs(input, true)
assert.Nil(t, err)
assert.Equal(t, len(goodCores), len(refs))
for i, tt := range goodCores {
assert.Equal(t, tt.expected, refs[i])
}
}
// This file is part of arduino-cli.
//
// Copyright 2019 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 license@arduino.cc.
package globals
import (
"fmt"
"os"
"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/common/formatter"
"github.com/arduino/arduino-cli/configs"
"github.com/arduino/go-paths-helper"
"github.com/sirupsen/logrus"
)
// InitConfigs initializes the configuration from the specified file.
func InitConfigs() {
// Start with default configuration
if conf, err := configs.NewConfiguration(); err != nil {
logrus.WithError(err).Error("Error creating default configuration")
formatter.PrintError(err, "Error creating default configuration")
os.Exit(errorcodes.ErrGeneric)
} else {
Config = conf
}
// Read configuration from global config file
logrus.Info("Checking for config file in: " + Config.ConfigFile.String())
if Config.ConfigFile.Exist() {
readConfigFrom(Config.ConfigFile)
}
if Config.IsBundledInDesktopIDE() {
logrus.Info("CLI is bundled into the IDE")
err := Config.LoadFromDesktopIDEPreferences()
if err != nil {
logrus.WithError(err).Warn("Did not manage to get config file of IDE, using default configuration")
}
} else {
logrus.Info("CLI is not bundled into the IDE")
}
// Read configuration from parent folders (project config)
if pwd, err := paths.Getwd(); err != nil {
logrus.WithError(err).Warn("Did not manage to find current path")
if path := paths.New("arduino-yaml"); path.Exist() {
readConfigFrom(path)
}
} else {
Config.Navigate(pwd)
}
// Read configuration from old configuration file if found, but output a warning.
if old := paths.New(".cli-config.yml"); old.Exist() {
logrus.Errorf("Old configuration file detected: %s.", old)
logrus.Info("The name of this file has been changed to `arduino-yaml`, please rename the file fix it.")
formatter.PrintError(
fmt.Errorf("WARNING: Old configuration file detected: %s", old),
"The name of this file has been changed to `arduino-yaml`, in a future release we will not support"+
"the old name `.cli-config.yml` anymore. Please rename the file to `arduino-yaml` to silence this warning.")
readConfigFrom(old)
}
// Read configuration from environment vars
Config.LoadFromEnv()
// Read configuration from user specified file
if YAMLConfigFile != "" {
Config.ConfigFile = paths.New(YAMLConfigFile)
readConfigFrom(Config.ConfigFile)
}
logrus.Info("Configuration set")
}
func readConfigFrom(path *paths.Path) {
logrus.Infof("Reading configuration from %s", path)
if err := Config.LoadFromYAML(path); err != nil {
logrus.WithError(err).Warnf("Could not read configuration from %s", path)
}
}
// This file is part of arduino-cli.
//
// Copyright 2019 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 license@arduino.cc.
package globals
import (
......@@ -7,12 +22,8 @@ import (
"path/filepath"
"runtime"
"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/common/formatter"
"github.com/arduino/arduino-cli/configs"
"github.com/arduino/arduino-cli/version"
"github.com/arduino/go-paths-helper"
"github.com/sirupsen/logrus"
)
var (
......@@ -36,70 +47,3 @@ func getHTTPClientHeader() http.Header {
downloaderHeaders := http.Header{"User-Agent": []string{userAgentValue}}
return downloaderHeaders
}
// InitConfigs initializes the configuration from the specified file.
func InitConfigs() {
// Start with default configuration
if conf, err := configs.NewConfiguration(); err != nil {
logrus.WithError(err).Error("Error creating default configuration")
formatter.PrintError(err, "Error creating default configuration")
os.Exit(errorcodes.ErrGeneric)
} else {
Config = conf
}
// Read configuration from global config file
logrus.Info("Checking for config file in: " + Config.ConfigFile.String())
if Config.ConfigFile.Exist() {
readConfigFrom(Config.ConfigFile)
}
if Config.IsBundledInDesktopIDE() {
logrus.Info("CLI is bundled into the IDE")
err := Config.LoadFromDesktopIDEPreferences()
if err != nil {
logrus.WithError(err).Warn("Did not manage to get config file of IDE, using default configuration")
}
} else {
logrus.Info("CLI is not bundled into the IDE")
}
// Read configuration from parent folders (project config)
if pwd, err := paths.Getwd(); err != nil {
logrus.WithError(err).Warn("Did not manage to find current path")
if path := paths.New("arduino-yaml"); path.Exist() {
readConfigFrom(path)
}
} else {
Config.Navigate(pwd)
}
// Read configuration from old configuration file if found, but output a warning.
if old := paths.New(".cli-config.yml"); old.Exist() {
logrus.Errorf("Old configuration file detected: %s.", old)
logrus.Info("The name of this file has been changed to `arduino-yaml`, please rename the file fix it.")
formatter.PrintError(
fmt.Errorf("WARNING: Old configuration file detected: %s", old),
"The name of this file has been changed to `arduino-yaml`, in a future release we will not support"+
"the old name `.cli-config.yml` anymore. Please rename the file to `arduino-yaml` to silence this warning.")
readConfigFrom(old)
}
// Read configuration from environment vars
Config.LoadFromEnv()
// Read configuration from user specified file
if YAMLConfigFile != "" {
Config.ConfigFile = paths.New(YAMLConfigFile)
readConfigFrom(Config.ConfigFile)
}
logrus.Info("Configuration set")
}
func readConfigFrom(path *paths.Path) {
logrus.Infof("Reading configuration from %s", path)
if err := Config.LoadFromYAML(path); err != nil {
logrus.WithError(err).Warnf("Could not read configuration from %s", path)
}
}
......@@ -21,7 +21,6 @@ import (
"context"
"os"
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/cli/globals"
"github.com/arduino/arduino-cli/cli/instance"
......@@ -48,16 +47,17 @@ func initDownloadCommand() *cobra.Command {
func runDownloadCommand(cmd *cobra.Command, args []string) {
instance := instance.CreateInstaceIgnorePlatformIndexErrors()
pairs, err := librariesindex.ParseArgs(args)
refs, err := globals.ParseReferenceArgs(args, false)
if err != nil {
formatter.PrintError(err, "Arguments error")
formatter.PrintError(err, "Invalid argument passed")
os.Exit(errorcodes.ErrBadArgument)
}
for _, library := range pairs {
for _, library := range refs {
libraryDownloadReq := &rpc.LibraryDownloadReq{
Instance: instance,
Name: library.Name,
Version: library.Version.String(),
Name: library.PackageName,
Version: library.Version,
}
_, err := lib.LibraryDownload(context.Background(), libraryDownloadReq, output.ProgressBar(),
globals.HTTPClientHeader)
......
......@@ -21,7 +21,6 @@ import (
"context"
"os"
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/cli/globals"
"github.com/arduino/arduino-cli/cli/instance"
......@@ -48,16 +47,17 @@ func initInstallCommand() *cobra.Command {
func runInstallCommand(cmd *cobra.Command, args []string) {
instance := instance.CreateInstaceIgnorePlatformIndexErrors()
refs, err := librariesindex.ParseArgs(args)
refs, err := globals.ParseReferenceArgs(args, false)
if err != nil {
formatter.PrintError(err, "Arguments error")
os.Exit(errorcodes.ErrBadArgument)
}
for _, library := range refs {
libraryInstallReq := &rpc.LibraryInstallReq{
Instance: instance,
Name: library.Name,
Version: library.Version.String(),
Name: library.PackageName,
Version: library.Version,
}
err := lib.LibraryInstall(context.Background(), libraryInstallReq, output.ProgressBar(),
output.TaskProgress(), globals.HTTPClientHeader)
......
......@@ -21,8 +21,8 @@ import (
"context"
"os"
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
"github.com/arduino/arduino-cli/cli/errorcodes"
"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"
......@@ -48,17 +48,17 @@ func runUninstallCommand(cmd *cobra.Command, args []string) {
logrus.Info("Executing `arduino lib uninstall`")
instance := instance.CreateInstaceIgnorePlatformIndexErrors()
libRefs, err := librariesindex.ParseArgs(args)
refs, err := globals.ParseReferenceArgs(args, false)
if err != nil {
formatter.PrintError(err, "Arguments error")
formatter.PrintError(err, "Invalid argument passed")
os.Exit(errorcodes.ErrBadArgument)
}
for _, library := range libRefs {
for _, library := range refs {
err := lib.LibraryUninstall(context.Background(), &rpc.LibraryUninstallReq{
Instance: instance,
Name: library.Name,
Version: library.Version.String(),
Name: library.PackageName,
Version: library.Version,
}, output.TaskProgress())
if err != nil {
formatter.PrintError(err, "Error uninstalling "+library.String())
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment