format

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package format bridges Codec[T] to concrete serialization formats (JSON, YAML, TOML, Gob).

A codec works with an intermediate representation (map[string]any) that is format-agnostic. Format wraps that intermediate layer so the same codec can read and write multiple wire formats without any changes to the codec itself.

Text-based formats (JSON, YAML, TOML) pass through the map[string]any intermediate. Binary and typed formats (Gob) bypass the intermediate and operate on the typed value directly via the marshalTyped/unmarshalTyped path.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrNotStreamable = errors.New("format: not streamable — use NewStreamed to create a streaming format")

ErrNotStreamable is returned by Format.MarshalTo when the format was not created with NewStreamed and therefore does not support streaming output.

Functions

func FromEnv added in v0.3.0

func FromEnv[T any](c codex.Codec[T], prefix string) (T, error)

FromEnv loads T from environment variables using schema-driven type coercion.

Naming convention: strings.ToUpper(prefix + field_name). Underscores in field names are preserved:

field "log_level"  + prefix "APP_" → "APP_LOG_LEVEL"
field "db"         + prefix "APP_" → recurse with prefix "APP_DB_"
nested field "host"                → "APP_DB_HOST"

Supported types (determined from the codec's schema):

flat primitives — direct env var, string coerced by schema type
nested structs  — prefix expansion (APP_DB_HOST) OR JSON object (APP_DB='{"host":"..."}')
slices          — comma-separated (APP_TAGS=a,b,c) OR JSON array (APP_TAGS='["a","b","c"]')
StringMap       — JSON object only (APP_LABELS='{"k":"v"}')
Nullable[T]     — absent = nil; present = coerce as inner type

JSON detection: when a field's env var is set and the value starts with '{' or '[' matching the field's schema type, it is parsed as JSON. JSON takes precedence over prefix expansion and comma-split when both would apply (e.g. APP_DB='{...}' takes priority over APP_DB_HOST=...).

Silently skipped: TaggedUnion, slices of objects.

Errors are returned as codex.ValidationErrors. Parse errors (an env var is set but its value cannot be coerced to the field's type) are collected and returned before the codec's Decode runs. Missing required fields and constraint violations are reported by Decode in the same error shape.

Types

type Format

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

Format binds a Codec[T] to a specific serialization format. Use JSON, YAML, TOML, or Gob to construct one. For formats that operate on the typed value directly (e.g. HTML rendering), use NewTyped. For streaming output (write to io.Writer without buffering), use NewStreamed.

func Gob added in v0.9.0

func Gob[T any](c codex.Codec[T]) Format[T]

Gob returns a Format[T] that serialises T using encoding/gob.

Unlike JSON, YAML, and TOML — which pass a map[string]any intermediate through the codec — Gob encodes and decodes the typed value directly. Codec constraints are enforced on both marshal (before encoding) and unmarshal (after decoding).

Suitable for internal Go-to-Go communication: forge pipelines, MQTT between Go services, and binary caching. Not suitable for REST content negotiation, human-readable output, or cross-language interoperability.

Requirements and limitations:

  • All struct fields that gob encodes must be exported; unexported fields are silently skipped.
  • For interface values, register concrete types with encoding/gob.Register before use.
  • "application/gob" is a conventional content type; it is not an IANA-registered MIME type.
  • Observability is handled by the adapter layer — no special configuration needed.

ContentType is "application/gob".

func JSON

func JSON[T any](c codex.Codec[T]) Format[T]

JSON returns a Format that reads and writes JSON. ContentType is "application/json".

Example
package main

import (
	"fmt"

	"github.com/DaniDeer/go-codex/codex"
	"github.com/DaniDeer/go-codex/format"
)

func main() {
	type Item struct {
		Name  string
		Price float64
	}

	itemCodec := codex.Struct[Item](
		codex.RequiredField("name", codex.String(),
			func(i Item) string { return i.Name },
			func(i *Item, v string) { i.Name = v },
		),
		codex.RequiredField("price", codex.Float64(),
			func(i Item) float64 { return i.Price },
			func(i *Item, v float64) { i.Price = v },
		),
	)

	j := format.JSON(itemCodec)

	// Marshal a Go value to JSON bytes.
	data, _ := j.Marshal(Item{Name: "Widget", Price: 9.99})
	fmt.Println(string(data))

	// Unmarshal JSON bytes back to the typed value.
	item, _ := j.Unmarshal(data)
	fmt.Printf("%s: %.2f\n", item.Name, item.Price)
}
Output:
{"name":"Widget","price":9.99}
Widget: 9.99

func New

func New[T any](c codex.Codec[T], marshal func(any) ([]byte, error), unmarshal func([]byte) (any, error)) Format[T]

New creates a Format from a codec and custom marshal/unmarshal functions. Use this to integrate formats not covered by the built-in constructors. ContentType is empty by default; call Format.WithContentType to set it.

func NewStreamed added in v0.8.0

func NewStreamed[T any](c codex.Codec[T], marshalTo func(T, io.Writer) error, unmarshal func([]byte) (T, error), contentType string) Format[T]

NewStreamed creates a Format where responses are written directly to an io.Writer without buffering to a []byte intermediate. This enables chunked or streaming responses for large payloads (HTML pages, JSON arrays, CSV exports).

The codec is used for validation: Format.MarshalTo runs all Refine constraints on the value before calling marshalTo, so invalid data is rejected before any bytes are written.

unmarshal is used for Format.Unmarshal (reading streaming formats is rarely needed; pass a function that returns an error when not applicable).

Use Format.IsStreamable to detect streaming formats. The adapter calls Format.MarshalTo instead of Format.Marshal and writes response headers before streaming, so partial output is never flushed on validation failure.

Example — streaming a templ component without buffering:

streamFmt := format.NewStreamed(
    propsCodec,
    func(props Props, w io.Writer) error {
        return component(props).Render(context.Background(), w)
    },
    func([]byte) (Props, error) {
        var zero Props
        return zero, errors.New("HTML is not decodable")
    },
    "text/html; charset=utf-8",
)

func NewTyped added in v0.8.0

func NewTyped[T any](c codex.Codec[T], marshal func(T) ([]byte, error), unmarshal func([]byte) (T, error), contentType string) Format[T]

NewTyped creates a Format where the marshal and unmarshal functions operate on the typed value directly, rather than on the intermediate representation. The codec is still used for validation: Format.Marshal runs all Refine constraints on the value before calling marshal, and Format.Validate works as normal.

Use this when the wire format cannot be represented via a map[string]any intermediate — for example, rendering HTML via a templ component:

htmlFormat := format.NewTyped(
    propsCodec,
    func(props Props) ([]byte, error) {
        var buf bytes.Buffer
        err := component(props).Render(context.Background(), &buf)
        return buf.Bytes(), err
    },
    func([]byte) (Props, error) {
        var zero Props
        return zero, errors.New("HTML is not decodable")
    },
    "text/html; charset=utf-8",
)

func TOML

func TOML[T any](c codex.Codec[T]) Format[T]

TOML returns a Format that reads and writes TOML. ContentType is "application/toml".

func YAML

func YAML[T any](c codex.Codec[T]) Format[T]

YAML returns a Format that reads and writes YAML. ContentType is "application/yaml".

func (Format[T]) ContentType added in v0.8.0

func (f Format[T]) ContentType() string

ContentType returns the MIME type associated with this format (e.g. "application/json"). Empty string means the format has no registered content type.

func (Format[T]) IsStreamable added in v0.8.0

func (f Format[T]) IsStreamable() bool

IsStreamable reports whether this format supports streaming output via Format.MarshalTo. Streaming formats are created with NewStreamed.

func (Format[T]) Marshal

func (f Format[T]) Marshal(v T) ([]byte, error)

Marshal encodes v to bytes using the codec and then the format serializer. If the format was created with NewTyped, the codec validates v first and then the typed marshal function is called directly (bypassing the intermediate). For streaming formats created with NewStreamed, use Format.MarshalTo instead.

func (Format[T]) MarshalTo added in v0.8.0

func (f Format[T]) MarshalTo(v T, w io.Writer) error

MarshalTo validates v via the codec and then writes the serialized form directly to w without buffering to a []byte intermediate. Use this with formats created via NewStreamed; call Format.IsStreamable first. Returns ErrNotStreamable if the format has no streaming marshal function. Validation errors are returned before any bytes are written to w.

func (Format[T]) Schema

func (f Format[T]) Schema() schema.Schema

Schema returns the schema.Schema from the underlying codec.

func (Format[T]) Unmarshal

func (f Format[T]) Unmarshal(data []byte) (T, error)

Unmarshal deserializes data into an intermediate and then decodes it via the codec. If the format was created with NewTyped, the typed unmarshal function is used directly.

func (Format[T]) Validate

func (f Format[T]) Validate(v T) error

Validate checks v against the codec's constraints without serializing to bytes. It delegates to Codec.Validate — see its documentation for the rationale.

func (Format[T]) WithContentType added in v0.8.0

func (f Format[T]) WithContentType(ct string) Format[T]

WithContentType returns a copy of the format with the given MIME content type set. Use this when registering custom formats for content negotiation.

Jump to

Keyboard shortcuts

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