diff --git a/go.mod b/go.mod index f8fe4132d0d..32c426eb89d 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-envparse v0.1.0 + github.com/jedib0t/go-pretty/v6 v6.5.4 github.com/joho/godotenv v1.5.1 github.com/mattn/go-isatty v0.0.20 github.com/mholt/archiver/v4 v4.0.0-alpha.8 diff --git a/go.sum b/go.sum index 13fcd1bffc1..be76a37cbea 100644 --- a/go.sum +++ b/go.sum @@ -227,6 +227,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jedib0t/go-pretty/v6 v6.5.4 h1:gOGo0613MoqUcf0xCj+h/V3sHDaZasfv152G6/5l91s= +github.com/jedib0t/go-pretty/v6 v6.5.4/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= diff --git a/internal/boxcli/search.go b/internal/boxcli/search.go index dbe00c4e253..656e449b2ab 100644 --- a/internal/boxcli/search.go +++ b/internal/boxcli/search.go @@ -4,13 +4,19 @@ package boxcli import ( + "bytes" "fmt" "io" "math" + "net/url" + "slices" "strings" + "text/tabwriter" "github.com/spf13/cobra" + "github.com/jedib0t/go-pretty/v6/table" + "github.com/jedib0t/go-pretty/v6/text" "go.jetpack.io/devbox/internal/boxcli/usererr" "go.jetpack.io/devbox/internal/searcher" "go.jetpack.io/devbox/internal/ux" @@ -86,25 +92,50 @@ func printSearchResults( pkgs = results.Packages[:int(math.Min(10, float64(len(results.Packages))))] } + rowConfigAutoMerge := table.RowConfig{AutoMerge: true} + + tableWriter := table.NewWriter() + tableWriter.AppendHeader(table.Row{"Package", "Versions", "Platforms"}, rowConfigAutoMerge) for _, pkg := range pkgs { - nonEmptyVersions := []string{} - for i, v := range pkg.Versions { + systemKey := "" + var versions []string + for i, pkgVersion := range pkg.Versions { + if pkgVersion.Version == "" { + continue + } if !showAll && i >= 10 { resultsAreTrimmed = true break } - if v.Version != "" { - nonEmptyVersions = append(nonEmptyVersions, v.Version) + + var systems []string + for _, sys := range pkgVersion.Systems { + systems = append(systems, sys.System) + } + slices.Sort(systems) + key := strings.Join(systems, " ") + if systemKey != key && systemKey != "" { + tableWriter.AppendRow(table.Row{pkg.Name, columnize(versions, 2), systemKey}, rowConfigAutoMerge) + versions = nil } + systemKey = key + versions = append(versions, pkgVersion.Version) } - versionString := "" - if len(nonEmptyVersions) > 0 { - versionString = fmt.Sprintf(" (%s)", strings.Join(nonEmptyVersions, ", ")) + if len(versions) > 0 { + tableWriter.AppendRow(table.Row{pkg.Name, columnize(versions, 2), systemKey}, rowConfigAutoMerge) } - fmt.Fprintf(w, "* %s %s\n", pkg.Name, versionString) } + tableWriter.SetColumnConfigs([]table.ColumnConfig{ + {Number: 1, AutoMerge: true, VAlign: text.VAlignMiddle}, + {Number: 2, AutoMerge: true, Align: text.AlignJustify, AlignHeader: text.AlignCenter}, + {Number: 3, AutoMerge: true, Align: text.AlignJustify, AlignHeader: text.AlignCenter, WidthMaxEnforcer: text.WrapSoft, WidthMin: 15, WidthMax: 15}, + }) + tableWriter.SetStyle(table.StyleLight) + tableWriter.Style().Options.SeparateRows = true + fmt.Println(tableWriter.Render()) + if resultsAreTrimmed { fmt.Println() ux.Fwarning( @@ -113,6 +144,30 @@ func printSearchResults( "show all.\n\n", ) } + ux.Finfo(w, "For more information go to: https://www.nixhub.io/search?q=%s\n\n", url.QueryEscape(query)) return nil } + +func columnize(data []string, maxColumns int) string { + columns := maxColumns + if len(data) <= columns { + columns = 1 + } + + buf := bytes.NewBufferString("") + var versionsGroup []string + writer := tabwriter.NewWriter(buf, 0, 8, 1, '\t', tabwriter.AlignRight) + for _, version := range data { + if len(versionsGroup) == columns { + _, _ = fmt.Fprintf(writer, "%s\n", strings.Join(versionsGroup, "\t")) + versionsGroup = nil + } + versionsGroup = append(versionsGroup, version) + } + if len(versionsGroup) > 0 { + _, _ = fmt.Fprintf(writer, "%s\n", strings.Join(versionsGroup, "\t")) + } + _ = writer.Flush() + return buf.String() +}