Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Refactoring 'upload' commands
  • Loading branch information
cmaglie committed Aug 28, 2021
commit acf791ec27d908980e126e491b39a798054e4b82
8 changes: 4 additions & 4 deletions arduino/cores/fqbn.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func ParseFQBN(fqbnIn string) (*FQBN, error) {
// Split fqbn
fqbnParts := strings.Split(fqbnIn, ":")
if len(fqbnParts) < 3 || len(fqbnParts) > 4 {
return nil, fmt.Errorf("invalid fqbn: %s", fqbnIn)
return nil, fmt.Errorf("not an FQBN: %s", fqbnIn)
}

fqbn := &FQBN{
Expand All @@ -45,18 +45,18 @@ func ParseFQBN(fqbnIn string) (*FQBN, error) {
Configs: properties.NewMap(),
}
if fqbn.BoardID == "" {
return nil, fmt.Errorf(tr("invalid fqbn: empty board identifier"))
return nil, fmt.Errorf(tr("empty board identifier"))
}
if len(fqbnParts) > 3 {
for _, pair := range strings.Split(fqbnParts[3], ",") {
parts := strings.SplitN(pair, "=", 2)
if len(parts) != 2 {
return nil, fmt.Errorf(tr("invalid fqbn config: %s"), pair)
return nil, fmt.Errorf(tr("invalid config oprion: %s"), pair)
}
k := strings.TrimSpace(parts[0])
v := strings.TrimSpace(parts[1])
if k == "" {
return nil, fmt.Errorf(tr("invalid fqbn config: %s"), pair)
return nil, fmt.Errorf(tr("invalid config option: %s"), pair)
}
fqbn.Configs.Set(k, v)
}
Expand Down
15 changes: 8 additions & 7 deletions cli/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,17 +231,18 @@ func run(cmd *cobra.Command, args []string) {
Programmer: programmer,
UserFields: fields,
}
var st *status.Status

var uploadError error
if output.OutputFormat == "json" {
// TODO: do not print upload output in json mode
uploadOut := new(bytes.Buffer)
uploadErr := new(bytes.Buffer)
_, st = upload.Upload(context.Background(), uploadRequest, uploadOut, uploadErr)
uploadStdOut := new(bytes.Buffer)
uploadStdErr := new(bytes.Buffer)
_, uploadError = upload.Upload(context.Background(), uploadRequest, uploadStdOut, uploadStdErr)
} else {
_, st = upload.Upload(context.Background(), uploadRequest, os.Stdout, os.Stderr)
_, uploadError = upload.Upload(context.Background(), uploadRequest, os.Stdout, os.Stderr)
}
if st != nil {
feedback.Errorf(tr("Error during Upload: %v"), st.Message())
if uploadError != nil {
feedback.Errorf(tr("Error during Upload: %v"), uploadError)
os.Exit(errorcodes.ErrGeneric)
}
}
Expand Down
15 changes: 11 additions & 4 deletions commands/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ type ArduinoCoreServerImpl struct {

var tr = i18n.Tr

func convertErrorToRPCStatus(err error) error {
if cmdErr, ok := err.(commands.CommandError); ok {
return cmdErr.ToRPCStatus().Err()
}
return err
}

// BoardDetails FIXMEDOC
func (s *ArduinoCoreServerImpl) BoardDetails(ctx context.Context, req *rpc.BoardDetailsRequest) (*rpc.BoardDetailsResponse, error) {
resp, err := board.Details(ctx, req)
Expand Down Expand Up @@ -336,7 +343,7 @@ func (s *ArduinoCoreServerImpl) Upload(req *rpc.UploadRequest, stream rpc.Arduin
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.UploadResponse{ErrStream: data}) }),
)
if err != nil {
return err.Err()
return convertErrorToRPCStatus(err)
}
return stream.Send(resp)
}
Expand All @@ -349,7 +356,7 @@ func (s *ArduinoCoreServerImpl) UploadUsingProgrammer(req *rpc.UploadUsingProgra
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.UploadUsingProgrammerResponse{ErrStream: data}) }),
)
if err != nil {
return err.Err()
return convertErrorToRPCStatus(err)
}
return stream.Send(resp)
}
Expand All @@ -362,15 +369,15 @@ func (s *ArduinoCoreServerImpl) BurnBootloader(req *rpc.BurnBootloaderRequest, s
utils.FeedStreamTo(func(data []byte) { stream.Send(&rpc.BurnBootloaderResponse{ErrStream: data}) }),
)
if err != nil {
return err.Err()
return convertErrorToRPCStatus(err)
}
return stream.Send(resp)
}

// ListProgrammersAvailableForUpload FIXMEDOC
func (s *ArduinoCoreServerImpl) ListProgrammersAvailableForUpload(ctx context.Context, req *rpc.ListProgrammersAvailableForUploadRequest) (*rpc.ListProgrammersAvailableForUploadResponse, error) {
resp, err := upload.ListProgrammersAvailableForUpload(ctx, req)
return resp, err.Err()
return resp, convertErrorToRPCStatus(err)
}

// LibraryDownload FIXMEDOC
Expand Down
207 changes: 207 additions & 0 deletions commands/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// 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 license@arduino.cc.

package commands

import (
"fmt"

rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

func composeErrorMsg(msg string, cause error) string {
if cause == nil {
return msg
}
return fmt.Sprintf("%v: %v", msg, cause)
}

// CommandError is an error that may be converted into a gRPC status.
type CommandError interface {
ToRPCStatus() *status.Status
}

// InvalidInstanceError is returned if the instance used in the command is not valid.
type InvalidInstanceError struct{}

func (e *InvalidInstanceError) Error() string {
return tr("Invalid instance")
}

func (e *InvalidInstanceError) ToRPCStatus() *status.Status {
return status.New(codes.InvalidArgument, e.Error())
}

// InvalidFQBNError is returned when the FQBN has syntax errors
type InvalidFQBNError struct {
Cause error
}

func (e *InvalidFQBNError) Error() string {
return composeErrorMsg(tr("Invalid FQBN"), e.Cause)
}

func (e *InvalidFQBNError) ToRPCStatus() *status.Status {
return status.New(codes.InvalidArgument, e.Error())
}

func (e *InvalidFQBNError) Unwrap() error {
return e.Cause
}

// MissingFQBNError is returned when the FQBN is mandatory and not specified
type MissingFQBNError struct{}

func (e *MissingFQBNError) Error() string {
return tr("Missing FQBN (Fully Qualified Board Name)")
}

func (e *MissingFQBNError) ToRPCStatus() *status.Status {
return status.New(codes.InvalidArgument, e.Error())
}

// UnknownFQBNError is returned when the FQBN is not found
type UnknownFQBNError struct {
Cause error
}

func (e *UnknownFQBNError) Error() string {
return composeErrorMsg(tr("Unknown FQBN"), e.Cause)
}

func (e *UnknownFQBNError) Unwrap() error {
return e.Cause
}

func (e *UnknownFQBNError) ToRPCStatus() *status.Status {
return status.New(codes.NotFound, e.Error())
}

// MissingPortProtocolError is returned when the port protocol is mandatory and not specified
type MissingPortProtocolError struct{}

func (e *MissingPortProtocolError) Error() string {
return tr("Missing port protocol")
}

func (e *MissingPortProtocolError) ToRPCStatus() *status.Status {
return status.New(codes.InvalidArgument, e.Error())
}

// MissingProgrammerError is returned when the programmer is mandatory and not specified
type MissingProgrammerError struct{}

func (e *MissingProgrammerError) Error() string {
return tr("Missing programmer")
}

func (e *MissingProgrammerError) ToRPCStatus() *status.Status {
return status.New(codes.InvalidArgument, e.Error())
}

// ProgreammerRequiredForUploadError is returned then the upload can be done only using a programmer
type ProgreammerRequiredForUploadError struct{}

func (e *ProgreammerRequiredForUploadError) Error() string {
return tr("A programmer is required to upload")
}

func (e *ProgreammerRequiredForUploadError) ToRPCStatus() *status.Status {
st, _ := status.
New(codes.InvalidArgument, e.Error()).
WithDetails(&rpc.ProgrammerIsRequiredForUploadError{})
return st
}

// UnknownProgrammerError is returned when the programmer is not found
type UnknownProgrammerError struct {
Cause error
}

func (e *UnknownProgrammerError) Error() string {
return composeErrorMsg(tr("Unknown programmer"), e.Cause)
}

func (e *UnknownProgrammerError) Unwrap() error {
return e.Cause
}

func (e *UnknownProgrammerError) ToRPCStatus() *status.Status {
return status.New(codes.NotFound, e.Error())
}

// InvalidPlatformPropertyError is returned when a property in the platform is not valid
type InvalidPlatformPropertyError struct {
Property string
Value string
}

func (e *InvalidPlatformPropertyError) Error() string {
return tr("Invalid '%[1]s' property: %[2]s", e.Property, e.Value)
}

func (e *InvalidPlatformPropertyError) ToRPCStatus() *status.Status {
return status.New(codes.FailedPrecondition, e.Error())
}

// MissingPlatformPropertyError is returned when a property in the platform is not found
type MissingPlatformPropertyError struct {
Property string
}

func (e *MissingPlatformPropertyError) Error() string {
return tr("Property '%s' is undefined", e.Property)
}

func (e *MissingPlatformPropertyError) ToRPCStatus() *status.Status {
return status.New(codes.FailedPrecondition, e.Error())
}

// SketchNotFoundError is returned when the sketch is not found
type SketchNotFoundError struct {
Cause error
}

func (e *SketchNotFoundError) Error() string {
return composeErrorMsg(tr("Sketch not found"), e.Cause)
}

func (e *SketchNotFoundError) Unwrap() error {
return e.Cause
}

func (e *SketchNotFoundError) ToRPCStatus() *status.Status {
return status.New(codes.NotFound, e.Error())
}

// FailedUploadError is returned when the upload fails
type FailedUploadError struct {
Message string
Cause error
}

func (e *FailedUploadError) Error() string {
return composeErrorMsg(e.Message, e.Cause)
}

func (e *FailedUploadError) Unwrap() error {
return e.Cause
}

func (e *FailedUploadError) ToRPCStatus() *status.Status {
return status.New(codes.Internal, e.Error())
}
3 changes: 1 addition & 2 deletions commands/upload/burnbootloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ import (
"github.com/arduino/arduino-cli/commands"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/status"
)

// BurnBootloader FIXMEDOC
func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStream io.Writer, errStream io.Writer) (*rpc.BurnBootloaderResponse, *status.Status) {
func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStream io.Writer, errStream io.Writer) (*rpc.BurnBootloaderResponse, error) {
logrus.
WithField("fqbn", req.GetFqbn()).
WithField("port", req.GetPort()).
Expand Down
10 changes: 4 additions & 6 deletions commands/upload/programmers_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,25 @@ import (
"github.com/arduino/arduino-cli/arduino/cores"
"github.com/arduino/arduino-cli/commands"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

// ListProgrammersAvailableForUpload FIXMEDOC
func ListProgrammersAvailableForUpload(ctx context.Context, req *rpc.ListProgrammersAvailableForUploadRequest) (*rpc.ListProgrammersAvailableForUploadResponse, *status.Status) {
func ListProgrammersAvailableForUpload(ctx context.Context, req *rpc.ListProgrammersAvailableForUploadRequest) (*rpc.ListProgrammersAvailableForUploadResponse, error) {
pm := commands.GetPackageManager(req.GetInstance().GetId())

fqbnIn := req.GetFqbn()
if fqbnIn == "" {
return nil, status.New(codes.InvalidArgument, tr("No FQBN (Fully Qualified Board Name) provided"))
return nil, &commands.MissingFQBNError{}
}
fqbn, err := cores.ParseFQBN(fqbnIn)
if err != nil {
return nil, status.Newf(codes.InvalidArgument, tr("Invalid FQBN: %s"), err)
return nil, &commands.InvalidFQBNError{Cause: err}
}

// Find target platforms
_, platform, _, _, refPlatform, err := pm.ResolveFQBN(fqbn)
if err != nil {
return nil, status.Newf(codes.Unknown, tr("Invalid FQBN: %s"), err)
return nil, &commands.UnknownFQBNError{Cause: err}
}

result := []*rpc.Programmer{}
Expand Down
Loading