tabular

package
v0.22.1 Latest Latest
Warning

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

Go to latest
Published: May 7, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// TagTabular is the struct tag name for tabular field configuration.
	TagTabular = "tabular"

	// Tag attributes for field configuration.
	AttrDive      = "dive"      // Recursively parse embedded struct
	AttrName      = "name"      // Column name (header)
	AttrWidth     = "width"     // Column width (for display/export hints)
	AttrOrder     = "order"     // Column order
	AttrDefault   = "default"   // Default value for import
	AttrFormat    = "format"    // Format template (date/number format)
	AttrFormatter = "formatter" // Custom formatter name for export
	AttrParser    = "parser"    // Custom parser name for import

	// Special tag value.
	IgnoreField = "-" // Ignore this field
)

Variables

View Source
var (
	// ErrUnsupportedType indicates the target type is not supported by the parser.
	ErrUnsupportedType = errors.New("unsupported type")
	// ErrDataMustBeSlice indicates the input data for export is not a slice.
	ErrDataMustBeSlice = errors.New("data must be a slice")
	// ErrNoDataRowsFound indicates the source contains no usable data rows.
	ErrNoDataRowsFound = errors.New("no data rows found")
	// ErrDuplicateColumnKey indicates a duplicate Key in a []ColumnSpec.
	ErrDuplicateColumnKey = errors.New("duplicate column key")
	// ErrDuplicateHeaderName indicates a duplicate header name during mapping.
	ErrDuplicateHeaderName = errors.New("duplicate header name")
	// ErrUnsetField indicates a struct field cannot be set (usually unexported).
	ErrUnsetField = errors.New("field is not settable")
	// ErrRequiredMissing indicates a required cell is empty.
	ErrRequiredMissing = errors.New("required value is missing")
	// ErrUnknownColumn indicates a column key is not present in the schema.
	ErrUnknownColumn = errors.New("unknown column")
	// ErrSchemaMismatch indicates the provided data does not match the schema
	// required by the adapter (e.g. wrong element type).
	ErrSchemaMismatch = errors.New("schema mismatch")
	// ErrMissingColumnType indicates a dynamic column spec has no target Type.
	ErrMissingColumnType = errors.New("column type is required")
	// ErrMissingColumnKey indicates a dynamic column spec has no Key.
	ErrMissingColumnKey = errors.New("column key is required")
	// ErrTypedRowMismatch indicates a TypedImporter received rows whose element
	// type does not match the requested generic parameter.
	ErrTypedRowMismatch = errors.New("importer returned unexpected row type")
)

Functions

func BuildHeaderMapping added in v0.22.0

func BuildHeaderMapping(
	headerRow []string, schema *Schema, opts MappingOptions,
) (map[int]int, error)

BuildHeaderMapping resolves a header row against the schema and returns a map from source column index to schema column index. Unknown headers are skipped; duplicate non-empty headers produce ErrDuplicateHeaderName.

func DefaultPositionalMapping added in v0.22.0

func DefaultPositionalMapping(schema *Schema) map[int]int

DefaultPositionalMapping returns a 1:1 index mapping suitable when the source has no header row.

func IsEmptyRow added in v0.22.0

func IsEmptyRow(cells []string, trimSpace bool) bool

IsEmptyRow reports whether every cell in the row is empty. When trimSpace is true, surrounding whitespace is ignored.

Types

type CellValidator added in v0.22.0

type CellValidator func(column *Column, value any) error

CellValidator validates a single cell value within the context of a column. It is primarily used by dynamic (map-based) schemas; struct schemas typically rely on the standard `validate` tag via the framework validator.

type Column

type Column struct {
	// Key is the logical identifier of the column. For struct schemas it is the
	// field name; for dynamic schemas it is the map key that addresses the cell.
	Key string
	// Name is the header text that appears in the exported file and is matched
	// against the header row during import. Defaults to Key when empty.
	Name string
	// Type is the target Go type used when parsing cell values. For struct
	// schemas it is derived from the struct field type; for dynamic schemas it
	// must be provided via ColumnSpec.
	Type reflect.Type
	// Order controls the column order. Columns are sorted stably by Order.
	Order int
	// Width hints the column width for formats that support it (Excel).
	Width float64
	// Default is the value used during import when the source cell is empty.
	Default string
	// Format is the format template consumed by the default Formatter/Parser,
	// e.g. "2006-01-02" for dates or "%.2f" for floats.
	Format string
	// Formatter is the name of a custom Formatter registered on the exporter.
	Formatter string
	// Parser is the name of a custom ValueParser registered on the importer.
	Parser string
	// FormatterFn is a Formatter instance bound directly on the column, taking
	// precedence over the named Formatter registry.
	FormatterFn Formatter
	// ParserFn is a ValueParser instance bound directly on the column, taking
	// precedence over the named ValueParser registry.
	ParserFn ValueParser
	// Required marks the column as mandatory; empty cells trigger a validation
	// error during import. Applies to dynamic schemas.
	Required bool
	// Validators is an ordered list of custom cell validators. Applies to
	// dynamic schemas.
	Validators []CellValidator
	// Index is the struct field index path used by StructAdapter. nil for
	// dynamic columns.
	Index []int
}

Column represents metadata for a single column in tabular data. Columns are shared between struct-based and dynamic schemas.

External code should construct Column instances through schema factories (NewSchema, NewSchemaFor, NewSchemaFromSpecs); fields are exported so that adapters can populate them, not for direct mutation by callers.

type ColumnMapping added in v0.22.0

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

ColumnMapping holds a header-to-schema index mapping together with a pre-sorted key list so that ParseRow does not re-sort on every row.

func NewColumnMapping added in v0.22.0

func NewColumnMapping(m map[int]int) ColumnMapping

NewColumnMapping creates a ColumnMapping from a raw map, sorting the source indices once at construction time.

type ColumnSpec added in v0.22.0

type ColumnSpec struct {
	// Key is the logical identifier and the map key used to read/write cell
	// values. Required and must be unique within the schema.
	Key string
	// Name is the header text. Defaults to Key when empty.
	Name string
	// Type is the Go target type used for parsing. Required.
	Type reflect.Type
	// Order controls the column order; columns are sorted stably.
	Order int
	// Width hints the column width for Excel export.
	Width float64
	// Default is used during import when the cell is empty.
	Default string
	// Format template consumed by the default Formatter/Parser.
	Format string
	// Formatter / Parser names resolved against the exporter / importer
	// registries.
	Formatter string
	Parser    string
	// FormatterFn / ParserFn bind implementations directly; they take
	// precedence over the named lookups.
	FormatterFn Formatter
	ParserFn    ValueParser
	// Required marks the column as mandatory during import.
	Required bool
	// Validators runs additional cell validation after parsing.
	Validators []CellValidator
}

ColumnSpec describes a dynamic column at runtime. It is converted to a Column by NewSchemaFromSpecs.

type ExportError

type ExportError struct {
	Row    int
	Column string
	Field  string
	Err    error
}

ExportError represents an error that occurred during data export. Row is 0-based data row index.

func (ExportError) Error

func (e ExportError) Error() string

Error implements the error interface.

func (ExportError) Unwrap

func (e ExportError) Unwrap() error

Unwrap returns the underlying error.

type Exporter

type Exporter interface {
	// RegisterFormatter registers a custom formatter referenced by Column.Formatter.
	RegisterFormatter(name string, formatter Formatter)
	// ExportToFile exports data to a tabular file on disk.
	ExportToFile(data any, filename string) error
	// Export exports data to a bytes.Buffer carrying tabular content.
	Export(data any) (*bytes.Buffer, error)
}

Exporter writes a slice of rows (struct or map) to a tabular destination (CSV, Excel, ...) Using a RowAdapter to read from the input.

type Formatter

type Formatter interface {
	// Format converts a Go value to a cell string
	Format(value any) (string, error)
}

Formatter defines the interface for custom value formatters. Formatters convert Go values to cell strings during export.

func NewDefaultFormatter

func NewDefaultFormatter(format string) Formatter

NewDefaultFormatter creates a default formatter with optional format template.

func ResolveFormatter added in v0.22.0

func ResolveFormatter(col *Column, registry map[string]Formatter) Formatter

ResolveFormatter selects the Formatter to use for a column.

Precedence:

  1. Column.FormatterFn (direct instance override)
  2. Column.Formatter (name lookup in registry)
  3. NewDefaultFormatter(Column.Format)

If the name is set but not present in the registry, it is logged and the default formatter is returned so exports keep working.

type FormatterFunc added in v0.22.0

type FormatterFunc func(value any) (string, error)

FormatterFunc adapts a plain function to the Formatter interface.

func (FormatterFunc) Format added in v0.22.0

func (f FormatterFunc) Format(value any) (string, error)

Format calls the wrapped function.

type ImportError

type ImportError struct {
	Row    int
	Column string
	Field  string
	Err    error
}

ImportError represents an error that occurred during data import. Row is 1-based and includes the header row.

Err may carry multiple leaf errors joined via errors.Join when a single row produces several failures (e.g. multiple Required misses or both a cell validator and a row validator failing). Use errors.Is on the ImportError to match a specific cause; to enumerate all leaves, assert Err against the interface{ Unwrap() []error } interface.

func ParseRow added in v0.22.0

func ParseRow(
	cells []string,
	mapping ColumnMapping,
	schema *Schema,
	builder RowBuilder,
	parsers map[string]ValueParser,
	rowNumber int,
	opts ParseRowOptions,
) []ImportError

ParseRow walks a single source row and writes parsed cells into the row builder. The mapping translates source column index to schema column index. Cells are read in sorted source-index order so that per-row import errors are deterministic.

RowNumber is the human-facing row number (1-based, including the header row) used when constructing ImportError entries. Empty cells fall back to the column Default; cells that remain empty are skipped so that adapters (e.g. MapAdapter) can distinguish absent values from explicit zeroes and enforce Required.

When the returned slice is non-empty the row builder is in a partial state and should NOT be committed. Callers should skip Commit and collect the errors instead.

func (ImportError) Error

func (e ImportError) Error() string

Error implements the error interface.

func (ImportError) Unwrap

func (e ImportError) Unwrap() error

Unwrap returns the underlying error.

type Importer

type Importer interface {
	// RegisterParser registers a custom parser referenced by Column.Parser.
	RegisterParser(name string, parser ValueParser)
	// ImportFromFile imports data from a tabular file on disk.
	ImportFromFile(filename string) (any, []ImportError, error)
	// Import imports data from an io.Reader carrying tabular content.
	Import(reader io.Reader) (any, []ImportError, error)
}

Importer reads tabular data (CSV, Excel, ...) Into a slice of rows produced by a RowAdapter. The shape of the returned data depends on the adapter: struct adapters return []T, map adapters return []map[string]any.

type MapOption added in v0.22.0

type MapOption func(*mapAdapter)

MapOption customizes MapAdapter behavior.

func WithRowValidator added in v0.22.0

func WithRowValidator(validator RowValidator) MapOption

WithRowValidator registers a validator that runs after all cells have been populated for a map-shaped row.

type MappingOptions added in v0.22.0

type MappingOptions struct {
	// TrimSpace trims leading/trailing whitespace from each header cell.
	TrimSpace bool
}

MappingOptions controls header-to-schema resolution.

type ParseRowOptions added in v0.22.0

type ParseRowOptions struct {
	// TrimSpace strips leading/trailing whitespace from each source cell
	// before applying defaults.
	TrimSpace bool
}

ParseRowOptions controls cell-level normalization performed by ParseRow.

type ParserFunc added in v0.22.0

type ParserFunc func(cellValue string, targetType reflect.Type) (any, error)

ParserFunc adapts a plain function to the ValueParser interface.

func (ParserFunc) Parse added in v0.22.0

func (p ParserFunc) Parse(cellValue string, targetType reflect.Type) (any, error)

Parse calls the wrapped function.

type RowAdapter added in v0.22.0

type RowAdapter interface {
	// Schema returns the column definitions this adapter is built for.
	Schema() *Schema
	// Reader wraps the caller-provided data (e.g. []Struct, []map[string]any)
	// into an iterable sequence of read-only row views for export.
	Reader(data any) (RowReader, error)
	// Writer creates a result accumulator used by importers. The capacity hint
	// is advisory; adapters may ignore it.
	Writer(capacity int) RowWriter
}

RowAdapter is the single bridge between the tabular engine (csv / excel importers and exporters) and any row model. Each adapter owns its Schema so callers only pass the adapter around; schema/model mismatches are impossible by construction.

func NewMapAdapter added in v0.22.0

func NewMapAdapter(schema *Schema, opts ...MapOption) RowAdapter

NewMapAdapter creates a RowAdapter driven by the supplied Schema. The schema is typically built from a []ColumnSpec via NewSchemaFromSpecs.

func NewMapAdapterFromSpecs added in v0.22.0

func NewMapAdapterFromSpecs(specs []ColumnSpec, opts ...MapOption) (RowAdapter, error)

NewMapAdapterFromSpecs is a convenience helper that turns dynamic column specs directly into a MapAdapter, applying any MapAdapter options.

func NewStructAdapter added in v0.22.0

func NewStructAdapter(typ reflect.Type) RowAdapter

NewStructAdapter creates a RowAdapter bound to the given struct type. Non-struct types produce a schema with no columns; the resulting adapter still satisfies the interface but Reader/Writer calls will return zero rows.

func NewStructAdapterFor added in v0.22.0

func NewStructAdapterFor[T any]() RowAdapter

NewStructAdapterFor creates a StructAdapter for the concrete type T.

type RowBuilder added in v0.22.0

type RowBuilder interface {
	// Set writes a parsed value for the given column.
	Set(column *Column, value any) error
	// Validate runs adapter-specific validation hooks (e.g. struct validator
	// or per-column cell validators plus row validators for maps).
	Validate() error
	// Value returns the underlying row representation (struct or map) in its
	// current state.
	Value() any
}

RowBuilder represents a single row under construction during import.

type RowReader added in v0.22.0

type RowReader interface {
	// All yields 0-based row index and a RowView for each row.
	All() iter.Seq2[int, RowView]
}

RowReader iterates over rows of export input.

type RowValidator added in v0.22.0

type RowValidator func(row map[string]any) error

RowValidator validates a complete map-shaped row after all cells have been set. It is used by MapAdapter; struct schemas validate the whole struct instead.

type RowView added in v0.22.0

type RowView interface {
	// Get returns the raw Go value held for the column. It may be nil for
	// missing map keys or zero struct fields.
	Get(column *Column) (any, error)
}

RowView exposes a read-only cell accessor for a single row.

type RowWriter added in v0.22.0

type RowWriter interface {
	// NewRow creates a fresh, writable row builder.
	NewRow() RowBuilder
	// Commit validates and appends the given row to the result set.
	Commit(row RowBuilder) error
	// Build returns the final aggregated result (e.g. []T or []map[string]any).
	Build() any
}

RowWriter accumulates imported rows and produces the final result.

type Schema

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

Schema contains the pre-parsed tabular metadata describing the columns of a tabular row model. It is produced either from a struct type or from a []ColumnSpec describing dynamic columns.

func NewSchema

func NewSchema(typ reflect.Type) *Schema

NewSchema creates a Schema instance by parsing struct fields with tabular tags.

func NewSchemaFor

func NewSchemaFor[T any]() *Schema

NewSchemaFor creates a Schema instance from type T.

func NewSchemaFromSpecs added in v0.22.0

func NewSchemaFromSpecs(specs []ColumnSpec) (*Schema, error)

NewSchemaFromSpecs builds a Schema from a slice of dynamic column specs. It validates that every spec has a non-empty Key and Type, and that keys and resolved names are unique.

func (*Schema) ColumnByKey added in v0.22.0

func (s *Schema) ColumnByKey(key string) (*Column, bool)

ColumnByKey returns the column addressed by the given logical key.

func (*Schema) ColumnByName added in v0.22.0

func (s *Schema) ColumnByName(name string) (*Column, bool)

ColumnByName returns the column with the given header name.

func (*Schema) ColumnCount

func (s *Schema) ColumnCount() int

ColumnCount returns the number of columns.

func (*Schema) ColumnNames

func (s *Schema) ColumnNames() []string

ColumnNames returns all column header names.

func (*Schema) Columns

func (s *Schema) Columns() []*Column

Columns returns all columns in the schema.

type TypedExporter added in v0.22.0

type TypedExporter[T any] struct {
	// contains filtered or unexported fields
}

TypedExporter wraps an Exporter to accept strongly-typed []T input, keeping export call sites free of any-typed values.

func NewTypedExporter added in v0.22.0

func NewTypedExporter[T any](inner Exporter) TypedExporter[T]

NewTypedExporter wraps an existing Exporter with strongly-typed input.

func (TypedExporter[T]) Export added in v0.22.0

func (e TypedExporter[T]) Export(rows []T) (*bytes.Buffer, error)

Export exports rows to an in-memory buffer.

func (TypedExporter[T]) ExportToFile added in v0.22.0

func (e TypedExporter[T]) ExportToFile(rows []T, filename string) error

ExportToFile exports rows to a file on disk.

func (TypedExporter[T]) Inner added in v0.22.0

func (e TypedExporter[T]) Inner() Exporter

Inner returns the underlying untyped exporter for advanced usage such as registering custom formatters without going through the typed wrapper.

func (TypedExporter[T]) RegisterFormatter added in v0.22.0

func (e TypedExporter[T]) RegisterFormatter(name string, formatter Formatter)

RegisterFormatter delegates to the underlying exporter.

type TypedImporter added in v0.22.0

type TypedImporter[T any] struct {
	// contains filtered or unexported fields
}

TypedImporter wraps an Importer to return strongly-typed []T results, removing the need for callers to perform a type assertion on each call. It is intended for struct-backed adapters; map adapters return []map[string]any and should keep using the untyped Importer.

func NewTypedImporter added in v0.22.0

func NewTypedImporter[T any](inner Importer) TypedImporter[T]

NewTypedImporter wraps an existing Importer with strongly-typed result access. The underlying importer must produce []T values; otherwise Import and ImportFromFile will return a type-mismatch error at call time.

func (TypedImporter[T]) Import added in v0.22.0

func (i TypedImporter[T]) Import(reader io.Reader) ([]T, []ImportError, error)

Import imports rows from reader and returns them as []T.

func (TypedImporter[T]) ImportFromFile added in v0.22.0

func (i TypedImporter[T]) ImportFromFile(filename string) ([]T, []ImportError, error)

ImportFromFile imports rows from disk and returns them as []T.

func (TypedImporter[T]) Inner added in v0.22.0

func (i TypedImporter[T]) Inner() Importer

Inner returns the underlying untyped importer for advanced usage such as registering custom parsers without going through the typed wrapper.

func (TypedImporter[T]) RegisterParser added in v0.22.0

func (i TypedImporter[T]) RegisterParser(name string, parser ValueParser)

RegisterParser delegates to the underlying importer.

type ValueParser

type ValueParser interface {
	// Parse converts a cell string to a Go value
	Parse(cellValue string, targetType reflect.Type) (any, error)
}

ValueParser defines the interface for custom value parsers. Parsers convert cell strings to Go values during import.

func NewDefaultParser

func NewDefaultParser(format string) ValueParser

NewDefaultParser creates a default parser with optional format template.

func ResolveParser added in v0.22.0

func ResolveParser(col *Column, registry map[string]ValueParser) ValueParser

ResolveParser selects the ValueParser to use for a column using the same precedence rules as ResolveFormatter.

Jump to

Keyboard shortcuts

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