Skip to content

Commit

Permalink
Refactor types for output
Browse files Browse the repository at this point in the history
  • Loading branch information
stoewer committed May 6, 2024
1 parent ce01bff commit 30bba24
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 56 deletions.
2 changes: 1 addition & 1 deletion cmd/parquet-cli/cmd_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,6 @@ func (r *metadataRow) Cells() []any {
}
}

func (r *metadataRow) Data() any {
func (r *metadataRow) SerializableData() any {
return r.s
}
2 changes: 1 addition & 1 deletion pkg/inspect/aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type Aggregate struct {
Stats []AggregateCellStats `json:"stats"`
}

func (rs *Aggregate) Data() any {
func (rs *Aggregate) SerializableData() any {
return rs
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/inspect/col_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type ColumnStats struct {
cells []any
}

func (rs *ColumnStats) Data() any {
func (rs *ColumnStats) SerializableData() any {
return rs
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/inspect/file_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (i *FileInfo) Add(k string, v any) {
i.keys = append(i.keys, k)
}

func (i *FileInfo) Data() any {
func (i *FileInfo) SerializableData() any {
return i.elem
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/inspect/row_dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type DumpLine struct {
Line []*parquet.Value
}

func (d *DumpLine) Data() any {
func (d *DumpLine) SerializableData() any {
return d.Line
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/inspect/row_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type RowStats struct {
Stats []RowCellStats
}

func (rs *RowStats) Data() any {
func (rs *RowStats) SerializableData() any {
return rs.Stats
}

Expand Down
44 changes: 44 additions & 0 deletions pkg/output/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package output

import "errors"

// Format describes a printable data representation.
type Format string

const (
FormatJSON = "json"
FormatCSV = "csv"
FormatTab = "tab"
FormatText = "text"
)

func (f *Format) Validate() error {
switch *f {
case FormatJSON, FormatTab, FormatCSV, FormatText:
return nil
default:
return errors.New("output format is expected to be 'json', 'tab', 'text' or 'csv'")
}
}

func formatsFor(data any) []Format {
var formats []Format
switch data.(type) {
case Serializable, SerializableIterator:
formats = append(formats, FormatJSON)
case Table, TableIterator:
formats = append(formats, FormatTab, FormatCSV)
case Text:
formats = append(formats, FormatText)
}
return formats
}

func supportsFormat(data any, f Format) bool {
for _, format := range formatsFor(data) {
if format == f {
return true
}
}
return false
}
42 changes: 42 additions & 0 deletions pkg/output/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package output

// A Table represents a tabular data that can also be printed as CSV.
// Suitable for small tables that fit into memory.
type Table interface {
Header() []string
Rows() []TableRow
}

// A TableIterator that can efficiently be printed as large table or CSV.
// Suitable for larger tables that do not fit into memory.
type TableIterator interface {
// Header returns the header of the table
Header() []any
// NextRow returns a new TableRow until the error is io.EOF
NextRow() (TableRow, error)
}

// A TableRow represents all data that belongs to a table row.
type TableRow interface {
// Cells returns all table cells for this row. This is used to
// print tabular formats such csv. The returned slice has the same
// length as the header slice returned by the parent TableIterator.
Cells() []any
}

// Serializable represents data that can be converted to JSON or YAML.
type Serializable interface {
// SerializableData returns arbitrary data that can be converted to formats like JSON or YAML.
SerializableData() any
}

// SerializableIterator represents a stream of data that can be converted to JSON or YAML.
type SerializableIterator interface {
NextSerializable() (any, error)
}

// Text represents a multi line text that can be printed but is not a table or another
// structured format such as JSON or YAML.
type Text interface {
Text() (string, error)
}
59 changes: 9 additions & 50 deletions pkg/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,8 @@ import (
"text/tabwriter"
)

// Format describes a printable data representation.
type Format string

const (
FormatJSON = "json"
FormatCSV = "csv"
FormatTab = "tab"
)

func (f *Format) Validate() error {
switch *f {
case FormatJSON, FormatTab, FormatCSV:
return nil
default:
return errors.New("output format is expected to be 'json', 'tab', or 'csv'")
}
}

// A Table that can be printed / encoded in different output formats.
type Table interface {
// Header returns the header of the table
Header() []any
// NextRow returns a new TableRow until the error is io.EOF
NextRow() (TableRow, error)
}

// SerializableData represents table data that can be converted to JSON.
type SerializableData interface {
// Data returns the table data suitable for structured data formats
// such as json.
Data() any
}

// A TableRow represents all data that belongs to a table row.
type TableRow interface {
// Cells returns all table cells for this row. This is used to
// print tabular formats such csv. The returned slice has the same
// length as the header slice returned by the parent Table.
Cells() []any
}

// PrintTable writes the Table data to w using the provided format.
func PrintTable(w io.Writer, f Format, data Table) error {
// PrintTable writes the TableIterator data to w using the provided format.
func PrintTable(w io.Writer, f Format, data TableIterator) error {
switch f {
case FormatJSON:
return printJSON(w, data)
Expand All @@ -66,7 +25,7 @@ func PrintTable(w io.Writer, f Format, data Table) error {
}
}

func printTab(w io.Writer, data Table) error {
func printTab(w io.Writer, data TableIterator) error {
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)

formatBuilder := strings.Builder{}
Expand Down Expand Up @@ -97,7 +56,7 @@ func printTab(w io.Writer, data Table) error {
return tw.Flush()
}

func printCSV(w io.Writer, data Table) error {
func printCSV(w io.Writer, data TableIterator) error {
cw := csv.NewWriter(w)
cw.Comma = ';'

Expand Down Expand Up @@ -128,11 +87,11 @@ func printCSV(w io.Writer, data Table) error {
return cw.Error()
}

func printJSON(w io.Writer, data Table) error {
if serializable, ok := data.(SerializableData); ok {
func printJSON(w io.Writer, data TableIterator) error {
if serializable, ok := data.(Serializable); ok {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(serializable.Data())
return enc.Encode(serializable.SerializableData())
}

_, err := fmt.Fprintln(w, "[")
Expand All @@ -153,13 +112,13 @@ func printJSON(w io.Writer, data Table) error {
if err != nil {
return err
}
serializableRow, ok := row.(SerializableData)
serializableRow, ok := row.(Serializable)
if !ok {
return errors.New("JSON not supported for sub command")
}

buf.Reset()
err = json.NewEncoder(buf).Encode(serializableRow.Data())
err = json.NewEncoder(buf).Encode(serializableRow.SerializableData())
if err != nil {
return err
}
Expand Down

0 comments on commit 30bba24

Please sign in to comment.