csvx

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2026 License: MIT Imports: 19 Imported by: 0

README

CSVX Go Engine

csvx-go is a Go library — the programmable interface for CSVX (load/edit/calculate/write). It is specification-first: it must conform to CSVX behavior, but its internal architecture does not define the format. This repo is a library only — its CLI was split into csvx-cli, which depends on this repo as an ordinary Go module. See AGENTS.md and ../csvx-spec/AGENTS.md for why that split exists.

Current scope

  • CSVX ZIP package loading and writing (Open, Load, WritePackage)
  • Unpacked CSVX directory loading/packaging (OpenDirectory, PackageDirectory, ExtractPackage)
  • UTF-8 CSV sheet loading, RFC 4180-compatible
  • Optional .meta.json sheet metadata: formulas, cached values, per-cell styles, validation
  • Style generated from ../csvx-spec/schemas/styles.schema.json (see internal/schema/), not hand-typed — see AGENTS.md for why that matters
  • XLSX→CSVX import and unmodified-source recovery (Convert)
  • Duplicate and unsafe ZIP entry rejection
  • Basic structural validation (Validate)

Not yet implemented: formula parsing/recalculation, general CSVX→XLSX export of arbitrary/edited content (only unmodified-source recovery exists today), full schema-conformance validation (that lives in ../csvx-spec/validator for now). See handoff.md for current known limitations.

Development rule

Each capability follows this sequence:

Specify → create conformance fixtures → implement → run tests

The specification repository is the authority: ../csvx-spec/.

Usage

workbook, err := csvx.Open("report.csvx")
if err != nil {
    return err
}
fmt.Println(workbook.Sheets[0].Records)

CSV is the canonical sheet data layer. Metadata that CSV cannot represent is stored in the matching .meta.json sidecar.

To exercise this library from the command line, use csvx-cli.

Development

go build ./...                              # build
go vet ./...                                # static analysis
go test ./...                               # run all tests
go test -race ./...                         # with the race detector
go test -run TestLoadCSVBackedWorkbook ./... # a specific test
gofmt -w .                                   # format before committing
go mod tidy                                  # after changing imports — review the diff
Pre-push checks (local, since GitHub Actions minutes are limited)

scripts/check.sh runs the same checks CI would (gofmt, go vet, go build, go test). Run it any time:

./scripts/check.sh

A git hook runs it automatically before every push, blocking the push if it fails. Enable it once per clone (this is local git config, not something that comes from cloning the repo):

git config core.hooksPath .githooks

Skip in a genuine emergency with git push --no-verify — prefer fixing the failure instead. See ../csvx-spec/AGENTS.md section 6 for why this exists: it's the interim stand-in for real CI.

Development workflow

  1. Update the relevant specification in ../csvx-spec/.
  2. Add or update a conformance fixture.
  3. Implement the behavior in this library.
  4. Run ./scripts/check.sh (or let the pre-push hook do it).
  5. Document any intentionally unsupported behavior in handoff.md.
  6. Confirm that CSV data and metadata sidecars remain round-trip safe.

Do not treat engine behavior as a specification change without updating the specification repository.

Documentation

Overview

Package csvx provides a specification-first reader and writer for CSVX workbooks.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ColumnWidthToPixels added in v0.1.2

func ColumnWidthToPixels(width float64) int

ColumnWidthToPixels converts an XLSX character-width unit (Column.width) to CSS pixels.

func Convert

func Convert(input, output string) error

Convert imports or exports a workbook based on the input and output extensions.

func ExtractPackage

func ExtractPackage(filename, directory string) error

ExtractPackage extracts a CSVX ZIP package into an unpacked directory.

func FormatValue added in v0.1.1

func FormatValue(value Value, numberFormat string) string

FormatValue renders a Value for display using an Excel-style numberFormat code, falling back to the plain value string when the value isn't numeric or the pattern isn't one of the supported shapes. Never mutates or reinterprets value — this is display text only. Mirrors csvx-ts's formatValue exactly; both engines must agree on every case in format_test.go.

func PackageDirectory

func PackageDirectory(directory, output string) error

PackageDirectory writes an unpacked CSVX package directory as a ZIP .csvx file.

func PixelsToColumnWidth added in v0.1.2

func PixelsToColumnWidth(pixels float64) float64

PixelsToColumnWidth converts CSS pixels back to an XLSX character-width unit — the inverse of ColumnWidthToPixels. Rounded to 2 decimal places, matching csvx-ts.

func PixelsToRowHeight added in v0.1.2

func PixelsToRowHeight(pixels float64) float64

PixelsToRowHeight converts CSS pixels back to points — the inverse of RowHeightToPixels.

func RowHeightToPixels added in v0.1.2

func RowHeightToPixels(points float64) int

RowHeightToPixels converts a row height in points (Sheet.rowHeights) to CSS pixels, at the standard 96 DPI / 72-points-per-inch ratio every renderer (and XLSX itself) assumes.

func WritePackage

func WritePackage(workbook *Workbook, output string) error

WritePackage writes a workbook to a deterministic CSVX ZIP package.

Types

type Calculation

type Calculation struct {
	Mode      string `json:"mode,omitempty"`
	Iteration bool   `json:"iteration,omitempty"`
}

Calculation contains workbook calculation settings.

type CellMetadata

type CellMetadata struct {
	Type       string          `json:"type,omitempty"`
	Formula    string          `json:"formula,omitempty"`
	Cached     *Value          `json:"cached,omitempty"`
	Style      string          `json:"style,omitempty"`
	Validation json.RawMessage `json:"validation,omitempty"`
}

CellMetadata contains behavior that cannot be represented in CSV.

func NextCellMetadata added in v0.1.1

func NextCellMetadata(existing CellMetadata, formula string) (CellMetadata, bool)

NextCellMetadata computes the next cell metadata after an edit overwrites a cell's content, per spec/05-cell-values.md: Type, Formula, and Cached describe a cell's *content* and must never survive past what they described, while Style and Validation describe the cell itself and are untouched. Consumer apps must call this rather than deciding for themselves which fields survive an edit (AGENTS.md rule 5.2 — no second opinion about what a CSVX value/type means). Pass a non-empty formula when the new content is a formula (starts with "="); pass "" for a literal edit. The second return value is false when nothing is left worth keeping (no metadata entry needed at all).

Note: CellMetadata in this engine is a closed struct (Type/Formula/Cached/Style/Validation only) rather than an open map, so unlike csvx-ts's nextCellMetadata this cannot yet preserve a field neither engine recognizes — that's an existing gap in this type (see its own doc comment), not one this function introduces.

type Column

type Column struct {
	ID    string  `json:"id"`
	Name  string  `json:"name"`
	Type  string  `json:"type,omitempty"`
	Width float64 `json:"width,omitempty"`
}

Column describes a CSV column and its optional CSVX type.

type Diagnostic

type Diagnostic struct {
	Code     string `json:"code"`
	Path     string `json:"path,omitempty"`
	Message  string `json:"message"`
	Severity string `json:"severity"`
}

Diagnostic describes a validation error or warning at a package resource.

type Manifest

type Manifest struct {
	Format   string   `json:"format"`
	Version  string   `json:"version"`
	Workbook string   `json:"workbook"`
	Files    []string `json:"files"`
}

Manifest identifies a CSVX package and its resources.

type Sheet

type Sheet struct {
	ID           string                     `json:"id"`
	Name         string                     `json:"name"`
	Path         string                     `json:"path"`
	MetadataPath string                     `json:"metadata,omitempty"`
	Columns      []Column                   `json:"columns"`
	Records      [][]string                 `json:"records"`
	RowHeights   map[int]float64            `json:"rowHeights,omitempty"`
	Cells        map[string]CellMetadata    `json:"cells,omitempty"`
	Extra        map[string]json.RawMessage `json:"-"`
}

Sheet is a CSV-backed worksheet. Records excludes the CSV header row.

type SheetEntry

type SheetEntry struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Path     string `json:"path"`
	Metadata string `json:"metadata,omitempty"`
}

SheetEntry locates a sheet's CSV and optional metadata sidecar in a package.

type SourceMetadata

type SourceMetadata struct {
	Format     string            `json:"format"`
	Filename   string            `json:"filename"`
	SHA256     string            `json:"sha256"`
	Authority  string            `json:"authority"`
	ImportedAt string            `json:"importedAt"`
	Importer   string            `json:"importer"`
	Features   XLSXFeatureCounts `json:"features,omitempty"`
	Warnings   []XLSXDiagnostic  `json:"warnings,omitempty"`
}

SourceMetadata describes an embedded external workbook preserved for interoperability.

type Style

Style is a single style record. Its field shape is generated from csvx-spec's styles.schema.json (see internal/schema/generated.go) rather than hand-typed, so it cannot silently drift from the schema the way the previous hand-written map[string]map[string]any representation did.

This is a defined type over the generated struct, not a plain alias, solely so MarshalJSON below can omit the generated AdditionalProperties bucket field until this engine implements real additionalProperties round-tripping on both read and write (styles.json is not read back in anywhere yet, so nothing currently depends on inheriting the generated UnmarshalJSON here).

func (Style) MarshalJSON

func (s Style) MarshalJSON() ([]byte, error)

MarshalJSON serializes a Style without the generated AdditionalProperties bucket field, which encoding/json would otherwise emit as a literal `"AdditionalProperties": null` key — schema- legal (styles.schema.json allows additional properties) but wrong output.

type ValidationResult

type ValidationResult struct {
	Valid    bool         `json:"valid"`
	Errors   []Diagnostic `json:"errors"`
	Warnings []Diagnostic `json:"warnings"`
}

ValidationResult contains machine-readable package validation output.

func Validate

func Validate(filename string) ValidationResult

Validate checks a CSVX ZIP file or unpacked CSVX directory.

type Value

type Value struct {
	Type    string `json:"type"`
	Value   any    `json:"value,omitempty"`
	Code    string `json:"code,omitempty"`
	Message string `json:"message,omitempty"`
}

Value is a typed CSVX value used by formulas, caches, and diagnostics.

func ParseFormattedLiteral added in v0.1.1

func ParseFormattedLiteral(text string, numberFormat string) (Value, bool)

ParseFormattedLiteral is the inverse of FormatValue: it attempts to parse typed literal text back into a numeric Value using a cell's own numberFormat, per spec/08-styles.md's symmetric allowance — e.g. "$7.00" against `"$"#,##0.00` becomes decimal "7.00" instead of falling through to string just because it has a currency symbol. Scoped strictly to the same documented format subset FormatValue supports; this is not general multi-locale currency parsing (see the package comment for why). Returns (Value{}, false) for text that doesn't match the format's own affix/grouping shape, or when there is no numberFormat at all — the caller falls through to its own generic literal rules either way. Mirrors csvx-ts's parseFormattedLiteral.

func ResolveCellValue added in v0.1.1

func ResolveCellValue(raw string, declaredType string, numberFormat string) Value

ResolveCellValue resolves a sheet CSV cell's raw text to a typed Value, per csvx-spec/spec/05-cell-values.md and 03-sheets.md: "Column types provide defaults and validation hints; an individual cell MAY override a column type." This is the one place that decision gets made — consumer apps must call this rather than sniffing "looks like a number" themselves (csvx-spec/AGENTS.md rule 1). declaredType is the resolved cell/column type (a cell's own Type override wins over its column's); pass "" when nothing declares a type at all.

When nothing declares a type (true for any hand-authored or freshly-edited CSVX, as opposed to an exhaustively cell-annotated XLSX import), numberFormat (the cell's resolved style numberFormat, if any) is tried first, per spec/08-styles.md's symmetric parsing allowance — "$7.00" against a `"$"#,##0.00` numberFormat resolves to decimal "7.00" rather than falling through to string just because it looks like currency text (see ParseFormattedLiteral for the documented, bounded subset this covers). Failing that, this falls back to the same narrow, well-established literal-shape inference every CSV-consuming spreadsheet tool uses (blank/boolean/integer/decimal by shape, otherwise string) rather than defaulting everything untyped to "string" and silently breaking formula arithmetic over it. A declared type (including an explicit "string") always wins and is never second-guessed, and never consults numberFormat. Mirrors csvx-ts's resolveCellValue — see that implementation for the TypeScript engine's identical contract.

type Workbook

type Workbook struct {
	ID          string          `json:"id"`
	Version     string          `json:"version"`
	Sheets      []*Sheet        `json:"sheets"`
	Calculation Calculation     `json:"calculation,omitempty"`
	Source      *SourceMetadata `json:"source,omitempty"`
	Styles      []Style         `json:"styles,omitempty"`
	SourceBytes []byte          `json:"-"`
}

Workbook is the canonical in-memory representation of a CSVX workbook.

func Load

func Load(reader io.ReaderAt, size int64) (*Workbook, error)

Load reads a CSVX ZIP package from an io.ReaderAt with the supplied size.

func Open

func Open(filename string) (*Workbook, error)

Open reads a CSVX ZIP package from disk.

func OpenDirectory

func OpenDirectory(directory string) (*Workbook, error)

OpenDirectory reads an unpacked CSVX package directory.

type WorkbookDocument

type WorkbookDocument struct {
	ID          string          `json:"id"`
	Version     string          `json:"version"`
	Sheets      []SheetEntry    `json:"sheets"`
	Calculation Calculation     `json:"calculation,omitempty"`
	Source      *SourceMetadata `json:"source,omitempty"`
	Styles      string          `json:"styles,omitempty"`
}

WorkbookDocument is the serialized workbook resource.

type XLSXDiagnostic

type XLSXDiagnostic struct {
	Severity string `json:"severity"`
	Feature  string `json:"feature"`
	Path     string `json:"path,omitempty"`
	Message  string `json:"message"`
}

XLSXDiagnostic records an interoperability limitation or observation.

type XLSXFeatureCounts

type XLSXFeatureCounts struct {
	SharedStrings int `json:"sharedStrings"`
	Styles        int `json:"styles"`
	NumberFormats int `json:"numberFormats"`
	Drawings      int `json:"drawings"`
	Charts        int `json:"charts"`
	Images        int `json:"images"`
	Comments      int `json:"comments"`
	Persons       int `json:"persons"`
	Macros        int `json:"macros"`
	Relationships int `json:"relationships"`
}

XLSXFeatureCounts summarizes workbook features relevant to CSVX interoperability.

type XLSXInspection

type XLSXInspection struct {
	Format    string            `json:"format"`
	Filename  string            `json:"filename"`
	SHA256    string            `json:"sha256"`
	Sheets    []XLSXSheet       `json:"sheets"`
	Features  XLSXFeatureCounts `json:"features"`
	Resources []string          `json:"resources"`
	Warnings  []XLSXDiagnostic  `json:"warnings"`
}

XLSXInspection describes the portable features and package resources detected in an XLSX file.

func InspectXLSX

func InspectXLSX(filename string) (*XLSXInspection, error)

InspectXLSX reads an XLSX package without executing macros, links, or external resources.

type XLSXSheet

type XLSXSheet struct {
	Name          string `json:"name"`
	Path          string `json:"path"`
	Cells         int    `json:"cells"`
	Formulas      int    `json:"formulas"`
	CachedValues  int    `json:"cachedValues"`
	StyledCells   int    `json:"styledCells"`
	SharedStrings int    `json:"sharedStrings"`
}

XLSXSheet describes one worksheet and its understood cell features.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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