tabular

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 24, 2026 License: MIT Imports: 7 Imported by: 0

README

gobspect/tabular

Package tabular writes gobspect.Value nodes as CSV or TSV rows.

import "github.com/codepuke/gobspect/tabular"

Overview

tabular is designed for presenting decoded gob streams as flat tabular data. It automatically derives a header row from the first struct's field definitions, aligns sparse gob rows to the canonical column order, and provides several strategies for handling streams that contain more than one struct type.

stream := ins.Stream(r)

var buf bytes.Buffer
tp := tabular.NewPrinter(&buf,
    tabular.WithStream(stream),
    tabular.WithDelimiter(','),
)

for v, err := range stream.Values() {
    if err != nil { ... }
    if err := tp.WriteValue(v); err != nil { ... }
}

if err := tp.Flush(); err != nil { ... }

fmt.Print(buf.String())
// Name,Score
// alice,42
// bob,17

Creating a Printer

func NewPrinter(out io.Writer, opts ...Option) *Printer

Defaults: comma delimiter, headers enabled, HeterogeneousFirstWins, BytesHex, no byte truncation.

Options

Option Default Description
WithDelimiter(r rune) ',' Column separator. Use '\t' for TSV.
WithNoHeaders(b bool) false Suppress the header row when true.
WithHeterogeneousMode(m HeterogeneousMode) HeterogeneousFirstWins Controls mixed-type rows.
WithBytesFormat(f gobspect.BytesFormat) gobspect.BytesHex Rendering format for BytesValue cells.
WithMaxBytes(n int) 0 (no limit) Truncate byte slices to at most n bytes before encoding.
WithStream(s *gobspect.Stream) nil Enables canonical column ordering via stream.TypeByID.
WithStream

When a *gobspect.Stream is provided, the printer looks up the canonical type definition for the first struct via stream.TypeByID. This ensures column order matches the original Go struct field declaration order, even when gob's sparse encoding omits zero-valued fields from some rows.

Without a stream, column order is derived from the fields present in the first row.

Writing values

WriteValue
func (p *Printer) WriteValue(v gobspect.Value) error

Writes a single value as a tabular row. On the first call it emits the header row (unless WithNoHeaders was set).

  • Struct values: each field becomes a column. Fields absent from a row (gob omits zero-valued fields) are emitted as empty strings so every row has the same column count as the header.
  • Scalar values: a single-column table with header "value".
  • Interface values: unwrapped automatically before writing.
Flush
func (p *Printer) Flush() error

Flushes the underlying csv.Writer and returns any write error. Always call Flush after all rows have been written.

Heterogeneous modes

When a gob stream contains structs of different types, HeterogeneousMode controls the behavior:

type HeterogeneousMode int

const (
    HeterogeneousFirstWins  HeterogeneousMode = iota // silently drop rows of a different type (default)
    HeterogeneousReject                              // return an error on type change
    HeterogeneousUnion                               // grow the header to include new columns
    HeterogeneousPartition                           // emit a blank line + new header on type change
)
ParseHeterogeneousMode
func ParseHeterogeneousMode(s string) (HeterogeneousMode, bool)

Converts "first", "reject", "union", or "partition" (case-insensitive) to the corresponding constant. An empty string maps to HeterogeneousFirstWins. Returns (HeterogeneousFirstWins, false) for unrecognised values.

mode, ok := tabular.ParseHeterogeneousMode("union")
if !ok { ... }
tp := tabular.NewPrinter(&buf, tabular.WithHeterogeneousMode(mode))
union mode

HeterogeneousUnion grows the header whenever a row introduces a column that has not been seen before. Rows already written cannot be backfilled; they have empty cells for the new columns.

Name,Score
alice,42
Name,Score,Rank   ← re-emitted header after column added
bob,17,3
partition mode

HeterogeneousPartition flushes, emits a blank line, then starts a new section with a fresh header whenever the struct type changes.

Name,Score
alice,42

Name,Level
dave,7
reject mode

HeterogeneousReject returns an error immediately when a row of a different type arrives. The error message suggests using a field projection query to unify columns.

Projection structs

When a value is a projection struct (produced by a gobspect/query projection expression like .Name,Score), the printer uses the struct's own field order and accepts values from any source type without triggering heterogeneous-type logic.

CellString

func CellString(v gobspect.Value) string

Converts a single Value to a flat string for a CSV cell. BytesValue is rendered as lowercase hex. Nested composite types (StructValue, SliceValue, ArrayValue, MapValue) produce descriptive placeholders: "(struct)", "(slice)", etc.

CellString does not respect WithBytesFormat or WithMaxBytes — it always uses hex. Use Printer.WriteValue for format-aware rendering.

Type Output
StringValue raw string value
IntValue / UintValue decimal
FloatValue %g notation
ComplexValue (real+imagi) / (real-imagi)
BoolValue "true" / "false"
NilValue "" (empty)
BytesValue lowercase hex
OpaqueValue with decoded string decoded string
OpaqueValue without decoded value "(opaque)"
InterfaceValue unwrapped and converted recursively
StructValue "(struct)"
SliceValue "(slice)"
ArrayValue "(array)"
MapValue "(map)"

Documentation

Overview

Package tabular writes gobspect.Value nodes as CSV or TSV rows.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CellString

func CellString(v gobspect.Value) string

CellString converts a single Value to a flat string suitable for a CSV cell. BytesValue is rendered as lowercase hex. Complex nested types are squashed to descriptive placeholders. Use Printer.WriteValue for format-aware rendering of bytes (respecting the configured [BytesFormat] and max-bytes limit).

Types

type HeterogeneousMode

type HeterogeneousMode int

HeterogeneousMode controls what the Printer does when a row with a different struct type arrives after the first row has locked the schema.

const (
	// HeterogeneousFirstWins silently drops rows whose type differs from the
	// first row's type.
	HeterogeneousFirstWins HeterogeneousMode = iota

	// HeterogeneousReject returns an error when a row with a different type
	// arrives after the schema has been locked.
	HeterogeneousReject

	// HeterogeneousUnion grows the header when new columns appear. Rows already
	// printed cannot be backfilled; they receive empty cells for new columns.
	HeterogeneousUnion

	// HeterogeneousPartition emits a blank line followed by a new header row
	// whenever the struct type changes, then continues with the new type's schema.
	HeterogeneousPartition
)

func ParseHeterogeneousMode

func ParseHeterogeneousMode(s string) (HeterogeneousMode, bool)

ParseHeterogeneousMode converts a string to a HeterogeneousMode constant. Accepts "first", "reject", "union", or "partition" (case-insensitive). An empty string maps to HeterogeneousFirstWins. Returns (HeterogeneousFirstWins, false) for unrecognised values.

type Option

type Option func(*Printer)

Option is a functional option for NewPrinter.

func WithBytesFormat

func WithBytesFormat(f gobspect.BytesFormat) Option

WithBytesFormat sets the rendering format for BytesValue cells.

func WithDelimiter

func WithDelimiter(r rune) Option

WithDelimiter sets the CSV/TSV column delimiter.

func WithHeterogeneousMode

func WithHeterogeneousMode(m HeterogeneousMode) Option

WithHeterogeneousMode sets the heterogeneous-type handling mode.

func WithMaxBytes

func WithMaxBytes(n int) Option

WithMaxBytes sets the byte-slice truncation limit (0 = no limit).

func WithNoHeaders

func WithNoHeaders(b bool) Option

WithNoHeaders suppresses the header row when b is true.

func WithStream

func WithStream(s *gobspect.Stream) Option

WithStream sets the gobspect.Stream used for canonical type-definition lookups. When nil, column order falls back to the struct's own Fields slice.

type Printer

type Printer struct {
	// contains filtered or unexported fields
}

Printer writes gobspect.Value nodes as CSV or TSV rows.

Column order is determined by the canonical type definition for the first struct received, looked up via stream.TypeByID. For projection structs (TypeName == query.ProjectionTypeName), the struct's own fields define the column order, and heterogeneous source types are accepted without error.

When structs of a locked type arrive with fewer fields than the header (gob sparse encoding omits zero-value fields), the missing columns are emitted as empty strings so every row has the same column count as the header.

The behavior when structs of a different type arrive is controlled by HeterogeneousMode.

func NewPrinter

func NewPrinter(out io.Writer, opts ...Option) *Printer

NewPrinter creates a Printer that writes to out. Defaults: comma delimiter, headers on, HeterogeneousFirstWins, BytesHex, no truncation.

func (*Printer) Flush

func (p *Printer) Flush() error

Flush flushes the underlying CSV writer and returns any write error.

func (*Printer) WriteValue

func (p *Printer) WriteValue(v gobspect.Value) error

WriteValue writes a single Value as a tabular row. On the first call it also emits a header row (unless WithNoHeaders was set).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL