From 2f262e4f5c02ac91d3e90c4a7f9cd3eaeabdf1ab Mon Sep 17 00:00:00 2001 From: Noah Holm <32292420+noppaz@users.noreply.github.com> Date: Thu, 28 Dec 2023 22:05:59 +0100 Subject: [PATCH] make row groups paginated --- cmd/rowgroups.go | 5 ++- go.mod | 9 +++++ go.sum | 19 ++++++++++ internal/commands/commands.go | 8 +++-- internal/parse/rowgroup.go | 2 +- internal/parse/util.go | 2 +- internal/visualize/paginator.go | 64 +++++++++++++++++++++++++++++++++ internal/visualize/visualize.go | 24 +++++++------ 8 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 internal/visualize/paginator.go diff --git a/cmd/rowgroups.go b/cmd/rowgroups.go index dac827b..52c873d 100644 --- a/cmd/rowgroups.go +++ b/cmd/rowgroups.go @@ -6,15 +6,18 @@ import ( ) func init() { + var perPage int rowGroupsCmd := &cobra.Command{ Use: "row-groups", Short: "Show row group metadata", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { filename := args[0] - return commands.RowGroupsCommand(filename) + return commands.RowGroupsCommand(filename, perPage) }, } + rowGroupsCmd.Flags().IntVarP(&perPage, "per-page", "n", 1, "Amount of row groups to show per page") + rootCmd.AddCommand(rowGroupsCmd) } diff --git a/go.mod b/go.mod index 14c1937..9c25739 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ replace github.com/noppaz/collie => ./ require ( github.com/apache/arrow/go/parquet v0.0.0-20211112161151-bc219186db40 + github.com/charmbracelet/bubbletea v0.25.0 github.com/charmbracelet/lipgloss v0.9.1 github.com/spf13/cobra v1.8.0 ) @@ -16,6 +17,8 @@ require ( github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 // indirect github.com/apache/thrift v0.19.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/bubbles v0.17.1 + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/flatbuffers v23.5.26+incompatible // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -24,9 +27,12 @@ require ( github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect + github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect github.com/pierrec/lz4/v4 v4.1.19 // indirect @@ -34,6 +40,9 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect + golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.3.8 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect ) diff --git a/go.sum b/go.sum index 0ce091f..55d6431 100644 --- a/go.sum +++ b/go.sum @@ -25,12 +25,18 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4= +github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -100,6 +106,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -107,6 +115,10 @@ github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpsp github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= @@ -198,6 +210,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -211,15 +225,20 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/commands/commands.go b/internal/commands/commands.go index 199e9b4..dfb4f3c 100644 --- a/internal/commands/commands.go +++ b/internal/commands/commands.go @@ -23,20 +23,22 @@ func MetaCommand(filename string) error { return nil } -func RowGroupsCommand(filename string) error { +func RowGroupsCommand(filename string, perPage int) error { reader, err := file.OpenParquetFile(filename, true) if err != nil { return fmt.Errorf("error opening parquet file: %w", err) } defer reader.Close() + var rowGroupOutput []string for i := range reader.MetaData().RowGroups { rowGroup := reader.RowGroup(i) rowGroupStats, err := parse.GetRowGroupStats(i, rowGroup) if err != nil { return err } - visualize.PrintRowGroup(rowGroupStats) + rowGroupOutput = append(rowGroupOutput, visualize.FormatRowGroup(rowGroupStats)) + } - return nil + return visualize.PaginatorCreator(rowGroupOutput, perPage) } diff --git a/internal/parse/rowgroup.go b/internal/parse/rowgroup.go index f5d7aa3..638b10d 100644 --- a/internal/parse/rowgroup.go +++ b/internal/parse/rowgroup.go @@ -29,7 +29,7 @@ func (c *ChunkStats) GetHeaders() []string { "Compression", "Compressed", "Uncompressed", - "Compression ratio", + "Ratio", "Encodings", "Nulls", "Min", diff --git a/internal/parse/util.go b/internal/parse/util.go index 34721c5..a3cd774 100644 --- a/internal/parse/util.go +++ b/internal/parse/util.go @@ -13,7 +13,7 @@ func humanBytes(b int64) string { for _, unit := range []string{"KiB", "MiB", "GiB", "TiB", "PiB"} { bf /= 1024.0 if math.Abs(bf) < float64(1024) { - return fmt.Sprintf("%.3f %s", bf, unit) + return fmt.Sprintf("%7.3f %s", bf, unit) } } return fmt.Sprintf("%v B", b) diff --git a/internal/visualize/paginator.go b/internal/visualize/paginator.go new file mode 100644 index 0000000..55fa167 --- /dev/null +++ b/internal/visualize/paginator.go @@ -0,0 +1,64 @@ +package visualize + +import ( + "fmt" + "strings" + + "github.com/charmbracelet/bubbles/paginator" + "github.com/charmbracelet/lipgloss" + + tea "github.com/charmbracelet/bubbletea" +) + +func PaginatorCreator(items []string, perPage int) error { + p := paginator.New() + p.Type = paginator.Dots + p.PerPage = perPage + p.ActiveDot = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "235", Dark: "252"}).Render("•") + p.InactiveDot = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "250", Dark: "238"}).Render("•") + p.SetTotalPages(len(items)) + + model := PaginatorModel{ + paginator: p, + items: items, + } + + paginate := tea.NewProgram(model) + if _, err := paginate.Run(); err != nil { + return err + } + return nil +} + +type PaginatorModel struct { + items []string + paginator paginator.Model +} + +func (m PaginatorModel) Init() tea.Cmd { + return nil +} + +func (m PaginatorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmd tea.Cmd + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "q", "esc", "ctrl+c": + return m, tea.Quit + } + } + m.paginator, cmd = m.paginator.Update(msg) + return m, cmd +} + +func (m PaginatorModel) View() string { + var b strings.Builder + start, end := m.paginator.GetSliceBounds(len(m.items)) + for _, item := range m.items[start:end] { + b.WriteString(fmt.Sprintf("\n%s\n", item)) + } + b.WriteString(" " + m.paginator.View()) + b.WriteString("\n\n h/l ←/→ page • q: quit\n") + return b.String() +} diff --git a/internal/visualize/visualize.go b/internal/visualize/visualize.go index 02f9f3a..7510ff2 100644 --- a/internal/visualize/visualize.go +++ b/internal/visualize/visualize.go @@ -3,6 +3,7 @@ package visualize import ( "fmt" "os" + "strings" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/table" @@ -67,7 +68,7 @@ func PrintFileStatistics(fileStats *parse.FileStats) { fmt.Println(t) } -func PrintRowGroup(rowGroupStats *parse.RowGroupStats) { +func FormatRowGroup(rowGroupStats *parse.RowGroupStats) string { var rows [][]string headers := rowGroupStats.ChunkStats[0].GetHeaders() for _, col := range rowGroupStats.ChunkStats { @@ -75,13 +76,16 @@ func PrintRowGroup(rowGroupStats *parse.RowGroupStats) { } t := lipglossTable(headers, rows) - fmt.Printf( - "Row group: %v, Rows: %v, Compressed size: %s, Uncompressed size: %s, Compression ratio %.2f\n", - rowGroupStats.Index, - rowGroupStats.RowCount, - rowGroupStats.SizeCompressed, - rowGroupStats.SizeUncompressed, - rowGroupStats.CompressionRatio, - ) - fmt.Println(t) + b := strings.Builder{} + b.WriteString( + fmt.Sprintf( + "Row group: %v, Rows: %v, Compressed size: %s, Uncompressed size: %s, Compression ratio %.2f\n", + rowGroupStats.Index, + rowGroupStats.RowCount, + rowGroupStats.SizeCompressed, + rowGroupStats.SizeUncompressed, + rowGroupStats.CompressionRatio, + )) + b.WriteString(fmt.Sprintf("%s\n", t.String())) + return b.String() }