From a33072ed3b57992449d70068c0746924ee0b939b Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 14:51:04 +0200 Subject: [PATCH 01/24] added more mapper in feedback result pkg --- internal/cli/feedback/result/rpc.go | 465 ++++++++++++++++++++++++++++ 1 file changed, 465 insertions(+) diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index b6bdb636706..34b7700e410 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -16,6 +16,8 @@ package result import ( + "cmp" + "github.com/arduino/arduino-cli/internal/orderedmap" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" semver "go.bug.st/relaxed-semver" @@ -119,3 +121,466 @@ type Board struct { type HelpResource struct { Online string `json:"online,omitempty"` } + +type InstalledLibrary struct { + Library *Library `json:"library,omitempty"` + Release *LibraryRelease `json:"release,omitempty"` +} + +type LibraryLocation string + +type LibraryLayout string + +type Library struct { + Name string `json:"name,omitempty"` + Author string `json:"author,omitempty"` + Maintainer string `json:"maintainer,omitempty"` + Sentence string `json:"sentence,omitempty"` + Paragraph string `json:"paragraph,omitempty"` + Website string `json:"website,omitempty"` + Category string `json:"category,omitempty"` + Architectures []string `json:"architectures,omitempty"` + Types []string `json:"types,omitempty"` + InstallDir string `json:"install_dir,omitempty"` + SourceDir string `json:"source_dir,omitempty"` + UtilityDir string `json:"utility_dir,omitempty"` + ContainerPlatform string `json:"container_platform,omitempty"` + DotALinkage bool `json:"dot_a_linkage,omitempty"` + Precompiled bool `json:"precompiled,omitempty"` + LdFlags string `json:"ld_flags,omitempty"` + IsLegacy bool `json:"is_legacy,omitempty"` + Version string `json:"version,omitempty"` + License string `json:"license,omitempty"` + Properties orderedmap.Map[string, string] `json:"properties,omitempty"` + Location LibraryLocation `json:"location,omitempty"` + Layout LibraryLayout `json:"layout,omitempty"` + Examples []string `json:"examples,omitempty"` + ProvidesIncludes []string `json:"provides_includes,omitempty"` + CompatibleWith orderedmap.Map[string, bool] `json:"compatible_with,omitempty"` + InDevelopment bool `json:"in_development,omitempty"` +} + +type LibraryRelease struct { + Author string `json:"author,omitempty"` + Version string `json:"version,omitempty"` + Maintainer string `json:"maintainer,omitempty"` + Sentence string `json:"sentence,omitempty"` + Paragraph string `json:"paragraph,omitempty"` + Website string `json:"website,omitempty"` + Category string `json:"category,omitempty"` + Architectures []string `json:"architectures,omitempty"` + Types []string `json:"types,omitempty"` + Resources *DownloadResource `json:"resources,omitempty"` + License string `json:"license,omitempty"` + ProvidesIncludes []string `json:"provides_includes,omitempty"` + Dependencies []*LibraryDependency `json:"dependencies,omitempty"` +} + +type DownloadResource struct { + Url string `json:"url,omitempty"` + ArchiveFilename string `json:"archive_filename,omitempty"` + Checksum string `json:"checksum,omitempty"` + Size int64 `json:"size,omitempty"` + CachePath string `json:"cache_path,omitempty"` +} + +type LibraryDependency struct { + Name string `json:"name,omitempty"` + VersionConstraint string `json:"version_constraint,omitempty"` +} + +func NewInstalledLibraryResult(l *rpc.InstalledLibrary) *InstalledLibrary { + libraryPropsMap := orderedmap.New[string, string]() + for k, v := range l.GetLibrary().GetProperties() { + libraryPropsMap.Set(k, v) + } + libraryPropsMap.SortStableKeys(cmp.Compare) + + libraryCompatibleWithMap := orderedmap.New[string, bool]() + for k, v := range l.GetLibrary().GetCompatibleWith() { + libraryCompatibleWithMap.Set(k, v) + } + libraryCompatibleWithMap.SortStableKeys(cmp.Compare) + + return &InstalledLibrary{ + Library: &Library{ + Name: l.GetLibrary().GetName(), + Author: l.GetLibrary().GetAuthor(), + Maintainer: l.GetLibrary().GetMaintainer(), + Sentence: l.GetLibrary().GetSentence(), + Paragraph: l.GetLibrary().GetParagraph(), + Website: l.GetLibrary().GetWebsite(), + Category: l.GetLibrary().GetCategory(), + Architectures: l.GetLibrary().GetArchitectures(), + Types: l.GetLibrary().GetTypes(), + InstallDir: l.GetLibrary().GetInstallDir(), + SourceDir: l.GetLibrary().GetSourceDir(), + UtilityDir: l.GetLibrary().GetUtilityDir(), + ContainerPlatform: l.GetLibrary().GetContainerPlatform(), + DotALinkage: l.GetLibrary().GetDotALinkage(), + Precompiled: l.GetLibrary().GetPrecompiled(), + LdFlags: l.GetLibrary().GetLdFlags(), + IsLegacy: l.GetLibrary().GetIsLegacy(), + Version: l.GetLibrary().GetVersion(), + License: l.GetLibrary().GetLicense(), + Properties: libraryPropsMap, + Location: LibraryLocation(l.GetLibrary().GetLocation().String()), + Layout: LibraryLayout(l.GetLibrary().GetLayout().String()), + Examples: l.GetLibrary().GetExamples(), + ProvidesIncludes: l.GetLibrary().GetProvidesIncludes(), + CompatibleWith: libraryCompatibleWithMap, + InDevelopment: l.GetLibrary().GetInDevelopment(), + }, + Release: &LibraryRelease{ + Author: l.GetRelease().GetAuthor(), + Version: l.GetRelease().GetVersion(), + Maintainer: l.GetRelease().GetMaintainer(), + Sentence: l.GetRelease().GetSentence(), + Paragraph: l.GetRelease().GetParagraph(), + Website: l.GetRelease().GetWebsite(), + Category: l.GetRelease().GetCategory(), + Architectures: l.GetRelease().GetArchitectures(), + Types: l.GetRelease().GetTypes(), + Resources: NewDownloadResource(l.GetRelease().GetResources()), + License: l.GetRelease().GetLicense(), + ProvidesIncludes: l.GetRelease().GetProvidesIncludes(), + Dependencies: NewLibraryDependencies(l.GetRelease().GetDependencies()), + }, + } +} + +func NewDownloadResource(r *rpc.DownloadResource) *DownloadResource { + if r == nil { + return nil + } + return &DownloadResource{ + Url: r.GetUrl(), + ArchiveFilename: r.GetArchiveFilename(), + Checksum: r.GetChecksum(), + Size: r.GetSize(), + CachePath: r.GetCachePath(), + } +} + +func NewLibraryDependencies(d []*rpc.LibraryDependency) []*LibraryDependency { + if d == nil { + return nil + } + result := make([]*LibraryDependency, len(d)) + for i, v := range d { + result[i] = NewLibraryDependency(v) + } + return result +} + +func NewLibraryDependency(d *rpc.LibraryDependency) *LibraryDependency { + if d == nil { + return nil + } + return &LibraryDependency{ + Name: d.GetName(), + VersionConstraint: d.GetVersionConstraint(), + } +} + +type Port struct { + Address string `json:"address,omitempty"` + Label string `json:"label,omitempty"` + Protocol string `json:"protocol,omitempty"` + ProtocolLabel string `json:"protocol_label,omitempty"` + Properties orderedmap.Map[string, string] `json:"properties,omitempty"` + HardwareId string `json:"hardware_id,omitempty"` +} + +func NewPort(p *rpc.Port) *Port { + propertiesMap := orderedmap.New[string, string]() + for k, v := range p.GetProperties() { + propertiesMap.Set(k, v) + } + propertiesMap.SortStableKeys(cmp.Compare) + if p == nil { + return nil + } + return &Port{ + Address: p.GetAddress(), + Label: p.GetLabel(), + Protocol: p.GetProtocol(), + ProtocolLabel: p.GetProtocolLabel(), + Properties: propertiesMap, + HardwareId: p.GetHardwareId(), + } +} + +type BoardDetailsResponse struct { + Fqbn string `json:"fqbn,omitempty"` + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` + PropertiesId string `json:"properties_id,omitempty"` + Alias string `json:"alias,omitempty"` + Official bool `json:"official,omitempty"` + Pinout string `json:"pinout,omitempty"` + Package *Package `json:"package,omitempty"` + Platform *BoardPlatform `json:"platform,omitempty"` + ToolsDependencies []*ToolsDependency `json:"tools_dependencies,omitempty"` + ConfigOptions []*ConfigOption `json:"config_options,omitempty"` + Programmers []*Programmer `json:"programmers,omitempty"` + DebuggingSupported bool `json:"debugging_supported,omitempty"` + IdentificationProperties []*BoardIdentificationProperties `json:"identification_properties,omitempty"` + BuildProperties []string `json:"build_properties,omitempty"` +} + +func NewBoardDetailsResponse(b *rpc.BoardDetailsResponse) *BoardDetailsResponse { + if b == nil { + return nil + } + return &BoardDetailsResponse{ + Fqbn: b.GetFqbn(), + Name: b.GetName(), + Version: b.GetVersion(), + PropertiesId: b.GetPropertiesId(), + Alias: b.GetAlias(), + Official: b.GetOfficial(), + Pinout: b.GetPinout(), + Package: NewPackage(b.GetPackage()), + Platform: NewBoardPlatform(b.GetPlatform()), + ToolsDependencies: NewToolsDependencies(b.GetToolsDependencies()), + ConfigOptions: NewConfigOptions(b.GetConfigOptions()), + Programmers: NewProgrammers(b.GetProgrammers()), + DebuggingSupported: b.GetDebuggingSupported(), + IdentificationProperties: NewBoardIdentificationProperties(b.GetIdentificationProperties()), + BuildProperties: b.GetBuildProperties(), + } +} + +type Package struct { + Maintainer string `json:"maintainer,omitempty"` + Url string `json:"url,omitempty"` + WebsiteUrl string `json:"website_url,omitempty"` + Email string `json:"email,omitempty"` + Name string `json:"name,omitempty"` + Help *Help `json:"help,omitempty"` +} + +func NewPackage(p *rpc.Package) *Package { + if p == nil { + return nil + } + return &Package{ + Maintainer: p.GetMaintainer(), + Url: p.GetUrl(), + WebsiteUrl: p.GetWebsiteUrl(), + Email: p.GetEmail(), + Name: p.GetName(), + Help: NewHelp(p.GetHelp()), + } +} + +type Help struct { + Online string `json:"online,omitempty"` +} + +func NewHelp(h *rpc.Help) *Help { + if h == nil { + return nil + } + return &Help{Online: h.GetOnline()} +} + +type BoardPlatform struct { + Architecture string `json:"architecture,omitempty"` + Category string `json:"category,omitempty"` + Url string `json:"url,omitempty"` + ArchiveFilename string `json:"archive_filename,omitempty"` + Checksum string `json:"checksum,omitempty"` + Size int64 `json:"size,omitempty"` + Name string `json:"name,omitempty"` +} + +func NewBoardPlatform(p *rpc.BoardPlatform) *BoardPlatform { + if p == nil { + return nil + } + return &BoardPlatform{ + Architecture: p.GetArchitecture(), + Category: p.GetCategory(), + Url: p.GetUrl(), + ArchiveFilename: p.GetArchiveFilename(), + Checksum: p.GetChecksum(), + Size: p.GetSize(), + Name: p.GetName(), + } +} + +type ToolsDependency struct { + Packager string `json:"packager,omitempty"` + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` + Systems []*System `json:"systems,omitempty"` +} + +func NewToolsDependencies(p []*rpc.ToolsDependencies) []*ToolsDependency { + if p == nil { + return nil + } + res := make([]*ToolsDependency, len(p)) + for i, v := range p { + res[i] = NewToolsDependency(v) + } + return res +} + +func NewToolsDependency(p *rpc.ToolsDependencies) *ToolsDependency { + if p == nil { + return nil + } + return &ToolsDependency{ + Packager: p.GetPackager(), + Name: p.GetName(), + Version: p.GetVersion(), + Systems: NewSystems(p.GetSystems()), + } +} + +type System struct { + Checksum string `json:"checksum,omitempty"` + Host string `json:"host,omitempty"` + ArchiveFilename string `json:"archive_filename,omitempty"` + Url string `json:"url,omitempty"` + Size int64 `json:"size,omitempty"` +} + +func NewSystems(p []*rpc.Systems) []*System { + if p == nil { + return nil + } + res := make([]*System, len(p)) + for i, v := range p { + res[i] = NewSystem(v) + } + return res +} + +func NewSystem(s *rpc.Systems) *System { + if s == nil { + return nil + } + return &System{ + Checksum: s.GetChecksum(), + Host: s.GetHost(), + ArchiveFilename: s.GetArchiveFilename(), + Url: s.GetUrl(), + Size: s.GetSize(), + } +} + +type ConfigOption struct { + Option string `json:"option,omitempty"` + OptionLabel string `json:"option_label,omitempty"` + Values []*ConfigValue `json:"values,omitempty"` +} + +func NewConfigOptions(c []*rpc.ConfigOption) []*ConfigOption { + if c == nil { + return nil + } + res := make([]*ConfigOption, len(c)) + for i, v := range c { + res[i] = NewConfigOption(v) + } + return res +} + +func NewConfigOption(o *rpc.ConfigOption) *ConfigOption { + if o == nil { + return nil + } + return &ConfigOption{ + Option: o.GetOption(), + OptionLabel: o.GetOptionLabel(), + Values: NewConfigValues(o.GetValues()), + } +} + +type ConfigValue struct { + Value string `json:"value,omitempty"` + ValueLabel string `json:"value_label,omitempty"` + Selected bool `json:"selected,omitempty"` +} + +func NewConfigValues(c []*rpc.ConfigValue) []*ConfigValue { + if c == nil { + return nil + } + res := make([]*ConfigValue, len(c)) + for i, v := range c { + res[i] = NewConfigValue(v) + } + return res +} + +func NewConfigValue(c *rpc.ConfigValue) *ConfigValue { + if c == nil { + return nil + } + return &ConfigValue{ + Value: c.GetValue(), + ValueLabel: c.GetValueLabel(), + Selected: c.GetSelected(), + } +} + +type Programmer struct { + Platform string `json:"platform,omitempty"` + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + +func NewProgrammers(c []*rpc.Programmer) []*Programmer { + if c == nil { + return nil + } + res := make([]*Programmer, len(c)) + for i, v := range c { + res[i] = NewProgrammer(v) + } + return res +} + +func NewProgrammer(c *rpc.Programmer) *Programmer { + if c == nil { + return nil + } + return &Programmer{ + Platform: c.GetPlatform(), + Id: c.GetId(), + Name: c.GetName(), + } +} + +type BoardIdentificationProperties struct { + Properties orderedmap.Map[string, string] `json:"properties,omitempty"` +} + +func NewBoardIdentificationProperties(p []*rpc.BoardIdentificationProperties) []*BoardIdentificationProperties { + if p == nil { + return nil + } + res := make([]*BoardIdentificationProperties, len(p)) + for i, v := range p { + res[i] = NewBoardIndentificationProperty(v) + } + return res +} + +func NewBoardIndentificationProperty(p *rpc.BoardIdentificationProperties) *BoardIdentificationProperties { + if p == nil { + return nil + } + propertiesMap := orderedmap.New[string, string]() + for k, v := range p.GetProperties() { + propertiesMap.Set(k, v) + } + propertiesMap.SortStableKeys(cmp.Compare) + + return &BoardIdentificationProperties{Properties: propertiesMap} +} From e639e5b97e02f55d205c9f93e70cf4ce63e0fb5f Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 14:51:36 +0200 Subject: [PATCH 02/24] cli: outdated command use feedback result structs --- internal/cli/outdated/outdated.go | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/internal/cli/outdated/outdated.go b/internal/cli/outdated/outdated.go index 5b2b6ac6309..c80d768f0bf 100644 --- a/internal/cli/outdated/outdated.go +++ b/internal/cli/outdated/outdated.go @@ -65,16 +65,21 @@ func Outdated(inst *rpc.Instance) { // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation type outdatedResult struct { - Platforms []*result.Platform `json:"platforms,omitempty"` - InstalledLibs []*rpc.InstalledLibrary `json:"libraries,omitempty"` + Platforms []*result.Platform `json:"platforms,omitempty"` + InstalledLibs []*result.InstalledLibrary `json:"libraries,omitempty"` } func newOutdatedResult(inPlatforms []*rpc.PlatformSummary, inLibraries []*rpc.InstalledLibrary) *outdatedResult { - res := &outdatedResult{} - for _, platformSummary := range inPlatforms { - res.Platforms = append(res.Platforms, result.NewPlatformResult(platformSummary)) + res := &outdatedResult{ + Platforms: make([]*result.Platform, len(inPlatforms)), + InstalledLibs: make([]*result.InstalledLibrary, len(inLibraries)), + } + for i, v := range inPlatforms { + res.Platforms[i] = result.NewPlatformResult(v) + } + for i, v := range inLibraries { + res.InstalledLibs[i] = result.NewInstalledLibraryResult(v) } - res.InstalledLibs = inLibraries return res } @@ -128,7 +133,7 @@ func (ir outdatedResult) String() string { }) lastName := "" for _, libMeta := range ir.InstalledLibs { - lib := libMeta.GetLibrary() + lib := libMeta.Library name := lib.Name if name == lastName { name = ` "` @@ -136,15 +141,15 @@ func (ir outdatedResult) String() string { lastName = name } - location := lib.GetLocation().String() + location := string(lib.Location) if lib.ContainerPlatform != "" { - location = lib.GetContainerPlatform() + location = lib.ContainerPlatform } available := "" sentence := "" - if libMeta.GetRelease() != nil { - available = libMeta.GetRelease().GetVersion() + if libMeta.Release != nil { + available = libMeta.Release.Version sentence = lib.Sentence } From cbe3b0d2df79c8b2466bfdb1974267fd9964ce97 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 15:00:16 +0200 Subject: [PATCH 03/24] cli: upload use feedback result structs --- internal/cli/upload/upload.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/cli/upload/upload.go b/internal/cli/upload/upload.go index 4444cf1e792..73e6166aaaa 100644 --- a/internal/cli/upload/upload.go +++ b/internal/cli/upload/upload.go @@ -29,6 +29,7 @@ import ( "github.com/arduino/arduino-cli/i18n" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/version" @@ -199,15 +200,15 @@ func runUploadCommand(args []string, uploadFieldsArgs map[string]string) { feedback.PrintResult(&uploadResult{ Stdout: io.Stdout, Stderr: io.Stderr, - UpdatedUploadPort: res.UpdatedUploadPort, + UpdatedUploadPort: result.NewPort(res.GetUpdatedUploadPort()), }) } } type uploadResult struct { - Stdout string `json:"stdout"` - Stderr string `json:"stderr"` - UpdatedUploadPort *rpc.Port `json:"updated_upload_port,omitempty"` + Stdout string `json:"stdout"` + Stderr string `json:"stderr"` + UpdatedUploadPort *result.Port `json:"updated_upload_port,omitempty"` } func (r *uploadResult) Data() interface{} { From 3ae9deee545022dc19080b6cd75ceb23b4d3c4d4 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 15:48:05 +0200 Subject: [PATCH 04/24] cli: board details use feedback result structs --- internal/cli/board/details.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/internal/cli/board/details.go b/internal/cli/board/details.go index 52bde9c0872..9a9bd504586 100644 --- a/internal/cli/board/details.go +++ b/internal/cli/board/details.go @@ -23,6 +23,7 @@ import ( "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" + fResult "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/table" @@ -74,7 +75,7 @@ func runDetailsCommand(fqbn string, showFullDetails, listProgrammers bool, showP } feedback.PrintResult(detailsResult{ - details: res, + details: fResult.NewBoardDetailsResponse(res), listProgrammers: listProgrammers, showFullDetails: showFullDetails, showProperties: showPropertiesMode != arguments.ShowPropertiesDisabled, @@ -84,7 +85,7 @@ func runDetailsCommand(fqbn string, showFullDetails, listProgrammers bool, showP // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation type detailsResult struct { - details *rpc.BoardDetailsResponse + details *fResult.BoardDetailsResponse listProgrammers bool showFullDetails bool showProperties bool @@ -99,7 +100,7 @@ func (dr detailsResult) String() string { if dr.showProperties { res := "" - for _, prop := range details.GetBuildProperties() { + for _, prop := range details.BuildProperties { res += fmt.Sprintln(prop) } return res @@ -109,7 +110,10 @@ func (dr detailsResult) String() string { t := table.New() t.AddRow(tr("Id"), tr("Programmer name")) for _, programmer := range details.Programmers { - t.AddRow(programmer.GetId(), programmer.GetName()) + if programmer == nil { + continue + } + t.AddRow(programmer.Id, programmer.Name) } return t.Render() } @@ -138,7 +142,7 @@ func (dr detailsResult) String() string { t.AddRow(tr("Board name:"), details.Name) t.AddRow(tr("FQBN:"), details.Fqbn) addIfNotEmpty(tr("Board version:"), details.Version) - if details.GetDebuggingSupported() { + if details.DebuggingSupported { t.AddRow(tr("Debugging supported:"), table.NewCell("✔", color.New(color.FgGreen))) } @@ -148,11 +152,15 @@ func (dr detailsResult) String() string { table.NewCell("✔", color.New(color.FgGreen))) } - for _, idp := range details.GetIdentificationProperties() { + for _, idp := range details.IdentificationProperties { + if idp == nil || idp.Properties == nil { + continue + } t.AddRow() // get some space from above header := tr("Identification properties:") - for k, v := range idp.GetProperties() { - t.AddRow(header, k+"="+v) + keys := idp.Properties.Keys() + for _, k := range keys { + t.AddRow(header, k+"="+idp.Properties.Get(k)) header = "" } } @@ -213,7 +221,10 @@ func (dr detailsResult) String() string { tab.AddRow(tr("Programmers:"), tr("ID"), tr("Name")) for _, programmer := range details.Programmers { - tab.AddRow("", programmer.GetId(), programmer.GetName()) + if programmer == nil { + continue + } + tab.AddRow("", programmer.Id, programmer.Name) } return t.Render() + tab.Render() From da7541a65aacdfeedc91f6cdf92dea96f6b58433 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 16:01:37 +0200 Subject: [PATCH 05/24] rename result Platform in PlatformSummary --- internal/cli/core/list.go | 4 ++-- internal/cli/core/search.go | 4 ++-- internal/cli/feedback/result/rpc.go | 14 +++++++------- internal/cli/outdated/outdated.go | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/cli/core/list.go b/internal/cli/core/list.go index 4242fa6d7a9..1adda2f26bc 100644 --- a/internal/cli/core/list.go +++ b/internal/cli/core/list.go @@ -91,13 +91,13 @@ func GetList(inst *rpc.Instance, all bool, updatableOnly bool) []*rpc.PlatformSu func newCoreListResult(in []*rpc.PlatformSummary) *coreListResult { res := &coreListResult{} for _, platformSummary := range in { - res.platforms = append(res.platforms, result.NewPlatformResult(platformSummary)) + res.platforms = append(res.platforms, result.NewPlatformSummary(platformSummary)) } return res } type coreListResult struct { - platforms []*result.Platform + platforms []*result.PlatformSummary } // Data implements Result interface diff --git a/internal/cli/core/search.go b/internal/cli/core/search.go index 78e02ddf400..30d4f749759 100644 --- a/internal/cli/core/search.go +++ b/internal/cli/core/search.go @@ -88,13 +88,13 @@ func runSearchCommand(cmd *cobra.Command, args []string) { // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation type searchResults struct { - platforms []*result.Platform + platforms []*result.PlatformSummary } func newSearchResult(in []*rpc.PlatformSummary) *searchResults { res := &searchResults{} for _, platformSummary := range in { - res.platforms = append(res.platforms, result.NewPlatformResult(platformSummary)) + res.platforms = append(res.platforms, result.NewPlatformSummary(platformSummary)) } return res } diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index 34b7700e410..a340d5d01ce 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -23,15 +23,15 @@ import ( semver "go.bug.st/relaxed-semver" ) -// NewPlatformResult creates a new result.Platform from rpc.PlatformSummary -func NewPlatformResult(in *rpc.PlatformSummary) *Platform { +// NewPlatformSummary creates a new result.Platform from rpc.PlatformSummary +func NewPlatformSummary(in *rpc.PlatformSummary) *PlatformSummary { releases := orderedmap.NewWithConversionFunc[*semver.Version, *PlatformRelease, string]((*semver.Version).String) for k, v := range in.Releases { releases.Set(semver.MustParse(k), NewPlatformReleaseResult(v)) } releases.SortKeys((*semver.Version).CompareTo) - return &Platform{ + return &PlatformSummary{ Id: in.Metadata.Id, Maintainer: in.Metadata.Maintainer, Website: in.Metadata.Website, @@ -45,8 +45,8 @@ func NewPlatformResult(in *rpc.PlatformSummary) *Platform { } } -// Platform maps a rpc.Platform -type Platform struct { +// PlatformSummary maps a rpc.PlatformSummary +type PlatformSummary struct { Id string `json:"id,omitempty"` Maintainer string `json:"maintainer,omitempty"` Website string `json:"website,omitempty"` @@ -62,12 +62,12 @@ type Platform struct { } // GetLatestRelease returns the latest relase of this platform or nil if none available. -func (p *Platform) GetLatestRelease() *PlatformRelease { +func (p *PlatformSummary) GetLatestRelease() *PlatformRelease { return p.Releases.Get(p.LatestVersion) } // GetInstalledRelease returns the installed relase of this platform or nil if none available. -func (p *Platform) GetInstalledRelease() *PlatformRelease { +func (p *PlatformSummary) GetInstalledRelease() *PlatformRelease { return p.Releases.Get(p.InstalledVersion) } diff --git a/internal/cli/outdated/outdated.go b/internal/cli/outdated/outdated.go index c80d768f0bf..782700e64ed 100644 --- a/internal/cli/outdated/outdated.go +++ b/internal/cli/outdated/outdated.go @@ -65,17 +65,17 @@ func Outdated(inst *rpc.Instance) { // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation type outdatedResult struct { - Platforms []*result.Platform `json:"platforms,omitempty"` + Platforms []*result.PlatformSummary `json:"platforms,omitempty"` InstalledLibs []*result.InstalledLibrary `json:"libraries,omitempty"` } func newOutdatedResult(inPlatforms []*rpc.PlatformSummary, inLibraries []*rpc.InstalledLibrary) *outdatedResult { res := &outdatedResult{ - Platforms: make([]*result.Platform, len(inPlatforms)), + Platforms: make([]*result.PlatformSummary, len(inPlatforms)), InstalledLibs: make([]*result.InstalledLibrary, len(inLibraries)), } for i, v := range inPlatforms { - res.Platforms[i] = result.NewPlatformResult(v) + res.Platforms[i] = result.NewPlatformSummary(v) } for i, v := range inLibraries { res.InstalledLibs[i] = result.NewInstalledLibraryResult(v) From 3aee471805a0bf8628268660f61beda6aa5d1555 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 16:13:31 +0200 Subject: [PATCH 06/24] fixup mapper BoardListAllRespnse --- internal/cli/feedback/result/rpc.go | 412 +++++++++++++++++++++++----- 1 file changed, 350 insertions(+), 62 deletions(-) diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index a340d5d01ce..2a66d763707 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -27,7 +27,7 @@ import ( func NewPlatformSummary(in *rpc.PlatformSummary) *PlatformSummary { releases := orderedmap.NewWithConversionFunc[*semver.Version, *PlatformRelease, string]((*semver.Version).String) for k, v := range in.Releases { - releases.Set(semver.MustParse(k), NewPlatformReleaseResult(v)) + releases.Set(semver.MustParse(k), NewPlatformRelease(v)) } releases.SortKeys((*semver.Version).CompareTo) @@ -71,8 +71,8 @@ func (p *PlatformSummary) GetInstalledRelease() *PlatformRelease { return p.Releases.Get(p.InstalledVersion) } -// NewPlatformReleaseResult creates a new result.PlatformRelease from rpc.PlatformRelease -func NewPlatformReleaseResult(in *rpc.PlatformRelease) *PlatformRelease { +// NewPlatformRelease creates a new result.PlatformRelease from rpc.PlatformRelease +func NewPlatformRelease(in *rpc.PlatformRelease) *PlatformRelease { var boards []*Board for _, board := range in.Boards { boards = append(boards, &Board{ @@ -160,6 +160,52 @@ type Library struct { InDevelopment bool `json:"in_development,omitempty"` } +func NewLibrary(l *rpc.Library) *Library { + if l == nil { + return nil + } + libraryPropsMap := orderedmap.New[string, string]() + for k, v := range l.GetProperties() { + libraryPropsMap.Set(k, v) + } + libraryPropsMap.SortStableKeys(cmp.Compare) + + libraryCompatibleWithMap := orderedmap.New[string, bool]() + for k, v := range l.GetCompatibleWith() { + libraryCompatibleWithMap.Set(k, v) + } + libraryCompatibleWithMap.SortStableKeys(cmp.Compare) + + return &Library{ + Name: l.GetName(), + Author: l.GetAuthor(), + Maintainer: l.GetMaintainer(), + Sentence: l.GetSentence(), + Paragraph: l.GetParagraph(), + Website: l.GetWebsite(), + Category: l.GetCategory(), + Architectures: l.GetArchitectures(), + Types: l.GetTypes(), + InstallDir: l.GetInstallDir(), + SourceDir: l.GetSourceDir(), + UtilityDir: l.GetUtilityDir(), + ContainerPlatform: l.GetContainerPlatform(), + DotALinkage: l.GetDotALinkage(), + Precompiled: l.GetPrecompiled(), + LdFlags: l.GetLdFlags(), + IsLegacy: l.GetIsLegacy(), + Version: l.GetVersion(), + License: l.GetLicense(), + Properties: libraryPropsMap, + Location: LibraryLocation(l.GetLocation().String()), + Layout: LibraryLayout(l.GetLayout().String()), + Examples: l.GetExamples(), + ProvidesIncludes: l.GetProvidesIncludes(), + CompatibleWith: libraryCompatibleWithMap, + InDevelopment: l.GetInDevelopment(), + } +} + type LibraryRelease struct { Author string `json:"author,omitempty"` Version string `json:"version,omitempty"` @@ -176,6 +222,27 @@ type LibraryRelease struct { Dependencies []*LibraryDependency `json:"dependencies,omitempty"` } +func NewLibraryRelease(l *rpc.LibraryRelease) *LibraryRelease { + if l == nil { + return nil + } + return &LibraryRelease{ + Author: l.GetAuthor(), + Version: l.GetVersion(), + Maintainer: l.GetMaintainer(), + Sentence: l.GetSentence(), + Paragraph: l.GetParagraph(), + Website: l.GetWebsite(), + Category: l.GetCategory(), + Architectures: l.GetArchitectures(), + Types: l.GetTypes(), + Resources: NewDownloadResource(l.GetResources()), + License: l.GetLicense(), + ProvidesIncludes: l.GetProvidesIncludes(), + Dependencies: NewLibraryDependencies(l.GetDependencies()), + } +} + type DownloadResource struct { Url string `json:"url,omitempty"` ArchiveFilename string `json:"archive_filename,omitempty"` @@ -189,63 +256,10 @@ type LibraryDependency struct { VersionConstraint string `json:"version_constraint,omitempty"` } -func NewInstalledLibraryResult(l *rpc.InstalledLibrary) *InstalledLibrary { - libraryPropsMap := orderedmap.New[string, string]() - for k, v := range l.GetLibrary().GetProperties() { - libraryPropsMap.Set(k, v) - } - libraryPropsMap.SortStableKeys(cmp.Compare) - - libraryCompatibleWithMap := orderedmap.New[string, bool]() - for k, v := range l.GetLibrary().GetCompatibleWith() { - libraryCompatibleWithMap.Set(k, v) - } - libraryCompatibleWithMap.SortStableKeys(cmp.Compare) - +func NewInstalledLibrary(l *rpc.InstalledLibrary) *InstalledLibrary { return &InstalledLibrary{ - Library: &Library{ - Name: l.GetLibrary().GetName(), - Author: l.GetLibrary().GetAuthor(), - Maintainer: l.GetLibrary().GetMaintainer(), - Sentence: l.GetLibrary().GetSentence(), - Paragraph: l.GetLibrary().GetParagraph(), - Website: l.GetLibrary().GetWebsite(), - Category: l.GetLibrary().GetCategory(), - Architectures: l.GetLibrary().GetArchitectures(), - Types: l.GetLibrary().GetTypes(), - InstallDir: l.GetLibrary().GetInstallDir(), - SourceDir: l.GetLibrary().GetSourceDir(), - UtilityDir: l.GetLibrary().GetUtilityDir(), - ContainerPlatform: l.GetLibrary().GetContainerPlatform(), - DotALinkage: l.GetLibrary().GetDotALinkage(), - Precompiled: l.GetLibrary().GetPrecompiled(), - LdFlags: l.GetLibrary().GetLdFlags(), - IsLegacy: l.GetLibrary().GetIsLegacy(), - Version: l.GetLibrary().GetVersion(), - License: l.GetLibrary().GetLicense(), - Properties: libraryPropsMap, - Location: LibraryLocation(l.GetLibrary().GetLocation().String()), - Layout: LibraryLayout(l.GetLibrary().GetLayout().String()), - Examples: l.GetLibrary().GetExamples(), - ProvidesIncludes: l.GetLibrary().GetProvidesIncludes(), - CompatibleWith: libraryCompatibleWithMap, - InDevelopment: l.GetLibrary().GetInDevelopment(), - }, - Release: &LibraryRelease{ - Author: l.GetRelease().GetAuthor(), - Version: l.GetRelease().GetVersion(), - Maintainer: l.GetRelease().GetMaintainer(), - Sentence: l.GetRelease().GetSentence(), - Paragraph: l.GetRelease().GetParagraph(), - Website: l.GetRelease().GetWebsite(), - Category: l.GetRelease().GetCategory(), - Architectures: l.GetRelease().GetArchitectures(), - Types: l.GetRelease().GetTypes(), - Resources: NewDownloadResource(l.GetRelease().GetResources()), - License: l.GetRelease().GetLicense(), - ProvidesIncludes: l.GetRelease().GetProvidesIncludes(), - Dependencies: NewLibraryDependencies(l.GetRelease().GetDependencies()), - }, + Library: NewLibrary(l.GetLibrary()), + Release: NewLibraryRelease(l.GetRelease()), } } @@ -293,14 +307,14 @@ type Port struct { } func NewPort(p *rpc.Port) *Port { + if p == nil { + return nil + } propertiesMap := orderedmap.New[string, string]() for k, v := range p.GetProperties() { propertiesMap.Set(k, v) } propertiesMap.SortStableKeys(cmp.Compare) - if p == nil { - return nil - } return &Port{ Address: p.GetAddress(), Label: p.GetLabel(), @@ -584,3 +598,277 @@ func NewBoardIndentificationProperty(p *rpc.BoardIdentificationProperties) *Boar return &BoardIdentificationProperties{Properties: propertiesMap} } + +type BoardListAllResponse struct { + Boards []*BoardListItem `json:"boards,omitempty"` +} + +func NewBoardListAllResponse(p *rpc.BoardListAllResponse) *BoardListAllResponse { + if p == nil { + return nil + } + boards := make([]*BoardListItem, len(p.GetBoards())) + for i, v := range p.GetBoards() { + boards[i] = NewBoardListItem(v) + } + return &BoardListAllResponse{Boards: boards} +} + +type BoardListItem struct { + Name string `json:"name,omitempty"` + Fqbn string `json:"fqbn,omitempty"` + IsHidden bool `json:"is_hidden,omitempty"` + Platform *Platform `json:"platform,omitempty"` +} + +func NewBoardListItem(b *rpc.BoardListItem) *BoardListItem { + if b == nil { + return nil + } + return &BoardListItem{ + Name: b.GetName(), + Fqbn: b.GetFqbn(), + IsHidden: b.GetIsHidden(), + Platform: NewPlatform(b.GetPlatform()), + } +} + +type Platform struct { + Metadata *PlatformMetadata `json:"metadata,omitempty"` + Release *PlatformRelease `json:"release,omitempty"` +} + +func NewPlatform(p *rpc.Platform) *Platform { + if p == nil { + return nil + } + return &Platform{ + Metadata: NewPlatformMetadata(p.GetMetadata()), + Release: NewPlatformRelease(p.GetRelease()), + } +} + +type PlatformMetadata struct { + Id string `json:"id,omitempty"` + Maintainer string `json:"maintainer,omitempty"` + Website string `json:"website,omitempty"` + Email string `json:"email,omitempty"` + ManuallyInstalled bool `json:"manually_installed,omitempty"` + Deprecated bool `json:"deprecated,omitempty"` + Indexed bool `json:"indexed,omitempty"` +} + +func NewPlatformMetadata(p *rpc.PlatformMetadata) *PlatformMetadata { + if p == nil { + return nil + } + return &PlatformMetadata{ + Id: p.Id, + Maintainer: p.Maintainer, + Website: p.Website, + Email: p.Email, + ManuallyInstalled: p.ManuallyInstalled, + Deprecated: p.Deprecated, + Indexed: p.Indexed, + } +} + +type DetectedPort struct { + MatchingBoards []*BoardListItem `json:"matching_boards,omitempty"` + Port *Port `json:"port,omitempty"` +} + +func NewDetectedPorts(p []*rpc.DetectedPort) []*DetectedPort { + if p == nil { + return nil + } + res := make([]*DetectedPort, len(p)) + for i, v := range p { + res[i] = NewDetectedPort(v) + } + return res +} + +func NewDetectedPort(p *rpc.DetectedPort) *DetectedPort { + if p == nil { + return nil + } + return &DetectedPort{ + MatchingBoards: NewBoardListItems(p.GetMatchingBoards()), + Port: NewPort(p.GetPort()), + } +} + +type LibraryResolveDependenciesResponse struct { + Dependencies []*LibraryDependencyStatus `json:"dependencies,omitempty"` +} + +func NewLibraryResolveDependenciesResponse(l *rpc.LibraryResolveDependenciesResponse) *LibraryResolveDependenciesResponse { + if l == nil { + return nil + } + dependencies := make([]*LibraryDependencyStatus, len(l.GetDependencies())) + for i, v := range l.GetDependencies() { + dependencies[i] = NewLibraryDependencyStatus(v) + } + return &LibraryResolveDependenciesResponse{Dependencies: dependencies} +} + +type LibraryDependencyStatus struct { + Name string `json:"name,omitempty"` + VersionRequired string `json:"version_required,omitempty"` + VersionInstalled string `json:"version_installed,omitempty"` +} + +func NewLibraryDependencyStatus(l *rpc.LibraryDependencyStatus) *LibraryDependencyStatus { + if l == nil { + return nil + } + return &LibraryDependencyStatus{ + Name: l.GetName(), + VersionRequired: l.GetVersionRequired(), + VersionInstalled: l.GetVersionInstalled(), + } +} + +type LibrarySearchStatus string + +type LibrarySearchResponse struct { + Libraries []*SearchedLibrary `json:"libraries,omitempty"` + Status LibrarySearchStatus `json:"status,omitempty"` +} + +func NewLibrarySearchResponse(l *rpc.LibrarySearchResponse) *LibrarySearchResponse { + if l == nil { + return nil + } + + searchedLibraries := make([]*SearchedLibrary, len(l.GetLibraries())) + for i, v := range l.GetLibraries() { + searchedLibraries[i] = NewSearchedLibrary(v) + } + + return &LibrarySearchResponse{ + Libraries: searchedLibraries, + Status: LibrarySearchStatus(l.GetStatus().Enum().String()), + } +} + +type SearchedLibrary struct { + Name string `json:"name,omitempty"` + Releases orderedmap.Map[string, *LibraryRelease] `json:"releases,omitempty"` + Latest *LibraryRelease `json:"latest,omitempty"` + AvailableVersions []string `json:"available_versions,omitempty"` +} + +func NewSearchedLibrary(l *rpc.SearchedLibrary) *SearchedLibrary { + if l == nil { + return nil + } + // TODO understand if we want semver keys + releasesMap := orderedmap.New[string, *LibraryRelease]() + for k, v := range l.GetReleases() { + releasesMap.Set(k, NewLibraryRelease(v)) + } + releasesMap.SortStableKeys(cmp.Compare) + + return &SearchedLibrary{ + Name: l.GetName(), + Releases: releasesMap, + Latest: NewLibraryRelease(l.GetLatest()), + AvailableVersions: l.GetAvailableVersions(), + } +} + +type MonitorPortSettingDescriptor struct { + SettingId string `json:"setting_id,omitempty"` + Label string `json:"label,omitempty"` + Type string `json:"type,omitempty"` + EnumValues []string `json:"enum_values,omitempty"` + Value string `json:"value,omitempty"` +} + +func NewMonitorPortSettingDescriptor(m *rpc.MonitorPortSettingDescriptor) *MonitorPortSettingDescriptor { + if m == nil { + return nil + } + return &MonitorPortSettingDescriptor{ + SettingId: m.GetSettingId(), + Label: m.GetLabel(), + Type: m.GetType(), + EnumValues: m.GetEnumValues(), + Value: m.GetValue(), + } +} + +type CompileResponse struct { + OutStream []byte `json:"out_stream,omitempty"` + ErrStream []byte `json:"err_stream,omitempty"` + BuildPath string `json:"build_path,omitempty"` + UsedLibraries []*Library `json:"used_libraries,omitempty"` + ExecutableSectionsSize []*ExecutableSectionSize `json:"executable_sections_size,omitempty"` + BoardPlatform *InstalledPlatformReference `json:"board_platform,omitempty"` + BuildPlatform *InstalledPlatformReference `json:"build_platform,omitempty"` + BuildProperties []string `json:"build_properties,omitempty"` +} + +func NewCompileResponse(c *rpc.CompileResponse) *CompileResponse { + if c == nil { + return nil + } + usedLibs := make([]*Library, len(c.GetUsedLibraries())) + for i, v := range c.GetUsedLibraries() { + usedLibs[i] = NewLibrary(v) + } + executableSectionsSizes := make([]*ExecutableSectionSize, len(c.GetExecutableSectionsSize())) + for i, v := range c.GetExecutableSectionsSize() { + executableSectionsSizes[i] = NewExecutableSectionSize(v) + } + + return &CompileResponse{ + OutStream: c.GetOutStream(), + ErrStream: c.GetErrStream(), + BuildPath: c.GetBuildPath(), + UsedLibraries: usedLibs, + ExecutableSectionsSize: executableSectionsSizes, + BoardPlatform: &InstalledPlatformReference{}, + BuildPlatform: &InstalledPlatformReference{}, + BuildProperties: c.GetBuildProperties(), + } +} + +type ExecutableSectionSize struct { + Name string `json:"name,omitempty"` + Size int64 `json:"size,omitempty"` + MaxSize int64 `json:"max_size,omitempty"` +} + +func NewExecutableSectionSize(s *rpc.ExecutableSectionSize) *ExecutableSectionSize { + if s == nil { + return nil + } + return &ExecutableSectionSize{ + Name: s.GetName(), + Size: s.GetSize(), + MaxSize: s.GetMaxSize(), + } +} + +type InstalledPlatformReference struct { + Id string `json:"id,omitempty"` + Version string `json:"version,omitempty"` + InstallDir string `json:"install_dir,omitempty"` + PackageUrl string `json:"package_url,omitempty"` +} + +func NewInstalledPlatformReference(r *rpc.InstalledPlatformReference) *InstalledPlatformReference { + if r == nil { + return nil + } + return &InstalledPlatformReference{ + Id: r.GetId(), + Version: r.GetVersion(), + InstallDir: r.GetInstallDir(), + PackageUrl: r.GetPackageUrl(), + } +} From dcf7f86ddea7a5724b500295b00cd6019f90eea6 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 16:18:16 +0200 Subject: [PATCH 07/24] cli: board listall use feedback result structs --- internal/cli/board/listall.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/internal/cli/board/listall.go b/internal/cli/board/listall.go index 0f47cba710a..fd894c127f8 100644 --- a/internal/cli/board/listall.go +++ b/internal/cli/board/listall.go @@ -23,6 +23,7 @@ import ( "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/internal/cli/feedback" + fResult "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/table" @@ -63,13 +64,13 @@ func runListAllCommand(cmd *cobra.Command, args []string) { feedback.Fatal(tr("Error listing boards: %v", err), feedback.ErrGeneric) } - feedback.PrintResult(resultAll{list}) + feedback.PrintResult(resultAll{fResult.NewBoardListAllResponse(list)}) } // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation type resultAll struct { - list *rpc.BoardListAllResponse + list *fResult.BoardListAllResponse } func (dr resultAll) Data() interface{} { @@ -77,18 +78,26 @@ func (dr resultAll) Data() interface{} { } func (dr resultAll) String() string { + t := table.New() + t.SetHeader(tr("Board Name"), tr("FQBN"), "") + + if dr.list == nil || len(dr.list.Boards) == 0 { + return t.Render() + } + sort.Slice(dr.list.Boards, func(i, j int) bool { - return dr.list.Boards[i].GetName() < dr.list.Boards[j].GetName() + return dr.list.Boards[i].Name < dr.list.Boards[j].Name }) - t := table.New() - t.SetHeader(tr("Board Name"), tr("FQBN"), "") - for _, item := range dr.list.GetBoards() { + for _, item := range dr.list.Boards { + if item == nil { + continue + } hidden := "" if item.IsHidden { hidden = tr("(hidden)") } - t.AddRow(item.GetName(), item.GetFqbn(), hidden) + t.AddRow(item.Name, item.Fqbn, hidden) } return t.Render() } From cdc80bf1d1a4673603db39bbb5a1f0c7eb8d98cf Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 16:23:39 +0200 Subject: [PATCH 08/24] cli: board search use feedback result structs --- internal/cli/board/search.go | 21 +++++++++++++++------ internal/cli/feedback/result/rpc.go | 11 +++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/internal/cli/board/search.go b/internal/cli/board/search.go index efa3f945662..2ef73e79b7a 100644 --- a/internal/cli/board/search.go +++ b/internal/cli/board/search.go @@ -24,6 +24,7 @@ import ( "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/internal/cli/feedback" + fResult "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/table" @@ -60,13 +61,13 @@ func runSearchCommand(cmd *cobra.Command, args []string) { feedback.Fatal(tr("Error searching boards: %v", err), feedback.ErrGeneric) } - feedback.PrintResult(searchResults{res.Boards}) + feedback.PrintResult(searchResults{fResult.NewBoardListItems(res.Boards)}) } // output from this command requires special formatting so we create a dedicated // feedback.Result implementation type searchResults struct { - boards []*rpc.BoardListItem + boards []*fResult.BoardListItem } func (r searchResults) Data() interface{} { @@ -74,18 +75,26 @@ func (r searchResults) Data() interface{} { } func (r searchResults) String() string { + t := table.New() + t.SetHeader(tr("Board Name"), tr("FQBN"), tr("Platform ID"), "") + + if len(r.boards) == 0 { + return t.Render() + } + sort.Slice(r.boards, func(i, j int) bool { - return r.boards[i].GetName() < r.boards[j].GetName() + return r.boards[i].Name < r.boards[j].Name }) - t := table.New() - t.SetHeader(tr("Board Name"), tr("FQBN"), tr("Platform ID"), "") for _, item := range r.boards { + if item == nil { + continue + } hidden := "" if item.IsHidden { hidden = tr("(hidden)") } - t.AddRow(item.GetName(), item.GetFqbn(), item.Platform.Metadata.Id, hidden) + t.AddRow(item.Name, item.Fqbn, item.Platform.Metadata.Id, hidden) } return t.Render() } diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index 2a66d763707..b9672eaada1 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -621,6 +621,17 @@ type BoardListItem struct { Platform *Platform `json:"platform,omitempty"` } +func NewBoardListItems(b []*rpc.BoardListItem) []*BoardListItem { + if b == nil { + return nil + } + res := make([]*BoardListItem, len(b)) + for i, v := range b { + res[i] = NewBoardListItem(v) + } + return res +} + func NewBoardListItem(b *rpc.BoardListItem) *BoardListItem { if b == nil { return nil From 60abc6aa740679b0b62c93083c2e23e47164b2e2 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 16:33:42 +0200 Subject: [PATCH 09/24] cli: board list use feedback result structs --- internal/cli/board/list.go | 63 ++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/internal/cli/board/list.go b/internal/cli/board/list.go index 65b0bd5e04d..b8612f1dce5 100644 --- a/internal/cli/board/list.go +++ b/internal/cli/board/list.go @@ -27,6 +27,7 @@ import ( "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/table" @@ -81,7 +82,8 @@ func runListCommand(watch bool, timeout int64, fqbn string) { for _, err := range discoveryErrors { feedback.Warning(tr("Error starting discovery: %v", err)) } - feedback.PrintResult(result{ports}) + + feedback.PrintResult(listResult{result.NewDetectedPorts(ports)}) } func watchList(inst *rpc.Instance) { @@ -98,10 +100,10 @@ func watchList(inst *rpc.Instance) { } for event := range eventsChan { - feedback.PrintResult(watchEvent{ + feedback.PrintResult(watchEventResult{ Type: event.EventType, - Boards: event.Port.MatchingBoards, - Port: event.Port.Port, + Boards: result.NewBoardListItems(event.Port.MatchingBoards), + Port: result.NewPort(event.Port.Port), Error: event.Error, }) } @@ -109,47 +111,50 @@ func watchList(inst *rpc.Instance) { // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation -type result struct { - ports []*rpc.DetectedPort +type listResult struct { + ports []*result.DetectedPort } -func (dr result) Data() interface{} { +func (dr listResult) Data() interface{} { return dr.ports } -func (dr result) String() string { +func (dr listResult) String() string { if len(dr.ports) == 0 { return tr("No boards found.") } sort.Slice(dr.ports, func(i, j int) bool { x, y := dr.ports[i].Port, dr.ports[j].Port - return x.GetProtocol() < y.GetProtocol() || - (x.GetProtocol() == y.GetProtocol() && x.GetAddress() < y.GetAddress()) + return x.Protocol < y.Protocol || + (x.Protocol == y.Protocol && x.Address < y.Address) }) t := table.New() t.SetHeader(tr("Port"), tr("Protocol"), tr("Type"), tr("Board Name"), tr("FQBN"), tr("Core")) for _, detectedPort := range dr.ports { + if detectedPort == nil { + continue + } port := detectedPort.Port - protocol := port.GetProtocol() - address := port.GetAddress() - if port.GetProtocol() == "serial" { - address = port.GetAddress() + protocol := port.Protocol + address := port.Address + if port.Protocol == "serial" { + address = port.Address } - protocolLabel := port.GetProtocolLabel() - if boards := detectedPort.GetMatchingBoards(); len(boards) > 0 { + protocolLabel := port.ProtocolLabel + if boards := detectedPort.MatchingBoards; len(boards) > 0 { sort.Slice(boards, func(i, j int) bool { x, y := boards[i], boards[j] - return x.GetName() < y.GetName() || (x.GetName() == y.GetName() && x.GetFqbn() < y.GetFqbn()) + return x.Name < y.Name || (x.Name == y.Name && x.Fqbn < y.Fqbn) }) for _, b := range boards { - board := b.GetName() + board := b.Name // to improve the user experience, show on a dedicated column // the name of the core supporting the board detected var coreName = "" - fqbn, err := cores.ParseFQBN(b.GetFqbn()) + fqbn, err := cores.ParseFQBN(b.Fqbn) if err == nil { coreName = fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch) } @@ -170,18 +175,18 @@ func (dr result) String() string { return t.Render() } -type watchEvent struct { - Type string `json:"eventType"` - Boards []*rpc.BoardListItem `json:"matching_boards,omitempty"` - Port *rpc.Port `json:"port,omitempty"` - Error string `json:"error,omitempty"` +type watchEventResult struct { + Type string `json:"eventType"` + Boards []*result.BoardListItem `json:"matching_boards,omitempty"` + Port *result.Port `json:"port,omitempty"` + Error string `json:"error,omitempty"` } -func (dr watchEvent) Data() interface{} { +func (dr watchEventResult) Data() interface{} { return dr } -func (dr watchEvent) String() string { +func (dr watchEventResult) String() string { t := table.New() event := map[string]string{ @@ -197,15 +202,15 @@ func (dr watchEvent) String() string { if boards := dr.Boards; len(boards) > 0 { sort.Slice(boards, func(i, j int) bool { x, y := boards[i], boards[j] - return x.GetName() < y.GetName() || (x.GetName() == y.GetName() && x.GetFqbn() < y.GetFqbn()) + return x.Name < y.Name || (x.Name == y.Name && x.Fqbn < y.Fqbn) }) for _, b := range boards { - board := b.GetName() + board := b.Name // to improve the user experience, show on a dedicated column // the name of the core supporting the board detected var coreName = "" - fqbn, err := cores.ParseFQBN(b.GetFqbn()) + fqbn, err := cores.ParseFQBN(b.Fqbn) if err == nil { coreName = fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch) } From 1534ab9eb4c6d228c4d3a8b156c67a0d8086e3ff Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 16:48:47 +0200 Subject: [PATCH 10/24] cli: lib deps use feedback result structs --- internal/cli/lib/check_deps.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/internal/cli/lib/check_deps.go b/internal/cli/lib/check_deps.go index b286a2c85a5..9a5666cd101 100644 --- a/internal/cli/lib/check_deps.go +++ b/internal/cli/lib/check_deps.go @@ -24,6 +24,7 @@ import ( "github.com/arduino/arduino-cli/commands/lib" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" + fResult "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/fatih/color" @@ -65,13 +66,13 @@ func runDepsCommand(cmd *cobra.Command, args []string) { feedback.Fatal(tr("Error resolving dependencies for %[1]s: %[2]s", libRef, err), feedback.ErrGeneric) } - feedback.PrintResult(&checkDepResult{deps: deps}) + feedback.PrintResult(&checkDepResult{deps: fResult.NewLibraryResolveDependenciesResponse(deps)}) } // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation type checkDepResult struct { - deps *rpc.LibraryResolveDependenciesResponse + deps *fResult.LibraryResolveDependenciesResponse } func (dr checkDepResult) Data() interface{} { @@ -79,6 +80,9 @@ func (dr checkDepResult) Data() interface{} { } func (dr checkDepResult) String() string { + if dr.deps == nil || dr.deps.Dependencies == nil { + return "" + } res := "" deps := dr.deps.Dependencies @@ -91,26 +95,29 @@ func (dr checkDepResult) String() string { }) for _, dep := range deps { + if dep == nil { + continue + } res += outputDep(dep) } return res } -func outputDep(dep *rpc.LibraryDependencyStatus) string { +func outputDep(dep *fResult.LibraryDependencyStatus) string { res := "" green := color.New(color.FgGreen) red := color.New(color.FgRed) yellow := color.New(color.FgYellow) - if dep.GetVersionInstalled() == "" { + if dep.VersionInstalled == "" { res += tr("%s must be installed.", - red.Sprintf("✕ %s %s", dep.GetName(), dep.GetVersionRequired())) - } else if dep.GetVersionInstalled() == dep.GetVersionRequired() { + red.Sprintf("✕ %s %s", dep.Name, dep.VersionRequired)) + } else if dep.VersionInstalled == dep.VersionRequired { res += tr("%s is already installed.", - green.Sprintf("✓ %s %s", dep.GetName(), dep.GetVersionRequired())) + green.Sprintf("✓ %s %s", dep.Name, dep.VersionRequired)) } else { res += tr("%[1]s is required but %[2]s is currently installed.", - yellow.Sprintf("✕ %s %s", dep.GetName(), dep.GetVersionRequired()), - yellow.Sprintf("%s", dep.GetVersionInstalled())) + yellow.Sprintf("✕ %s %s", dep.Name, dep.VersionRequired), + yellow.Sprintf("%s", dep.VersionInstalled)) } res += "\n" return res From 5fd453914f403973bed00c7ad878623ce784fe1b Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 17:12:16 +0200 Subject: [PATCH 11/24] cli: lib search use feedback result structs --- internal/cli/feedback/result/rpc.go | 16 +++++----- internal/cli/lib/search.go | 47 +++++++++++++++++------------ 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index b9672eaada1..96c907ed9a1 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -766,23 +766,21 @@ func NewLibrarySearchResponse(l *rpc.LibrarySearchResponse) *LibrarySearchRespon } type SearchedLibrary struct { - Name string `json:"name,omitempty"` - Releases orderedmap.Map[string, *LibraryRelease] `json:"releases,omitempty"` - Latest *LibraryRelease `json:"latest,omitempty"` - AvailableVersions []string `json:"available_versions,omitempty"` + Name string `json:"name,omitempty"` + Releases orderedmap.Map[*semver.Version, *LibraryRelease] `json:"releases,omitempty"` + Latest *LibraryRelease `json:"latest,omitempty"` + AvailableVersions []string `json:"available_versions,omitempty"` } func NewSearchedLibrary(l *rpc.SearchedLibrary) *SearchedLibrary { if l == nil { return nil } - // TODO understand if we want semver keys - releasesMap := orderedmap.New[string, *LibraryRelease]() + releasesMap := orderedmap.NewWithConversionFunc[*semver.Version, *LibraryRelease, string]((*semver.Version).String) for k, v := range l.GetReleases() { - releasesMap.Set(k, NewLibraryRelease(v)) + releasesMap.Set(semver.MustParse(k), NewLibraryRelease(v)) } - releasesMap.SortStableKeys(cmp.Compare) - + releasesMap.SortKeys((*semver.Version).CompareTo) return &SearchedLibrary{ Name: l.GetName(), Releases: releasesMap, diff --git a/internal/cli/lib/search.go b/internal/cli/lib/search.go index 9af3c7c1079..d5d1ba676fb 100644 --- a/internal/cli/lib/search.go +++ b/internal/cli/lib/search.go @@ -26,6 +26,7 @@ import ( "github.com/arduino/arduino-cli/commands/lib" "github.com/arduino/arduino-cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" @@ -128,8 +129,8 @@ func runSearchCommand(args []string, namesOnly bool, omitReleasesDetails bool) { feedback.Fatal(tr("Error searching for Libraries: %v", err), feedback.ErrGeneric) } - feedback.PrintResult(result{ - results: searchResp, + feedback.PrintResult(librarySearchResult{ + results: result.NewLibrarySearchResponse(searchResp), namesOnly: namesOnly, }) @@ -138,12 +139,12 @@ func runSearchCommand(args []string, namesOnly bool, omitReleasesDetails bool) { // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation -type result struct { - results *rpc.LibrarySearchResponse +type librarySearchResult struct { + results *result.LibrarySearchResponse namesOnly bool } -func (res result) Data() interface{} { +func (res librarySearchResult) Data() interface{} { if res.namesOnly { type LibName struct { Name string `json:"name"` @@ -154,33 +155,36 @@ func (res result) Data() interface{} { } names := []LibName{} - results := res.results.GetLibraries() - for _, lib := range results { + for _, lib := range res.results.Libraries { + if lib == nil { + continue + } names = append(names, LibName{lib.Name}) } - return NamesOnly{ - names, - } + return NamesOnly{names} } return res.results } -func (res result) String() string { - results := res.results.GetLibraries() +func (res librarySearchResult) String() string { + results := res.results.Libraries if len(results) == 0 { return tr("No libraries matching your search.") } var out strings.Builder - if res.results.GetStatus() == rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_FAILED { + if string(res.results.Status) == rpc.LibrarySearchStatus_name[int32(rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_FAILED)] { out.WriteString(tr("No libraries matching your search.\nDid you mean...\n")) } for _, lib := range results { - if res.results.GetStatus() == rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_SUCCESS { + if lib == nil { + continue + } + if string(res.results.Status) == rpc.LibrarySearchStatus_name[int32(rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_SUCCESS)] { out.WriteString(tr(`Name: "%s"`, lib.Name) + "\n") if res.namesOnly { continue @@ -190,14 +194,17 @@ func (res result) String() string { continue } - latest := lib.GetLatest() + latest := lib.Latest deps := []string{} - for _, dep := range latest.GetDependencies() { - if dep.GetVersionConstraint() == "" { - deps = append(deps, dep.GetName()) + for _, dep := range latest.Dependencies { + if dep == nil { + continue + } + if dep.VersionConstraint == "" { + deps = append(deps, dep.Name) } else { - deps = append(deps, dep.GetName()+" ("+dep.GetVersionConstraint()+")") + deps = append(deps, dep.Name+" ("+dep.VersionConstraint+")") } } @@ -212,7 +219,7 @@ func (res result) String() string { out.WriteString(fmt.Sprintf(" "+tr("Category: %s")+"\n", latest.Category)) out.WriteString(fmt.Sprintf(" "+tr("Architecture: %s")+"\n", strings.Join(latest.Architectures, ", "))) out.WriteString(fmt.Sprintf(" "+tr("Types: %s")+"\n", strings.Join(latest.Types, ", "))) - out.WriteString(fmt.Sprintf(" "+tr("Versions: %s")+"\n", strings.ReplaceAll(fmt.Sprint(lib.GetAvailableVersions()), " ", ", "))) + out.WriteString(fmt.Sprintf(" "+tr("Versions: %s")+"\n", strings.ReplaceAll(fmt.Sprint(lib.AvailableVersions), " ", ", "))) if len(latest.ProvidesIncludes) > 0 { out.WriteString(fmt.Sprintf(" "+tr("Provides includes: %s")+"\n", strings.Join(latest.ProvidesIncludes, ", "))) } From c93f4bc9e04b10dd5bd118b22df12e45ce6cc287 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 17:20:11 +0200 Subject: [PATCH 12/24] cli: lib examples use feedback result structs --- internal/cli/lib/examples.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/internal/cli/lib/examples.go b/internal/cli/lib/examples.go index a6564ec6378..8a8daf52938 100644 --- a/internal/cli/lib/examples.go +++ b/internal/cli/lib/examples.go @@ -25,6 +25,7 @@ import ( "github.com/arduino/arduino-cli/commands/lib" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" @@ -75,7 +76,7 @@ func runExamplesCommand(cmd *cobra.Command, args []string) { found := []*libraryExamples{} for _, lib := range res.GetInstalledLibraries() { found = append(found, &libraryExamples{ - Library: lib.Library, + Library: result.NewLibrary(lib.Library), Examples: lib.Library.Examples, }) } @@ -88,8 +89,8 @@ func runExamplesCommand(cmd *cobra.Command, args []string) { // feedback.Result implementation type libraryExamples struct { - Library *rpc.Library `json:"library"` - Examples []string `json:"examples"` + Library *result.Library `json:"library"` + Examples []string `json:"examples"` } type libraryExamplesResult struct { @@ -113,9 +114,9 @@ func (ir libraryExamplesResult) String() string { for _, lib := range ir.Examples { name := lib.Library.Name if lib.Library.ContainerPlatform != "" { - name += " (" + lib.Library.GetContainerPlatform() + ")" - } else if lib.Library.Location != rpc.LibraryLocation_LIBRARY_LOCATION_USER { - name += " (" + lib.Library.GetLocation().String() + ")" + name += " (" + lib.Library.ContainerPlatform + ")" + } else if string(lib.Library.Location) != rpc.LibraryLocation_name[int32(rpc.LibraryLocation_LIBRARY_LOCATION_USER)] { + name += " (" + string(lib.Library.Location) + ")" } r := tr("Examples for library %s", color.GreenString("%s", name)) + "\n" sort.Slice(lib.Examples, func(i, j int) bool { From e18ec5fec5c75cc258c7d652529b4a447dc6e095 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 17:25:03 +0200 Subject: [PATCH 13/24] cli: lib list use feedback result structs --- internal/cli/lib/list.go | 23 ++++++++++++++++------- internal/cli/outdated/outdated.go | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/internal/cli/lib/list.go b/internal/cli/lib/list.go index ea644367db5..5059551b6db 100644 --- a/internal/cli/lib/list.go +++ b/internal/cli/lib/list.go @@ -24,6 +24,7 @@ import ( "github.com/arduino/arduino-cli/commands/lib" "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/table" @@ -59,9 +60,14 @@ not listed, they can be listed by adding the --all flag.`), // List gets and prints a list of installed libraries. func List(instance *rpc.Instance, args []string, all bool, updatable bool) { installedLibs := GetList(instance, args, all, updatable) + + installedLibsResult := make([]*result.InstalledLibrary, len(installedLibs)) + for i, v := range installedLibs { + installedLibsResult[i] = result.NewInstalledLibrary(v) + } feedback.PrintResult(installedResult{ onlyUpdates: updatable, - installedLibs: installedLibs, + installedLibs: installedLibsResult, }) logrus.Info("Done") } @@ -113,7 +119,7 @@ func GetList( // feedback.Result implementation type installedResult struct { onlyUpdates bool - installedLibs []*rpc.InstalledLibrary + installedLibs []*result.InstalledLibrary } func (ir installedResult) Data() interface{} { @@ -140,7 +146,10 @@ func (ir installedResult) String() string { lastName := "" for _, libMeta := range ir.installedLibs { - lib := libMeta.GetLibrary() + if libMeta == nil { + continue + } + lib := libMeta.Library name := lib.Name if name == lastName { name = ` "` @@ -148,15 +157,15 @@ func (ir installedResult) String() string { lastName = name } - location := lib.GetLocation().String() + location := string(lib.Location) if lib.ContainerPlatform != "" { - location = lib.GetContainerPlatform() + location = lib.ContainerPlatform } available := "" sentence := "" - if libMeta.GetRelease() != nil { - available = libMeta.GetRelease().GetVersion() + if libMeta.Release != nil { + available = libMeta.Release.Version sentence = lib.Sentence } diff --git a/internal/cli/outdated/outdated.go b/internal/cli/outdated/outdated.go index 782700e64ed..39cba04481c 100644 --- a/internal/cli/outdated/outdated.go +++ b/internal/cli/outdated/outdated.go @@ -78,7 +78,7 @@ func newOutdatedResult(inPlatforms []*rpc.PlatformSummary, inLibraries []*rpc.In res.Platforms[i] = result.NewPlatformSummary(v) } for i, v := range inLibraries { - res.InstalledLibs[i] = result.NewInstalledLibraryResult(v) + res.InstalledLibs[i] = result.NewInstalledLibrary(v) } return res } From ce31a9b0eab420fdc446548400e13335c24c5eee Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 17:32:00 +0200 Subject: [PATCH 14/24] cli: monitor use feedback result structs --- internal/cli/monitor/monitor.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/internal/cli/monitor/monitor.go b/internal/cli/monitor/monitor.go index 009897e8bdb..93ab6668946 100644 --- a/internal/cli/monitor/monitor.go +++ b/internal/cli/monitor/monitor.go @@ -32,6 +32,7 @@ import ( "github.com/arduino/arduino-cli/i18n" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/table" @@ -154,7 +155,11 @@ func runMonitorCmd( feedback.Fatal(tr("Error getting port settings details: %s", err), feedback.ErrGeneric) } if describe { - feedback.PrintResult(&detailsResult{Settings: enumerateResp.Settings}) + settings := make([]*result.MonitorPortSettingDescriptor, len(enumerateResp.Settings)) + for i, v := range enumerateResp.Settings { + settings[i] = result.NewMonitorPortSettingDescriptor(v) + } + feedback.PrintResult(&detailsResult{Settings: settings}) return } @@ -277,7 +282,7 @@ func (cd *charDetectorWriter) Write(buf []byte) (int, error) { } type detailsResult struct { - Settings []*rpc.MonitorPortSettingDescriptor `json:"settings"` + Settings []*result.MonitorPortSettingDescriptor `json:"settings"` } func (r *detailsResult) Data() interface{} { @@ -286,8 +291,13 @@ func (r *detailsResult) Data() interface{} { func (r *detailsResult) String() string { t := table.New() - green := color.New(color.FgGreen) t.SetHeader(tr("ID"), tr("Setting"), tr("Default"), tr("Values")) + + if len(r.Settings) == 0 { + return t.Render() + } + + green := color.New(color.FgGreen) sort.Slice(r.Settings, func(i, j int) bool { return r.Settings[i].Label < r.Settings[j].Label }) From 429f52548e83a8c2c9860f5391c07720e3c02473 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Wed, 25 Oct 2023 17:54:56 +0200 Subject: [PATCH 15/24] cli: compile use feedback result structs --- internal/cli/compile/compile.go | 64 +++++++++++++++++------------ internal/cli/feedback/result/rpc.go | 4 +- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index a26a90eceb6..9f0c1015437 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -33,6 +33,7 @@ import ( "github.com/arduino/arduino-cli/i18n" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/table" @@ -340,10 +341,14 @@ func runCompileCommand(cmd *cobra.Command, args []string) { stdIO := stdIORes() res := &compileResult{ - CompilerOut: stdIO.Stdout, - CompilerErr: stdIO.Stderr, - BuilderResult: compileRes, - UploadResult: uploadRes, + CompilerOut: stdIO.Stdout, + CompilerErr: stdIO.Stderr, + BuilderResult: result.NewCompileResponse(compileRes), + UploadResult: struct { + UpdatedUploadPort *result.Port `json:"updated_upload_port,omitempty"` + }{ + UpdatedUploadPort: result.NewPort(uploadRes.GetUpdatedUploadPort()), + }, ProfileOut: profileOut, Success: compileError == nil, showPropertiesMode: showProperties, @@ -385,13 +390,15 @@ func runCompileCommand(cmd *cobra.Command, args []string) { } type compileResult struct { - CompilerOut string `json:"compiler_out"` - CompilerErr string `json:"compiler_err"` - BuilderResult *rpc.CompileResponse `json:"builder_result"` - UploadResult *rpc.UploadResult `json:"upload_result"` - Success bool `json:"success"` - ProfileOut string `json:"profile_out,omitempty"` - Error string `json:"error,omitempty"` + CompilerOut string `json:"compiler_out"` + CompilerErr string `json:"compiler_err"` + BuilderResult *result.CompileResponse `json:"builder_result"` + UploadResult struct { + UpdatedUploadPort *result.Port `json:"updated_upload_port,omitempty"` + } `json:"upload_result"` + Success bool `json:"success"` + ProfileOut string `json:"profile_out,omitempty"` + Error string `json:"error,omitempty"` showPropertiesMode arguments.ShowPropertiesMode hideStats bool @@ -402,8 +409,8 @@ func (r *compileResult) Data() interface{} { } func (r *compileResult) String() string { - if r.showPropertiesMode != arguments.ShowPropertiesDisabled { - return strings.Join(r.BuilderResult.GetBuildProperties(), fmt.Sprintln()) + if r.BuilderResult != nil && r.showPropertiesMode != arguments.ShowPropertiesDisabled { + return strings.Join(r.BuilderResult.BuildProperties, fmt.Sprintln()) } if r.hideStats { @@ -419,38 +426,41 @@ func (r *compileResult) String() string { if r.CompilerOut != "" || r.CompilerErr != "" { res += fmt.Sprintln() } - if len(build.GetUsedLibraries()) > 0 { + if build != nil && len(build.UsedLibraries) > 0 { libraries := table.New() libraries.SetHeader( table.NewCell(tr("Used library"), titleColor), table.NewCell(tr("Version"), titleColor), table.NewCell(tr("Path"), pathColor)) - for _, l := range build.GetUsedLibraries() { + for _, l := range build.UsedLibraries { + if l == nil { + continue + } libraries.AddRow( - table.NewCell(l.GetName(), nameColor), - l.GetVersion(), - table.NewCell(l.GetInstallDir(), pathColor)) + table.NewCell(l.Name, nameColor), + l.Version, + table.NewCell(l.InstallDir, pathColor)) } res += fmt.Sprintln(libraries.Render()) } - - if boardPlatform := build.GetBoardPlatform(); boardPlatform != nil { + if build != nil && build.BoardPlatform != nil { + boardPlatform := build.BoardPlatform platforms := table.New() platforms.SetHeader( table.NewCell(tr("Used platform"), titleColor), table.NewCell(tr("Version"), titleColor), table.NewCell(tr("Path"), pathColor)) platforms.AddRow( - table.NewCell(boardPlatform.GetId(), nameColor), - boardPlatform.GetVersion(), - table.NewCell(boardPlatform.GetInstallDir(), pathColor)) - if buildPlatform := build.GetBuildPlatform(); buildPlatform != nil && + table.NewCell(boardPlatform.Id, nameColor), + boardPlatform.Version, + table.NewCell(boardPlatform.InstallDir, pathColor)) + if buildPlatform := build.BuildPlatform; buildPlatform != nil && buildPlatform.Id != boardPlatform.Id && buildPlatform.Version != boardPlatform.Version { platforms.AddRow( - table.NewCell(buildPlatform.GetId(), nameColor), - buildPlatform.GetVersion(), - table.NewCell(buildPlatform.GetInstallDir(), pathColor)) + table.NewCell(buildPlatform.Id, nameColor), + buildPlatform.Version, + table.NewCell(buildPlatform.InstallDir, pathColor)) } res += fmt.Sprintln(platforms.Render()) } diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index 96c907ed9a1..dea4f00afcc 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -840,8 +840,8 @@ func NewCompileResponse(c *rpc.CompileResponse) *CompileResponse { BuildPath: c.GetBuildPath(), UsedLibraries: usedLibs, ExecutableSectionsSize: executableSectionsSizes, - BoardPlatform: &InstalledPlatformReference{}, - BuildPlatform: &InstalledPlatformReference{}, + BoardPlatform: NewInstalledPlatformReference(c.GetBoardPlatform()), + BuildPlatform: NewInstalledPlatformReference(c.GetBuildPlatform()), BuildProperties: c.GetBuildProperties(), } } From a16ba4d207805f78b451fcc480da8c883548bd2d Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Thu, 26 Oct 2023 18:26:30 +0200 Subject: [PATCH 16/24] add tests --- internal/cli/feedback/result/rpc.go | 27 +-- internal/cli/feedback/result/rpc_test.go | 204 +++++++++++++++++++++++ 2 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 internal/cli/feedback/result/rpc_test.go diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index dea4f00afcc..d43576daf5b 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -25,23 +25,27 @@ import ( // NewPlatformSummary creates a new result.Platform from rpc.PlatformSummary func NewPlatformSummary(in *rpc.PlatformSummary) *PlatformSummary { + if in == nil { + return nil + } + releases := orderedmap.NewWithConversionFunc[*semver.Version, *PlatformRelease, string]((*semver.Version).String) - for k, v := range in.Releases { + for k, v := range in.GetReleases() { releases.Set(semver.MustParse(k), NewPlatformRelease(v)) } releases.SortKeys((*semver.Version).CompareTo) return &PlatformSummary{ - Id: in.Metadata.Id, - Maintainer: in.Metadata.Maintainer, - Website: in.Metadata.Website, - Email: in.Metadata.Email, - ManuallyInstalled: in.Metadata.ManuallyInstalled, - Deprecated: in.Metadata.Deprecated, - Indexed: in.Metadata.Indexed, + Id: in.GetMetadata().GetId(), + Maintainer: in.GetMetadata().GetMaintainer(), + Website: in.GetMetadata().GetWebsite(), + Email: in.GetMetadata().GetEmail(), + ManuallyInstalled: in.GetMetadata().GetManuallyInstalled(), + Deprecated: in.GetMetadata().GetDeprecated(), + Indexed: in.GetMetadata().GetIndexed(), Releases: releases, - InstalledVersion: semver.MustParse(in.InstalledVersion), - LatestVersion: semver.MustParse(in.LatestVersion), + InstalledVersion: semver.MustParse(in.GetInstalledVersion()), + LatestVersion: semver.MustParse(in.GetLatestVersion()), } } @@ -73,6 +77,9 @@ func (p *PlatformSummary) GetInstalledRelease() *PlatformRelease { // NewPlatformRelease creates a new result.PlatformRelease from rpc.PlatformRelease func NewPlatformRelease(in *rpc.PlatformRelease) *PlatformRelease { + if in == nil { + return nil + } var boards []*Board for _, board := range in.Boards { boards = append(boards, &Board{ diff --git a/internal/cli/feedback/result/rpc_test.go b/internal/cli/feedback/result/rpc_test.go new file mode 100644 index 00000000000..b6f4833b69a --- /dev/null +++ b/internal/cli/feedback/result/rpc_test.go @@ -0,0 +1,204 @@ +// This file is part of arduino-cli. +// +// Copyright 2023 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 result_test + +import ( + "reflect" + "slices" + "strings" + "testing" + + "github.com/arduino/arduino-cli/internal/cli/feedback/result" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/stretchr/testify/require" +) + +func getStructJsonTags(t *testing.T, a any) []string { + tags := []string{} + rt := reflect.TypeOf(a) + if rt.Kind() != reflect.Struct { + rt = rt.Elem() + require.Equal(t, reflect.Struct, rt.Kind()) + } + for i := 0; i < rt.NumField(); i++ { + tag := rt.Field(i).Tag.Get("json") + if tag == "" { + continue + } + key, _, _ := strings.Cut(tag, ",") + tags = append(tags, key) + } + return tags +} + +func mustContainsAllPropertyOfRpcStruct(t *testing.T, a, b any, excludeFields ...string) { + // must not be the same pointer, a and b struct must be of differnt type + require.NotSame(t, a, b) + rta, rtb := reflect.TypeOf(a), reflect.TypeOf(b) + if rta.Kind() != reflect.Struct { + rta = rta.Elem() + require.Equal(t, reflect.Struct, rta.Kind()) + } + if rtb.Kind() != reflect.Struct { + rtb = rtb.Elem() + require.Equal(t, reflect.Struct, rtb.Kind()) + } + require.NotEqual(t, rta.String(), rtb.String()) + + aTags := getStructJsonTags(t, a) + bTags := getStructJsonTags(t, b) + if len(excludeFields) > 0 { + aTags = slices.DeleteFunc(aTags, func(s string) bool { return slices.Contains(excludeFields, s) }) + bTags = slices.DeleteFunc(bTags, func(s string) bool { return slices.Contains(excludeFields, s) }) + } + require.ElementsMatch(t, aTags, bTags) +} + +func TestAllFieldAreMapped(t *testing.T) { + // Our PlatformSummary expands the PlatformMetadata without the need to nest it as the rpc does. + platformSummaryRpc := &rpc.PlatformSummary{ + InstalledVersion: "1.0.0", + LatestVersion: "1.0.0", + } + platformSummaryRpcTags := getStructJsonTags(t, platformSummaryRpc) + platformSummaryRpcTags = append(platformSummaryRpcTags, getStructJsonTags(t, platformSummaryRpc.GetMetadata())...) + platformSummaryRpcTags = slices.DeleteFunc(platformSummaryRpcTags, func(s string) bool { return s == "metadata" }) + + platformSummaryResult := result.NewPlatformSummary(platformSummaryRpc) + platformSummaryResultTags := getStructJsonTags(t, platformSummaryResult) + + require.ElementsMatch(t, platformSummaryRpcTags, platformSummaryResultTags) + + platformRelease := &rpc.PlatformRelease{} + platformReleaseResult := result.NewPlatformRelease(platformRelease) + mustContainsAllPropertyOfRpcStruct(t, platformRelease, platformReleaseResult) + + libraryRpc := &rpc.Library{} + libraryResult := result.NewLibrary(libraryRpc) + mustContainsAllPropertyOfRpcStruct(t, libraryRpc, libraryResult) + + libraryReleaseRpc := &rpc.LibraryRelease{} + libraryReleaseResult := result.NewLibraryRelease(libraryReleaseRpc) + mustContainsAllPropertyOfRpcStruct(t, libraryReleaseRpc, libraryReleaseResult) + + installedLibrary := &rpc.InstalledLibrary{} + installedLibraryResult := result.NewInstalledLibrary(installedLibrary) + mustContainsAllPropertyOfRpcStruct(t, installedLibrary, installedLibraryResult) + + downloadResource := &rpc.DownloadResource{} + downloadResourceResult := result.NewDownloadResource(downloadResource) + mustContainsAllPropertyOfRpcStruct(t, downloadResource, downloadResourceResult) + + libraryDependencyRpc := &rpc.LibraryDependency{} + libraryDependencyResult := result.NewLibraryDependency(libraryDependencyRpc) + mustContainsAllPropertyOfRpcStruct(t, libraryDependencyRpc, libraryDependencyResult) + + portRpc := &rpc.Port{} + portResult := result.NewPort(portRpc) + mustContainsAllPropertyOfRpcStruct(t, portRpc, portResult) + + boardDetailsResponseRpc := &rpc.BoardDetailsResponse{} + boardDetailsResponseResult := result.NewBoardDetailsResponse(boardDetailsResponseRpc) + mustContainsAllPropertyOfRpcStruct(t, boardDetailsResponseRpc, boardDetailsResponseResult) + + packageRpc := &rpc.Package{} + packageResult := result.NewPackage(packageRpc) + mustContainsAllPropertyOfRpcStruct(t, packageRpc, packageResult) + + helpRpc := &rpc.Help{} + helpResult := result.NewHelp(helpRpc) + mustContainsAllPropertyOfRpcStruct(t, helpRpc, helpResult) + + boardPlatformRpc := &rpc.BoardPlatform{} + boardPlatformResult := result.NewBoardPlatform(boardPlatformRpc) + mustContainsAllPropertyOfRpcStruct(t, boardPlatformRpc, boardPlatformResult) + + toolsDependencyRpc := &rpc.ToolsDependencies{} + toolsDependencyResult := result.NewToolsDependency(toolsDependencyRpc) + mustContainsAllPropertyOfRpcStruct(t, toolsDependencyRpc, toolsDependencyResult) + + systemRpc := &rpc.Systems{} + systemResult := result.NewSystem(systemRpc) + mustContainsAllPropertyOfRpcStruct(t, systemRpc, systemResult) + + configOptionRpc := &rpc.ConfigOption{} + configOptionResult := result.NewConfigOption(configOptionRpc) + mustContainsAllPropertyOfRpcStruct(t, configOptionRpc, configOptionResult) + + configValueRpc := &rpc.ConfigValue{} + configValueResult := result.NewConfigValue(configValueRpc) + mustContainsAllPropertyOfRpcStruct(t, configValueRpc, configValueResult) + + programmerRpc := &rpc.Programmer{} + programmerResult := result.NewProgrammer(programmerRpc) + mustContainsAllPropertyOfRpcStruct(t, programmerRpc, programmerResult) + + boardIdentificationPropertiesRpc := &rpc.BoardIdentificationProperties{} + boardIdentificationPropertiesResult := result.NewBoardIndentificationProperty(boardIdentificationPropertiesRpc) + mustContainsAllPropertyOfRpcStruct(t, boardIdentificationPropertiesRpc, boardIdentificationPropertiesResult) + + boardListAllResponseRpc := &rpc.BoardListAllResponse{} + boardListAllResponseResult := result.NewBoardListAllResponse(boardListAllResponseRpc) + mustContainsAllPropertyOfRpcStruct(t, boardListAllResponseRpc, boardListAllResponseResult) + + boardListItemRpc := &rpc.BoardListItem{} + boardListItemResult := result.NewBoardListItem(boardListItemRpc) + mustContainsAllPropertyOfRpcStruct(t, boardListItemRpc, boardListItemResult) + + platformRpc := &rpc.Platform{} + platformResult := result.NewPlatform(platformRpc) + mustContainsAllPropertyOfRpcStruct(t, platformRpc, platformResult) + + platformMetadataRpc := &rpc.PlatformMetadata{} + platformMetadataResult := result.NewPlatformMetadata(platformMetadataRpc) + mustContainsAllPropertyOfRpcStruct(t, platformMetadataRpc, platformMetadataResult) + + detectedPortRpc := &rpc.DetectedPort{} + detectedPortResult := result.NewDetectedPort(detectedPortRpc) + mustContainsAllPropertyOfRpcStruct(t, detectedPortRpc, detectedPortResult) + + libraryResolveDependenciesResponseRpc := &rpc.LibraryResolveDependenciesResponse{} + libraryResolveDependenciesResponseResult := result.NewLibraryResolveDependenciesResponse(libraryResolveDependenciesResponseRpc) + mustContainsAllPropertyOfRpcStruct(t, libraryResolveDependenciesResponseRpc, libraryResolveDependenciesResponseResult) + + libraryDependencyStatusRpc := &rpc.LibraryDependencyStatus{} + libraryDependencyStatusResult := result.NewLibraryDependencyStatus(libraryDependencyStatusRpc) + mustContainsAllPropertyOfRpcStruct(t, libraryDependencyStatusRpc, libraryDependencyStatusResult) + + librarySearchResponseRpc := &rpc.LibrarySearchResponse{} + librarySearchResponseResult := result.NewLibrarySearchResponse(librarySearchResponseRpc) + mustContainsAllPropertyOfRpcStruct(t, librarySearchResponseRpc, librarySearchResponseResult) + + searchedLibraryRpc := &rpc.SearchedLibrary{} + searchedLibraryResult := result.NewSearchedLibrary(searchedLibraryRpc) + mustContainsAllPropertyOfRpcStruct(t, searchedLibraryRpc, searchedLibraryResult) + + monitorPortSettingDescriptorRpc := &rpc.MonitorPortSettingDescriptor{} + monitorPortSettingDescriptorResult := result.NewMonitorPortSettingDescriptor(monitorPortSettingDescriptorRpc) + mustContainsAllPropertyOfRpcStruct(t, monitorPortSettingDescriptorRpc, monitorPortSettingDescriptorResult) + + compileResponseRpc := &rpc.CompileResponse{} + compileResponseResult := result.NewCompileResponse(compileResponseRpc) + mustContainsAllPropertyOfRpcStruct(t, compileResponseRpc, compileResponseResult, "progress") + + executableSectionSizeRpc := &rpc.ExecutableSectionSize{} + executableSectionSizeResult := result.NewExecutableSectionSize(executableSectionSizeRpc) + mustContainsAllPropertyOfRpcStruct(t, executableSectionSizeRpc, executableSectionSizeResult) + + installedPlatformReferenceRpc := &rpc.InstalledPlatformReference{} + installedPlatformReferenceResult := result.NewInstalledPlatformReference(installedPlatformReferenceRpc) + mustContainsAllPropertyOfRpcStruct(t, installedPlatformReferenceRpc, installedPlatformReferenceResult) +} From e6190935ea2a0d0189447051dac2e50cebc5226d Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Mon, 6 Nov 2023 08:18:56 +0100 Subject: [PATCH 17/24] fix typo and import name --- internal/cli/board/details.go | 6 +++--- internal/cli/feedback/result/rpc.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/cli/board/details.go b/internal/cli/board/details.go index 9a9bd504586..70ba6b68a09 100644 --- a/internal/cli/board/details.go +++ b/internal/cli/board/details.go @@ -23,7 +23,7 @@ import ( "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" - fResult "github.com/arduino/arduino-cli/internal/cli/feedback/result" + "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/table" @@ -75,7 +75,7 @@ func runDetailsCommand(fqbn string, showFullDetails, listProgrammers bool, showP } feedback.PrintResult(detailsResult{ - details: fResult.NewBoardDetailsResponse(res), + details: result.NewBoardDetailsResponse(res), listProgrammers: listProgrammers, showFullDetails: showFullDetails, showProperties: showPropertiesMode != arguments.ShowPropertiesDisabled, @@ -85,7 +85,7 @@ func runDetailsCommand(fqbn string, showFullDetails, listProgrammers bool, showP // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation type detailsResult struct { - details *fResult.BoardDetailsResponse + details *result.BoardDetailsResponse listProgrammers bool showFullDetails bool showProperties bool diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index d43576daf5b..c664ca5104f 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -23,7 +23,7 @@ import ( semver "go.bug.st/relaxed-semver" ) -// NewPlatformSummary creates a new result.Platform from rpc.PlatformSummary +// NewPlatformSummary creates a new result.PlatformSummary from rpc.PlatformSummary func NewPlatformSummary(in *rpc.PlatformSummary) *PlatformSummary { if in == nil { return nil From 2e16184b822ca8ee5e5f15f3ca99ff8440f05974 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Mon, 6 Nov 2023 08:23:04 +0100 Subject: [PATCH 18/24] remove redundant nil checks --- internal/cli/board/details.go | 8 +------- internal/cli/board/list.go | 3 --- internal/cli/board/listall.go | 3 --- internal/cli/board/search.go | 3 --- internal/cli/compile/compile.go | 3 --- internal/cli/lib/list.go | 3 --- internal/cli/lib/search.go | 9 --------- 7 files changed, 1 insertion(+), 31 deletions(-) diff --git a/internal/cli/board/details.go b/internal/cli/board/details.go index 70ba6b68a09..6ce3ed7143c 100644 --- a/internal/cli/board/details.go +++ b/internal/cli/board/details.go @@ -110,9 +110,6 @@ func (dr detailsResult) String() string { t := table.New() t.AddRow(tr("Id"), tr("Programmer name")) for _, programmer := range details.Programmers { - if programmer == nil { - continue - } t.AddRow(programmer.Id, programmer.Name) } return t.Render() @@ -153,7 +150,7 @@ func (dr detailsResult) String() string { } for _, idp := range details.IdentificationProperties { - if idp == nil || idp.Properties == nil { + if idp.Properties == nil { continue } t.AddRow() // get some space from above @@ -221,9 +218,6 @@ func (dr detailsResult) String() string { tab.AddRow(tr("Programmers:"), tr("ID"), tr("Name")) for _, programmer := range details.Programmers { - if programmer == nil { - continue - } tab.AddRow("", programmer.Id, programmer.Name) } diff --git a/internal/cli/board/list.go b/internal/cli/board/list.go index b8612f1dce5..cc8f5b7a52c 100644 --- a/internal/cli/board/list.go +++ b/internal/cli/board/list.go @@ -133,9 +133,6 @@ func (dr listResult) String() string { t := table.New() t.SetHeader(tr("Port"), tr("Protocol"), tr("Type"), tr("Board Name"), tr("FQBN"), tr("Core")) for _, detectedPort := range dr.ports { - if detectedPort == nil { - continue - } port := detectedPort.Port protocol := port.Protocol address := port.Address diff --git a/internal/cli/board/listall.go b/internal/cli/board/listall.go index fd894c127f8..74167c2014d 100644 --- a/internal/cli/board/listall.go +++ b/internal/cli/board/listall.go @@ -90,9 +90,6 @@ func (dr resultAll) String() string { }) for _, item := range dr.list.Boards { - if item == nil { - continue - } hidden := "" if item.IsHidden { hidden = tr("(hidden)") diff --git a/internal/cli/board/search.go b/internal/cli/board/search.go index 2ef73e79b7a..f4c03cd08b9 100644 --- a/internal/cli/board/search.go +++ b/internal/cli/board/search.go @@ -87,9 +87,6 @@ func (r searchResults) String() string { }) for _, item := range r.boards { - if item == nil { - continue - } hidden := "" if item.IsHidden { hidden = tr("(hidden)") diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index 9f0c1015437..c8b0165d80b 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -433,9 +433,6 @@ func (r *compileResult) String() string { table.NewCell(tr("Version"), titleColor), table.NewCell(tr("Path"), pathColor)) for _, l := range build.UsedLibraries { - if l == nil { - continue - } libraries.AddRow( table.NewCell(l.Name, nameColor), l.Version, diff --git a/internal/cli/lib/list.go b/internal/cli/lib/list.go index 5059551b6db..75547a93d6d 100644 --- a/internal/cli/lib/list.go +++ b/internal/cli/lib/list.go @@ -146,9 +146,6 @@ func (ir installedResult) String() string { lastName := "" for _, libMeta := range ir.installedLibs { - if libMeta == nil { - continue - } lib := libMeta.Library name := lib.Name if name == lastName { diff --git a/internal/cli/lib/search.go b/internal/cli/lib/search.go index d5d1ba676fb..b65378c4d8c 100644 --- a/internal/cli/lib/search.go +++ b/internal/cli/lib/search.go @@ -156,9 +156,6 @@ func (res librarySearchResult) Data() interface{} { names := []LibName{} for _, lib := range res.results.Libraries { - if lib == nil { - continue - } names = append(names, LibName{lib.Name}) } @@ -181,9 +178,6 @@ func (res librarySearchResult) String() string { } for _, lib := range results { - if lib == nil { - continue - } if string(res.results.Status) == rpc.LibrarySearchStatus_name[int32(rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_SUCCESS)] { out.WriteString(tr(`Name: "%s"`, lib.Name) + "\n") if res.namesOnly { @@ -198,9 +192,6 @@ func (res librarySearchResult) String() string { deps := []string{} for _, dep := range latest.Dependencies { - if dep == nil { - continue - } if dep.VersionConstraint == "" { deps = append(deps, dep.Name) } else { From 0702b77c3d768ed6888f2cba71e5f42257bf1687 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Mon, 6 Nov 2023 08:25:06 +0100 Subject: [PATCH 19/24] fix printing empty string instead of empty table --- internal/cli/board/search.go | 8 ++++---- internal/cli/monitor/monitor.go | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/internal/cli/board/search.go b/internal/cli/board/search.go index f4c03cd08b9..9f8133fd80c 100644 --- a/internal/cli/board/search.go +++ b/internal/cli/board/search.go @@ -75,13 +75,13 @@ func (r searchResults) Data() interface{} { } func (r searchResults) String() string { - t := table.New() - t.SetHeader(tr("Board Name"), tr("FQBN"), tr("Platform ID"), "") - if len(r.boards) == 0 { - return t.Render() + return "" } + t := table.New() + t.SetHeader(tr("Board Name"), tr("FQBN"), tr("Platform ID"), "") + sort.Slice(r.boards, func(i, j int) bool { return r.boards[i].Name < r.boards[j].Name }) diff --git a/internal/cli/monitor/monitor.go b/internal/cli/monitor/monitor.go index 93ab6668946..c48fbfac0e0 100644 --- a/internal/cli/monitor/monitor.go +++ b/internal/cli/monitor/monitor.go @@ -290,12 +290,11 @@ func (r *detailsResult) Data() interface{} { } func (r *detailsResult) String() string { - t := table.New() - t.SetHeader(tr("ID"), tr("Setting"), tr("Default"), tr("Values")) - if len(r.Settings) == 0 { - return t.Render() + return "" } + t := table.New() + t.SetHeader(tr("ID"), tr("Setting"), tr("Default"), tr("Values")) green := color.New(color.FgGreen) sort.Slice(r.Settings, func(i, j int) bool { From 90cb43a70d92c79fdd2f7ba40fdb6a50e5a54398 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Mon, 6 Nov 2023 08:27:48 +0100 Subject: [PATCH 20/24] fix: make anonymous struct explicit --- internal/cli/compile/compile.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index c8b0165d80b..8f371d5b060 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -344,9 +344,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { CompilerOut: stdIO.Stdout, CompilerErr: stdIO.Stderr, BuilderResult: result.NewCompileResponse(compileRes), - UploadResult: struct { - UpdatedUploadPort *result.Port `json:"updated_upload_port,omitempty"` - }{ + UploadResult: updatedUploadPortResult{ UpdatedUploadPort: result.NewPort(uploadRes.GetUpdatedUploadPort()), }, ProfileOut: profileOut, @@ -389,16 +387,18 @@ func runCompileCommand(cmd *cobra.Command, args []string) { feedback.PrintResult(res) } +type updatedUploadPortResult struct { + UpdatedUploadPort *result.Port `json:"updated_upload_port,omitempty"` +} + type compileResult struct { CompilerOut string `json:"compiler_out"` CompilerErr string `json:"compiler_err"` BuilderResult *result.CompileResponse `json:"builder_result"` - UploadResult struct { - UpdatedUploadPort *result.Port `json:"updated_upload_port,omitempty"` - } `json:"upload_result"` - Success bool `json:"success"` - ProfileOut string `json:"profile_out,omitempty"` - Error string `json:"error,omitempty"` + UploadResult updatedUploadPortResult `json:"upload_result"` + Success bool `json:"success"` + ProfileOut string `json:"profile_out,omitempty"` + Error string `json:"error,omitempty"` showPropertiesMode arguments.ShowPropertiesMode hideStats bool From 2d77b39a01211719e32076a48ccf339e26247134 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Mon, 6 Nov 2023 08:36:58 +0100 Subject: [PATCH 21/24] fix: use rpc enum String method --- internal/cli/lib/examples.go | 2 +- internal/cli/lib/search.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/cli/lib/examples.go b/internal/cli/lib/examples.go index 8a8daf52938..7efdafd64e4 100644 --- a/internal/cli/lib/examples.go +++ b/internal/cli/lib/examples.go @@ -115,7 +115,7 @@ func (ir libraryExamplesResult) String() string { name := lib.Library.Name if lib.Library.ContainerPlatform != "" { name += " (" + lib.Library.ContainerPlatform + ")" - } else if string(lib.Library.Location) != rpc.LibraryLocation_name[int32(rpc.LibraryLocation_LIBRARY_LOCATION_USER)] { + } else if string(lib.Library.Location) != rpc.LibraryLocation_LIBRARY_LOCATION_USER.String() { name += " (" + string(lib.Library.Location) + ")" } r := tr("Examples for library %s", color.GreenString("%s", name)) + "\n" diff --git a/internal/cli/lib/search.go b/internal/cli/lib/search.go index b65378c4d8c..4f2b52cfb12 100644 --- a/internal/cli/lib/search.go +++ b/internal/cli/lib/search.go @@ -173,12 +173,12 @@ func (res librarySearchResult) String() string { var out strings.Builder - if string(res.results.Status) == rpc.LibrarySearchStatus_name[int32(rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_FAILED)] { + if string(res.results.Status) == rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_FAILED.String() { out.WriteString(tr("No libraries matching your search.\nDid you mean...\n")) } for _, lib := range results { - if string(res.results.Status) == rpc.LibrarySearchStatus_name[int32(rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_SUCCESS)] { + if string(res.results.Status) == rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_SUCCESS.String() { out.WriteString(tr(`Name: "%s"`, lib.Name) + "\n") if res.namesOnly { continue From a126e93624b098e784461718994d2c747c8e1efc Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Mon, 6 Nov 2023 08:57:20 +0100 Subject: [PATCH 22/24] add BoardListWatchResponse mapper --- internal/cli/board/list.go | 14 ++++++++------ internal/cli/feedback/result/rpc.go | 17 +++++++++++++++++ internal/cli/feedback/result/rpc_test.go | 6 +++++- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/internal/cli/board/list.go b/internal/cli/board/list.go index cc8f5b7a52c..82f2b9b74d2 100644 --- a/internal/cli/board/list.go +++ b/internal/cli/board/list.go @@ -100,12 +100,14 @@ func watchList(inst *rpc.Instance) { } for event := range eventsChan { - feedback.PrintResult(watchEventResult{ - Type: event.EventType, - Boards: result.NewBoardListItems(event.Port.MatchingBoards), - Port: result.NewPort(event.Port.Port), - Error: event.Error, - }) + if res := result.NewBoardListWatchResponse(event); res != nil { + feedback.PrintResult(watchEventResult{ + Type: res.EventType, + Boards: res.Port.MatchingBoards, + Port: res.Port.Port, + Error: res.Error, + }) + } } } diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index c664ca5104f..ea310a49272 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -888,3 +888,20 @@ func NewInstalledPlatformReference(r *rpc.InstalledPlatformReference) *Installed PackageUrl: r.GetPackageUrl(), } } + +type BoardListWatchResponse struct { + EventType string `json:"event_type,omitempty"` + Port *DetectedPort `json:"port,omitempty"` + Error string `json:"error,omitempty"` +} + +func NewBoardListWatchResponse(r *rpc.BoardListWatchResponse) *BoardListWatchResponse { + if r == nil { + return nil + } + return &BoardListWatchResponse{ + EventType: r.EventType, + Port: NewDetectedPort(r.Port), + Error: r.Error, + } +} diff --git a/internal/cli/feedback/result/rpc_test.go b/internal/cli/feedback/result/rpc_test.go index b6f4833b69a..79b9193994c 100644 --- a/internal/cli/feedback/result/rpc_test.go +++ b/internal/cli/feedback/result/rpc_test.go @@ -45,7 +45,7 @@ func getStructJsonTags(t *testing.T, a any) []string { } func mustContainsAllPropertyOfRpcStruct(t *testing.T, a, b any, excludeFields ...string) { - // must not be the same pointer, a and b struct must be of differnt type + // must not be the same pointer, a and b struct must be of different type require.NotSame(t, a, b) rta, rtb := reflect.TypeOf(a), reflect.TypeOf(b) if rta.Kind() != reflect.Struct { @@ -201,4 +201,8 @@ func TestAllFieldAreMapped(t *testing.T) { installedPlatformReferenceRpc := &rpc.InstalledPlatformReference{} installedPlatformReferenceResult := result.NewInstalledPlatformReference(installedPlatformReferenceRpc) mustContainsAllPropertyOfRpcStruct(t, installedPlatformReferenceRpc, installedPlatformReferenceResult) + + boardListWatchResponseRpc := &rpc.BoardListWatchResponse{} + boardListWatchResponseResult := result.NewBoardListWatchResponse(boardListWatchResponseRpc) + mustContainsAllPropertyOfRpcStruct(t, boardListWatchResponseRpc, boardListWatchResponseResult) } From 0aabc1b545c3d47f7b163567caf17a3ff7a05f05 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Mon, 6 Nov 2023 13:58:10 +0100 Subject: [PATCH 23/24] fix: remap rpc enums --- internal/cli/feedback/result/rpc.go | 60 ++++++++++++++++++++++-- internal/cli/feedback/result/rpc_test.go | 41 ++++++++++++++++ internal/cli/lib/examples.go | 2 +- internal/cli/lib/search.go | 4 +- 4 files changed, 101 insertions(+), 6 deletions(-) diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index ea310a49272..6b19c138a6d 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -136,8 +136,47 @@ type InstalledLibrary struct { type LibraryLocation string +const ( + LibraryLocationUser LibraryLocation = "user" + LibraryLocationIDEBuiltin LibraryLocation = "ide" + LibraryLocationPlatformBuiltin LibraryLocation = "platform" + LibraryLocationReferencedPlatformBuiltin LibraryLocation = "ref-platform" + LibraryLocationUnmanged LibraryLocation = "unmanaged" +) + +func NewLibraryLocation(r rpc.LibraryLocation) LibraryLocation { + switch r { + case rpc.LibraryLocation_LIBRARY_LOCATION_BUILTIN: + return LibraryLocationIDEBuiltin + case rpc.LibraryLocation_LIBRARY_LOCATION_PLATFORM_BUILTIN: + return LibraryLocationPlatformBuiltin + case rpc.LibraryLocation_LIBRARY_LOCATION_REFERENCED_PLATFORM_BUILTIN: + return LibraryLocationReferencedPlatformBuiltin + case rpc.LibraryLocation_LIBRARY_LOCATION_USER: + return LibraryLocationUser + case rpc.LibraryLocation_LIBRARY_LOCATION_UNMANAGED: + return LibraryLocationUnmanged + } + return LibraryLocationIDEBuiltin +} + type LibraryLayout string +const ( + LibraryLayoutFlat LibraryLayout = "flat" + LibraryLayoutRecursive LibraryLayout = "recursive" +) + +func NewLibraryLayout(r rpc.LibraryLayout) LibraryLayout { + switch r { + case rpc.LibraryLayout_LIBRARY_LAYOUT_FLAT: + return LibraryLayoutFlat + case rpc.LibraryLayout_LIBRARY_LAYOUT_RECURSIVE: + return LibraryLayoutRecursive + } + return LibraryLayoutFlat +} + type Library struct { Name string `json:"name,omitempty"` Author string `json:"author,omitempty"` @@ -204,8 +243,8 @@ func NewLibrary(l *rpc.Library) *Library { Version: l.GetVersion(), License: l.GetLicense(), Properties: libraryPropsMap, - Location: LibraryLocation(l.GetLocation().String()), - Layout: LibraryLayout(l.GetLayout().String()), + Location: NewLibraryLocation(l.GetLocation()), + Layout: NewLibraryLayout(l.GetLayout()), Examples: l.GetExamples(), ProvidesIncludes: l.GetProvidesIncludes(), CompatibleWith: libraryCompatibleWithMap, @@ -751,6 +790,21 @@ func NewLibraryDependencyStatus(l *rpc.LibraryDependencyStatus) *LibraryDependen type LibrarySearchStatus string +const ( + LibrarySearchStatusFailed LibrarySearchStatus = "failed" + LibrarySearchStatusSuccess LibrarySearchStatus = "success" +) + +func NewLibrarySearchStatus(r rpc.LibrarySearchStatus) LibrarySearchStatus { + switch r { + case rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_FAILED: + return LibrarySearchStatusFailed + case rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_SUCCESS: + return LibrarySearchStatusSuccess + } + return LibrarySearchStatusFailed +} + type LibrarySearchResponse struct { Libraries []*SearchedLibrary `json:"libraries,omitempty"` Status LibrarySearchStatus `json:"status,omitempty"` @@ -768,7 +822,7 @@ func NewLibrarySearchResponse(l *rpc.LibrarySearchResponse) *LibrarySearchRespon return &LibrarySearchResponse{ Libraries: searchedLibraries, - Status: LibrarySearchStatus(l.GetStatus().Enum().String()), + Status: NewLibrarySearchStatus(l.GetStatus()), } } diff --git a/internal/cli/feedback/result/rpc_test.go b/internal/cli/feedback/result/rpc_test.go index 79b9193994c..46adbcd8acc 100644 --- a/internal/cli/feedback/result/rpc_test.go +++ b/internal/cli/feedback/result/rpc_test.go @@ -206,3 +206,44 @@ func TestAllFieldAreMapped(t *testing.T) { boardListWatchResponseResult := result.NewBoardListWatchResponse(boardListWatchResponseRpc) mustContainsAllPropertyOfRpcStruct(t, boardListWatchResponseRpc, boardListWatchResponseResult) } + +func TestEnumsMapsEveryRpcCounterpart(t *testing.T) { + t.Run("LibraryLocation enums maps every element", func(t *testing.T) { + results := make([]result.LibraryLocation, 0, len(rpc.LibraryLocation_name)) + for key := range rpc.LibraryLocation_name { + results = append(results, result.NewLibraryLocation(rpc.LibraryLocation(key))) + } + require.NotEmpty(t, results) + require.Len(t, results, len(rpc.LibraryLocation_name)) + require.True(t, isUnique(results)) + }) + t.Run("LibraryLayout enums maps every element", func(t *testing.T) { + results := make([]result.LibraryLayout, 0, len(rpc.LibraryLayout_name)) + for key := range rpc.LibraryLayout_name { + results = append(results, result.NewLibraryLayout(rpc.LibraryLayout(key))) + } + require.NotEmpty(t, results) + require.Len(t, results, len(rpc.LibraryLayout_name)) + require.True(t, isUnique(results)) + }) + t.Run("LibrarySearchStatus enums maps every element", func(t *testing.T) { + results := make([]result.LibrarySearchStatus, 0, len(rpc.LibrarySearchStatus_name)) + for key := range rpc.LibrarySearchStatus_name { + results = append(results, result.NewLibrarySearchStatus(rpc.LibrarySearchStatus(key))) + } + require.NotEmpty(t, results) + require.Len(t, results, len(rpc.LibrarySearchStatus_name)) + require.True(t, isUnique(results)) + }) +} + +func isUnique[T comparable](s []T) bool { + seen := map[T]bool{} + for _, v := range s { + if _, ok := seen[v]; ok { + return false + } + seen[v] = true + } + return true +} diff --git a/internal/cli/lib/examples.go b/internal/cli/lib/examples.go index 7efdafd64e4..17852f4003a 100644 --- a/internal/cli/lib/examples.go +++ b/internal/cli/lib/examples.go @@ -115,7 +115,7 @@ func (ir libraryExamplesResult) String() string { name := lib.Library.Name if lib.Library.ContainerPlatform != "" { name += " (" + lib.Library.ContainerPlatform + ")" - } else if string(lib.Library.Location) != rpc.LibraryLocation_LIBRARY_LOCATION_USER.String() { + } else if lib.Library.Location != result.LibraryLocationUser { name += " (" + string(lib.Library.Location) + ")" } r := tr("Examples for library %s", color.GreenString("%s", name)) + "\n" diff --git a/internal/cli/lib/search.go b/internal/cli/lib/search.go index 4f2b52cfb12..da666bdc579 100644 --- a/internal/cli/lib/search.go +++ b/internal/cli/lib/search.go @@ -173,12 +173,12 @@ func (res librarySearchResult) String() string { var out strings.Builder - if string(res.results.Status) == rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_FAILED.String() { + if res.results.Status == result.LibrarySearchStatusFailed { out.WriteString(tr("No libraries matching your search.\nDid you mean...\n")) } for _, lib := range results { - if string(res.results.Status) == rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_SUCCESS.String() { + if res.results.Status == result.LibrarySearchStatusSuccess { out.WriteString(tr(`Name: "%s"`, lib.Name) + "\n") if res.namesOnly { continue From 1d855a0d7e9be0f9d2e7ae9d4406000a6b7c59ef Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Mon, 6 Nov 2023 14:13:31 +0100 Subject: [PATCH 24/24] fix: cli sketch new - not showing any json output --- internal/cli/sketch/new.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/internal/cli/sketch/new.go b/internal/cli/sketch/new.go index ff141417ad0..01aa818168c 100644 --- a/internal/cli/sketch/new.go +++ b/internal/cli/sketch/new.go @@ -81,5 +81,17 @@ func runNewCommand(args []string, overwrite bool) { feedback.Fatal(tr("Error creating sketch: %v", err), feedback.ErrGeneric) } - feedback.Print(tr("Sketch created in: %s", sketchDirPath)) + feedback.PrintResult(sketchResult{SketchDirPath: sketchDirPath}) +} + +type sketchResult struct { + SketchDirPath *paths.Path `json:"sketch_path"` +} + +func (ir sketchResult) Data() interface{} { + return ir +} + +func (ir sketchResult) String() string { + return tr("Sketch created in: %s", ir.SketchDirPath) }