Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

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 'board' commands
  • Loading branch information
cmaglie committed Aug 28, 2021
commit 4fa1080c3a50ef8760aa74207f54ef814ccf54f3
16 changes: 7 additions & 9 deletions commands/board/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,23 @@ import (
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
discovery "github.com/arduino/board-discovery"
"github.com/arduino/go-paths-helper"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

var tr = i18n.Tr

// Attach FIXMEDOC
func Attach(ctx context.Context, req *rpc.BoardAttachRequest, taskCB commands.TaskProgressCB) (*rpc.BoardAttachResponse, *status.Status) {
func Attach(ctx context.Context, req *rpc.BoardAttachRequest, taskCB commands.TaskProgressCB) (*rpc.BoardAttachResponse, error) {
pm := commands.GetPackageManager(req.GetInstance().GetId())
if pm == nil {
return nil, status.New(codes.InvalidArgument, tr("Invalid instance"))
return nil, &commands.InvalidInstanceError{}
}
var sketchPath *paths.Path
if req.GetSketchPath() != "" {
sketchPath = paths.New(req.GetSketchPath())
}
sk, err := sketch.New(sketchPath)
if err != nil {
return nil, status.Newf(codes.FailedPrecondition, tr("Error opening sketch: %s"), err)
return nil, &commands.SketchNotFoundError{Cause: err}
}

boardURI := req.GetBoardUri()
Expand All @@ -64,7 +62,7 @@ func Attach(ctx context.Context, req *rpc.BoardAttachRequest, taskCB commands.Ta
} else {
deviceURI, err := url.Parse(boardURI)
if err != nil {
return nil, status.Newf(codes.InvalidArgument, tr("Invalid Device URL format: %s"), err)
return nil, &commands.InvalidArgumentError{Message: tr("Invalid Device URL format"), Cause: err}
}

var findBoardFunc func(*packagemanager.PackageManager, *discovery.Monitor, *url.URL) *cores.Board
Expand All @@ -74,7 +72,7 @@ func Attach(ctx context.Context, req *rpc.BoardAttachRequest, taskCB commands.Ta
case "http", "https", "tcp", "udp":
findBoardFunc = findNetworkConnectedBoard
default:
return nil, status.New(codes.InvalidArgument, tr("Invalid device port type provided"))
return nil, &commands.InvalidArgumentError{Message: tr("Invalid device port type provided")}
}

duration, err := time.ParseDuration(req.GetSearchTimeout())
Expand All @@ -90,7 +88,7 @@ func Attach(ctx context.Context, req *rpc.BoardAttachRequest, taskCB commands.Ta
// TODO: Handle the case when no board is found.
board := findBoardFunc(pm, monitor, deviceURI)
if board == nil {
return nil, status.Newf(codes.NotFound, tr("No supported board found at %s"), deviceURI.String())
return nil, &commands.InvalidArgumentError{Message: tr("No supported board found at %s", deviceURI)}
}
taskCB(&rpc.TaskProgress{Name: fmt.Sprintf(tr("Board found: %s"), board.Name())})

Expand All @@ -105,7 +103,7 @@ func Attach(ctx context.Context, req *rpc.BoardAttachRequest, taskCB commands.Ta

err = sk.ExportMetadata()
if err != nil {
return nil, status.Newf(codes.PermissionDenied, tr("Cannot export sketch metadata: %s"), err)
return nil, &commands.PermissionDeniedError{Message: tr("Cannot export sketch metadata"), Cause: err}
}
taskCB(&rpc.TaskProgress{Name: fmt.Sprintf(tr("Selected fqbn: %s"), sk.Metadata.CPU.Fqbn), Completed: true})
return &rpc.BoardAttachResponse{}, nil
Expand Down
11 changes: 4 additions & 7 deletions commands/board/details.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,24 @@ 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"
)

// Details returns all details for a board including tools and HW identifiers.
// This command basically gather al the information and translates it into the required grpc struct properties
func Details(ctx context.Context, req *rpc.BoardDetailsRequest) (*rpc.BoardDetailsResponse, *status.Status) {
func Details(ctx context.Context, req *rpc.BoardDetailsRequest) (*rpc.BoardDetailsResponse, error) {
pm := commands.GetPackageManager(req.GetInstance().GetId())
if pm == nil {
return nil, status.New(codes.InvalidArgument, tr("Invalid instance"))
return nil, &commands.InvalidInstanceError{}
}

fqbn, err := cores.ParseFQBN(req.GetFqbn())
if err != nil {
return nil, status.Newf(codes.InvalidArgument, tr("Error parsing fqbn: %s"), err)
return nil, &commands.InvalidFQBNError{Cause: err}
}

boardPackage, boardPlatform, board, boardProperties, boardRefPlatform, err := pm.ResolveFQBN(fqbn)

if err != nil {
return nil, status.Newf(codes.FailedPrecondition, tr("Error loading board data: %s"), err)
return nil, &commands.UnknownFQBNError{Cause: err}
}

details := &rpc.BoardDetailsResponse{}
Expand Down
20 changes: 9 additions & 11 deletions commands/board/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ import (
"github.com/pkg/errors"
"github.com/segmentio/stats/v4"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

var (
Expand Down Expand Up @@ -139,7 +137,7 @@ func identify(pm *packagemanager.PackageManager, port *discovery.Port) ([]*rpc.B
logrus.Debug("Board not recognized")
} else if err != nil {
// this is bad, bail out
return nil, errors.Wrap(err, tr("error getting board info from Arduino Cloud"))
return nil, &commands.UnavailableError{Message: tr("Error getting board info from Arduino Cloud")}
}

// add a DetectedPort entry in any case: the `Boards` field will
Expand Down Expand Up @@ -170,7 +168,7 @@ func identify(pm *packagemanager.PackageManager, port *discovery.Port) ([]*rpc.B
}

// List FIXMEDOC
func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, e *status.Status) {
func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, e error) {
tags := map[string]string{}
// Use defer func() to evaluate tags map when function returns
// and set success flag inspecting the error named return parameter
Expand All @@ -184,15 +182,15 @@ func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, e *status.Status) {

pm := commands.GetPackageManager(req.GetInstance().Id)
if pm == nil {
return nil, status.New(codes.InvalidArgument, tr("Invalid instance"))
return nil, &commands.InvalidInstanceError{}
}

dm := pm.DiscoveryManager()
if errs := dm.RunAll(); len(errs) > 0 {
return nil, status.New(codes.FailedPrecondition, tr("Error getting port list: %v", errs))
return nil, &commands.UnavailableError{Message: tr("Error starting board discoveries"), Cause: errs[0]}
}
if errs := dm.StartAll(); len(errs) > 0 {
return nil, status.New(codes.FailedPrecondition, tr("Error getting port list: %v", errs))
return nil, &commands.UnavailableError{Message: tr("Error starting board discoveries"), Cause: errs[0]}
}
defer func() {
if errs := dm.StopAll(); len(errs) > 0 {
Expand All @@ -204,12 +202,12 @@ func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, e *status.Status) {
retVal := []*rpc.DetectedPort{}
ports, errs := pm.DiscoveryManager().List()
if len(errs) > 0 {
return nil, status.New(codes.FailedPrecondition, tr("Error getting port list: %v", errs))
return nil, &commands.UnavailableError{Message: tr("Error getting board list"), Cause: errs[0]}
}
for _, port := range ports {
boards, err := identify(pm, port)
if err != nil {
return nil, status.New(codes.Internal, err.Error())
return nil, err
}

// boards slice can be empty at this point if neither the cores nor the
Expand All @@ -226,14 +224,14 @@ func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, e *status.Status) {

// Watch returns a channel that receives boards connection and disconnection events.
// The discovery process can be interrupted by sending a message to the interrupt channel.
func Watch(instanceID int32, interrupt <-chan bool) (<-chan *rpc.BoardListWatchResponse, *status.Status) {
func Watch(instanceID int32, interrupt <-chan bool) (<-chan *rpc.BoardListWatchResponse, error) {
pm := commands.GetPackageManager(instanceID)
dm := pm.DiscoveryManager()

runErrs := dm.RunAll()
if len(runErrs) == len(dm.IDs()) {
// All discoveries failed to run, we can't do anything
return nil, status.New(codes.FailedPrecondition, fmt.Sprintf("%v", runErrs))
return nil, &commands.UnavailableError{Message: tr("Error starting board discoveries"), Cause: runErrs[0]}
}

eventsChan, errs := dm.StartSyncAll()
Expand Down
6 changes: 2 additions & 4 deletions commands/board/listall.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@ import (
"github.com/arduino/arduino-cli/arduino/utils"
"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"
)

// ListAll FIXMEDOC
func ListAll(ctx context.Context, req *rpc.BoardListAllRequest) (*rpc.BoardListAllResponse, *status.Status) {
func ListAll(ctx context.Context, req *rpc.BoardListAllRequest) (*rpc.BoardListAllResponse, error) {
pm := commands.GetPackageManager(req.GetInstance().GetId())
if pm == nil {
return nil, status.New(codes.InvalidArgument, tr("Invalid instance"))
return nil, &commands.InvalidInstanceError{}
}

searchArgs := strings.Join(req.GetSearchArgs(), " ")
Expand Down
6 changes: 2 additions & 4 deletions commands/board/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,16 @@ import (
"github.com/arduino/arduino-cli/arduino/utils"
"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"
)

// Search returns all boards that match the search arg.
// Boards are searched in all platforms, including those in the index that are not yet
// installed. Note that platforms that are not installed don't include boards' FQBNs.
// If no search argument is used all boards are returned.
func Search(ctx context.Context, req *rpc.BoardSearchRequest) (*rpc.BoardSearchResponse, *status.Status) {
func Search(ctx context.Context, req *rpc.BoardSearchRequest) (*rpc.BoardSearchResponse, error) {
pm := commands.GetPackageManager(req.GetInstance().GetId())
if pm == nil {
return nil, status.New(codes.InvalidArgument, tr("Invalid instance"))
return nil, &commands.InvalidInstanceError{}
}

res := &rpc.BoardSearchResponse{Boards: []*rpc.BoardListItem{}}
Expand Down
30 changes: 8 additions & 22 deletions commands/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,15 @@ func convertErrorToRPCStatus(err error) error {
// BoardDetails FIXMEDOC
func (s *ArduinoCoreServerImpl) BoardDetails(ctx context.Context, req *rpc.BoardDetailsRequest) (*rpc.BoardDetailsResponse, error) {
resp, err := board.Details(ctx, req)
if err != nil {
return nil, err.Err()
}

return resp, err.Err()
return resp, convertErrorToRPCStatus(err)
}

// BoardList FIXMEDOC
func (s *ArduinoCoreServerImpl) BoardList(ctx context.Context, req *rpc.BoardListRequest) (*rpc.BoardListResponse, error) {
ports, err := board.List(req)
if err != nil {
return nil, err.Err()
return nil, convertErrorToRPCStatus(err)
}

return &rpc.BoardListResponse{
Ports: ports,
}, nil
Expand All @@ -73,21 +68,13 @@ func (s *ArduinoCoreServerImpl) BoardList(ctx context.Context, req *rpc.BoardLis
// BoardListAll FIXMEDOC
func (s *ArduinoCoreServerImpl) BoardListAll(ctx context.Context, req *rpc.BoardListAllRequest) (*rpc.BoardListAllResponse, error) {
resp, err := board.ListAll(ctx, req)
if err != nil {
return nil, err.Err()
}

return resp, nil
return resp, convertErrorToRPCStatus(err)
}

// BoardSearch exposes to the gRPC interface the board search command
func (s *ArduinoCoreServerImpl) BoardSearch(ctx context.Context, req *rpc.BoardSearchRequest) (*rpc.BoardSearchResponse, error) {
resp, err := board.Search(ctx, req)
if err != nil {
return nil, err.Err()
}

return resp, nil
return resp, convertErrorToRPCStatus(err)
}

// BoardListWatch FIXMEDOC
Expand Down Expand Up @@ -134,9 +121,9 @@ func (s *ArduinoCoreServerImpl) BoardListWatch(stream rpc.ArduinoCoreService_Boa
}
}()

eventsChan, stat := board.Watch(msg.Instance.Id, interrupt)
if stat != nil {
return stat.Err()
eventsChan, err := board.Watch(msg.Instance.Id, interrupt)
if err != nil {
return convertErrorToRPCStatus(err)
}

for event := range eventsChan {
Expand All @@ -151,12 +138,11 @@ func (s *ArduinoCoreServerImpl) BoardListWatch(stream rpc.ArduinoCoreService_Boa

// BoardAttach FIXMEDOC
func (s *ArduinoCoreServerImpl) BoardAttach(req *rpc.BoardAttachRequest, stream rpc.ArduinoCoreService_BoardAttachServer) error {

resp, err := board.Attach(stream.Context(), req,
func(p *rpc.TaskProgress) { stream.Send(&rpc.BoardAttachResponse{TaskProgress: p}) },
)
if err != nil {
return err.Err()
return convertErrorToRPCStatus(err)
}
return stream.Send(resp)
}
Expand Down
72 changes: 72 additions & 0 deletions commands/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,75 @@ func (e *FailedUploadError) Unwrap() error {
func (e *FailedUploadError) ToRPCStatus() *status.Status {
return status.New(codes.Internal, e.Error())
}

// InvalidArgumentError is returned when an invalid argument is passed to the command
type InvalidArgumentError struct {
Message string
Cause error
}

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

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

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

// NotFoundError is returned when a resource is not found
type NotFoundError struct {
Message string
Cause error
}

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

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

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

// PermissionDeniedError is returned when a resource cannot be accessed or modified
type PermissionDeniedError struct {
Message string
Cause error
}

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

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

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

// UnavailableError is returned when a resource is temporarily not available
type UnavailableError struct {
Message string
Cause error
}

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

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

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