zoobzio January 5, 2025 Edit this page

API Reference

Complete API documentation for the atom package.

Package

import "github.com/zoobz-io/atom"

Functions

Use

func Use[T any]() (*Atomizer[T], error)

Registers and returns an Atomizer for type T.

  • First call builds the atomizer via reflection
  • Subsequent calls return the cached instance
  • Returns error if T contains unsupported field types

Example:

atomizer, err := atom.Use[User]()
if err != nil {
    log.Fatalf("registration failed: %v", err)
}

Errors:

  • "type X: field Y: unsupported type Z" - Field type not supported
  • "type X: field Y: only string map keys are supported" - Non-string map keys not allowed

AllTables

func AllTables() []Table

Returns all table types in canonical order.

Example:

for _, t := range atom.AllTables() {
    fmt.Println(t)
}

TableForField

func TableForField(spec Spec, field string) (Table, bool)

Returns the storage table for a field by Spec lookup.

  • Enables field→table lookup without the generic type parameter
  • Returns "", false if the type was not registered via Use[T]()
  • Returns "", false if the field does not exist

Example:

atomizer, _ := atom.Use[User]()
spec := atomizer.Spec()

// Later, without knowing T:
table, ok := atom.TableForField(spec, "Age")
if ok {
    fmt.Println(table) // "ints"
}

FieldsFor

func FieldsFor(spec Spec) ([]Field, bool)

Returns all field-to-table mappings for a registered type.

  • Returns nil, false if the type was not registered via Use[T]()
  • Nested struct fields are excluded (they have no table)

Example:

atomizer, _ := atom.Use[User]()
fields, ok := atom.FieldsFor(atomizer.Spec())
if ok {
    for _, f := range fields {
        fmt.Printf("%s -> %s\n", f.Name, f.Table)
    }
}

Types

AtomizerT

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

Provides typed bidirectional conversion for type T.

Methods

Atomize
func (a *Atomizer[T]) Atomize(obj *T) *Atom

Converts a struct to its atomic representation.

  • If T implements Atomizable, that method is used
  • Otherwise, reflection is used

Example:

user := &User{Name: "Alice", Age: 30}
atom := atomizer.Atomize(user)

Deatomize
func (a *Atomizer[T]) Deatomize(atom *Atom) (*T, error)

Reconstructs a struct from an Atom.

  • If T implements Deatomizable, that method is used
  • Otherwise, reflection is used
  • Returns error on overflow or validation failure

Example:

user, err := atomizer.Deatomize(atom)
if err != nil {
    log.Printf("deatomize failed: %v", err)
}

NewAtom
func (a *Atomizer[T]) NewAtom() *Atom

Creates an Atom with pre-allocated maps for this type.

Only allocates maps that will be used based on T's fields.

Example:

atom := atomizer.NewAtom()
atom.Strings["Name"] = "Bob"

Spec
func (a *Atomizer[T]) Spec() Spec

Returns the type specification for T.

Example:

spec := atomizer.Spec()
fmt.Println(spec.TypeName)    // "User"
fmt.Println(spec.PackageName) // "github.com/example/app"

Fields
func (a *Atomizer[T]) Fields() []Field

Returns all fields with their table mappings.

Example:

for _, f := range atomizer.Fields() {
    fmt.Printf("%s -> %s\n", f.Name, f.Table)
}
// ID -> ints
// Name -> strings

FieldsIn
func (a *Atomizer[T]) FieldsIn(table Table) []string

Returns field names stored in the given table.

Example:

stringFields := atomizer.FieldsIn(atom.TableStrings)
// ["Name", "Email"]

TableFor
func (a *Atomizer[T]) TableFor(field string) (Table, bool)

Returns the table for a field name.

Example:

table, ok := atomizer.TableFor("Age")
if ok {
    fmt.Println(table) // "ints"
}

Atom

type Atom struct {
    // Scalars
    Strings map[string]string
    Ints    map[string]int64
    Uints   map[string]uint64
    Floats  map[string]float64
    Bools   map[string]bool
    Times   map[string]time.Time
    Bytes   map[string][]byte

    // Pointers (nullable)
    StringPtrs map[string]*string
    IntPtrs    map[string]*int64
    UintPtrs   map[string]*uint64
    FloatPtrs  map[string]*float64
    BoolPtrs   map[string]*bool
    TimePtrs   map[string]*time.Time
    BytePtrs   map[string]*[]byte

    // Slices
    StringSlices map[string][]string
    IntSlices    map[string][]int64
    UintSlices   map[string][]uint64
    FloatSlices  map[string][]float64
    BoolSlices   map[string][]bool
    TimeSlices   map[string][]time.Time
    ByteSlices   map[string][][]byte

    // Maps (string-keyed)
    StringMaps map[string]map[string]string
    IntMaps    map[string]map[string]int64
    UintMaps   map[string]map[string]uint64
    FloatMaps  map[string]map[string]float64
    BoolMaps   map[string]map[string]bool
    TimeMaps   map[string]map[string]time.Time
    ByteMaps   map[string]map[string][]byte

    // Nested
    Nested       map[string]Atom
    NestedSlices map[string][]Atom
    NestedMaps   map[string]map[string]Atom

    // Metadata
    Spec Spec
}

Holds decomposed atomic values by type.

Methods

Clone
func (a *Atom) Clone() *Atom

Returns a deep copy of the Atom.

  • All maps are copied (not shared)
  • Pointer values are dereferenced and copied
  • Nested atoms are recursively cloned
  • Nil entries in pointer maps are preserved
  • Returns nil if called on nil

Example:

original := atomizer.Atomize(user)
copy := original.Clone()

// Modify copy without affecting original
copy.Strings["Name"] = "Modified"
fmt.Println(original.Strings["Name"]) // Unchanged

Table

type Table string

Identifies the segregated storage table for atomic values.

Constants

const (
    TableStrings      Table = "strings"
    TableInts         Table = "ints"
    TableUints        Table = "uints"
    TableFloats       Table = "floats"
    TableBools        Table = "bools"
    TableTimes        Table = "times"
    TableBytes        Table = "bytes"
    TableBytePtrs     Table = "byte_ptrs"
    TableStringPtrs   Table = "string_ptrs"
    TableIntPtrs      Table = "int_ptrs"
    TableUintPtrs     Table = "uint_ptrs"
    TableFloatPtrs    Table = "float_ptrs"
    TableBoolPtrs     Table = "bool_ptrs"
    TableTimePtrs     Table = "time_ptrs"
    TableStringSlices Table = "string_slices"
    TableIntSlices    Table = "int_slices"
    TableUintSlices   Table = "uint_slices"
    TableFloatSlices  Table = "float_slices"
    TableBoolSlices   Table = "bool_slices"
    TableTimeSlices   Table = "time_slices"
    TableByteSlices   Table = "byte_slices"
)

Methods

Prefix
func (t Table) Prefix() string

Returns the storage key prefix for this table.

Example:

prefix := atom.TableStrings.Prefix()
// "strings:"

Spec

type Spec = sentinel.Metadata

Type metadata describing a struct type. Aliased from sentinel.Metadata:

type Metadata struct {
    TypeName      string             // e.g., "User"
    PackageName   string             // e.g., "github.com/example/app"
    Fields        []FieldMetadata    // All exported fields
    Relationships []TypeRelationship // References to other types
}

Most commonly used fields:

  • TypeName - The short type name
  • PackageName - The full package path

Field

type Field struct {
    Name  string
    Table Table
}

Maps a field name to its storage table.


Interfaces

Atomizable

type Atomizable interface {
    Atomize(*Atom)
}

Allows types to provide custom atomization logic.

If implemented, Atomizer.Atomize() calls this method instead of using reflection.

Example:

func (u *User) Atomize(a *Atom) {
    a.Strings["Name"] = u.Name
    a.Ints["Age"] = u.Age
}

Deatomizable

type Deatomizable interface {
    Deatomize(*Atom) error
}

Allows types to provide custom deatomization logic.

If implemented, Atomizer.Deatomize() calls this method instead of using reflection.

Example:

func (u *User) Deatomize(a *Atom) error {
    u.Name = a.Strings["Name"]
    u.Age = a.Ints["Age"]
    return nil
}

Errors

Sentinel errors for programmatic error handling via errors.Is().

ErrOverflow

var ErrOverflow = errors.New("numeric overflow")

Returned during Deatomize() when an int64/uint64/float64 value cannot fit in a narrower type (e.g., int8, uint16, float32).

Example:

_, err := atomizer.Deatomize(atom)
if errors.Is(err, atom.ErrOverflow) {
    // Handle overflow
}

ErrUnsupportedType

var ErrUnsupportedType = errors.New("unsupported type")

Returned during Use[T]() for types like channels, functions, interfaces, or maps with non-string keys.

Example:

_, err := atom.Use[BadType]()
if errors.Is(err, atom.ErrUnsupportedType) {
    // Handle unsupported type
}

ErrSizeMismatch

var ErrSizeMismatch = errors.New("size mismatch")

Returned during Deatomize() when a []byte value is assigned to a [N]byte field but the lengths differ.

Example:

_, err := atomizer.Deatomize(atom)
if errors.Is(err, atom.ErrSizeMismatch) {
    // Handle size mismatch
}

Type Mappings

Go TypeTableAtom Field
stringstringsStrings
int, int8, int16, int32, int64intsInts
uint, uint8, uint16, uint32, uint64uintsUints
float32, float64floatsFloats
boolboolsBools
time.TimetimesTimes
[]bytebytesBytes
*stringstring_ptrsStringPtrs
*int64int_ptrsIntPtrs
*uint64uint_ptrsUintPtrs
*float64float_ptrsFloatPtrs
*boolbool_ptrsBoolPtrs
*time.Timetime_ptrsTimePtrs
*[]bytebyte_ptrsBytePtrs
[]stringstring_slicesStringSlices
[]int64int_slicesIntSlices
[]uint64uint_slicesUintSlices
[]float64float_slicesFloatSlices
[]boolbool_slicesBoolSlices
[]time.Timetime_slicesTimeSlices
[][]bytebyte_slicesByteSlices
map[string]stringstring_mapsStringMaps
map[string]int64int_mapsIntMaps
map[string]uint64uint_mapsUintMaps
map[string]float64float_mapsFloatMaps
map[string]boolbool_mapsBoolMaps
map[string]time.Timetime_mapsTimeMaps
map[string][]bytebyte_mapsByteMaps
struct-Nested
*struct-Nested
[]struct-NestedSlices
map[string]structnested_mapsNestedMaps