atom

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Mar 19, 2026 License: MIT Imports: 7 Imported by: 0

README

atom

CI Status codecov Go Report Card CodeQL Go Reference License Go Version Release

Type-segregated struct decomposition for Go.

Break structs into typed maps, work with fields programmatically, reconstruct later — without knowing T.

Normalized Type-Safe Data Without the Types

User code knows its types. Infrastructure code doesn't need to.

// User side: knows T
type User struct {
    ID      string
    Name    string
    Age     int64
    Balance float64
    Active  bool
}

atomizer, _ := atom.Use[User]()
user := &User{ID: "usr-1", Name: "Alice", Age: 30, Balance: 100.50, Active: true}
a := atomizer.Atomize(user)

// Pass atom to any library...
result := storage.Save(a)            // storage never imports User
validated := validator.Check(a)      // validator never imports User
transformed := migrator.Upgrade(a)   // migrator never imports User

// ...get it back
restored, _ := atomizer.Deatomize(result)

The receiving library sees typed maps and metadata — not T:

// Library side: doesn't know T, doesn't need T
func Save(a *atom.Atom) *atom.Atom {
    // Spec describes the struct
    fmt.Println(a.Spec.TypeName) // "User"

    // Typed maps hold the values
    for field, value := range a.Strings {
        db.SetString(field, value)
    }
    for field, value := range a.Ints {
        db.SetInt(field, value)
    }
    for field, value := range a.Floats {
        db.SetFloat(field, value)
    }

    return a
}

Type-safe field access. Zero knowledge of the original struct.

Install

go get github.com/zoobz-io/atom@latest

Requires Go 1.24+.

Quick Start

package main

import (
    "fmt"
    "github.com/zoobz-io/atom"
)

type Order struct {
    ID     string
    Total  float64
    Status string
}

func main() {
    // Register the type once
    atomizer, err := atom.Use[Order]()
    if err != nil {
        panic(err)
    }

    order := &Order{ID: "order-123", Total: 99.99, Status: "pending"}

    // Decompose to atom
    a := atomizer.Atomize(order)

    // Work with typed maps
    fmt.Printf("ID: %s\n", a.Strings["ID"])
    fmt.Printf("Total: %.2f\n", a.Floats["Total"])

    // Modify fields
    a.Strings["Status"] = "confirmed"

    // Reconstruct
    restored, _ := atomizer.Deatomize(a)
    fmt.Printf("Status: %s\n", restored.Status) // "confirmed"
}

Capabilities

Feature Description Docs
Type Segregation Strings, ints, floats, bools, times, bytes in separate typed maps Concepts
Nullable Fields Pointer types (*string, *int64) with explicit nil handling Basic Usage
Slices []string, []int64, etc. preserved as typed slices Basic Usage
Nested Composition Embed atoms within atoms for complex object graphs Nested Structs
Field Introspection Query fields, tables, and type metadata via Spec API Reference
Custom Implementations Atomizable/Deatomizable interfaces bypass reflection Interfaces
Code Generation Generate implementations for zero-reflection paths Code Generation

Why atom?

  • Type-safe without T — Libraries work with typed maps, not any or reflection
  • Field-level control — Read, write, transform individual fields programmatically
  • Decoupled — Infrastructure code never imports user types
  • Zero reflection path — Implement interfaces or use codegen for production performance
  • Sentinel integration — Automatic field discovery and metadata extraction

The Typed Bridge

Atom enables a pattern: user code owns types, infrastructure owns behaviour.

Your application defines structs. Libraries accept atoms. Each side works with what it knows — concrete types on one end, typed maps on the other. No shared type imports. No reflection at runtime (with codegen).

// Your domain package defines types
type User struct { ... }
type Order struct { ... }

// Storage library accepts atoms — never sees User or Order
func (s *Store) Put(a *atom.Atom) error { ... }
func (s *Store) Get(spec atom.Spec, id string) (*atom.Atom, error) { ... }

// Your code bridges the two
a := userAtomizer.Atomize(user)
store.Put(a)

The contract is the Atom structure. The types stay where they belong.

Documentation

Learn
Guides
Cookbook
Reference

Contributing

See CONTRIBUTING.md for guidelines. Run make help for available commands.

License

MIT License — see LICENSE for details.

Documentation

Overview

Package atom provides type-segregated atomic value storage.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrOverflow indicates a numeric value exceeds the target type's range.
	// Returned during Deatomize when an int64/uint64/float64 value cannot fit
	// in a narrower type (e.g., int8, uint16, float32).
	ErrOverflow = errors.New("numeric overflow")

	// ErrUnsupportedType indicates a struct field has a type that cannot be atomized.
	// Returned during Use[T]() registration for types like channels, functions,
	// interfaces, or maps with non-string keys.
	ErrUnsupportedType = errors.New("unsupported type")

	// ErrSizeMismatch indicates a byte slice length doesn't match a fixed-size array.
	// Returned during Deatomize when a []byte value is assigned to a [N]byte field
	// but the lengths differ.
	ErrSizeMismatch = errors.New("size mismatch")
)

Sentinel errors for programmatic error handling. Use errors.Is() to check for these error types.

Functions

This section is empty.

Types

type 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 (placed last for optimal alignment)
	Spec Spec
}

Atom holds decomposed atomic values by type.

func (*Atom) Clone

func (a *Atom) Clone() *Atom

Clone returns a deep copy of the Atom.

type Atomizable

type Atomizable interface {
	Atomize(*Atom)
}

Atomizable allows types to provide custom atomization logic. If a type implements this interface, it will be used instead of reflection. This enables code generation to avoid reflection overhead.

type Atomizer

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

Atomizer provides typed bidirectional resolution for type T.

func Use

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

Use registers and returns an Atomizer for type T. First call builds the atomizer; subsequent calls return the cached instance. Returns an error if the type contains unsupported field types.

func (*Atomizer[T]) Atomize

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

Atomize converts an object to its atomic representation. If T implements Atomizable, that method is used instead of reflection.

func (*Atomizer[T]) Deatomize

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

Deatomize reconstructs an object from an Atom. If T implements Deatomizable, that method is used instead of reflection.

func (*Atomizer[T]) Fields

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

Fields returns all fields with their table mappings.

func (*Atomizer[T]) FieldsIn

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

FieldsIn returns field names stored in the given table.

func (*Atomizer[T]) NewAtom

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

NewAtom creates an Atom with only the maps needed for this type.

func (*Atomizer[T]) Spec

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

Spec returns the type specification for type T.

func (*Atomizer[T]) TableFor

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

TableFor returns the table for a field name.

type Deatomizable

type Deatomizable interface {
	Deatomize(*Atom) error
}

Deatomizable allows types to provide custom deatomization logic. If a type implements this interface, it will be used instead of reflection. This enables code generation to avoid reflection overhead.

type Field

type Field struct {
	Name  string
	Table Table
}

Field maps a field name to its storage table.

func FieldsFor

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

FieldsFor returns all field-to-table mappings for a registered type. Requires the type to have been registered via Use[T]().

type Spec

type Spec = sentinel.Metadata

Spec is metadata describing a struct type. Aliased from sentinel.Metadata to decouple downstream users from sentinel.

type Table

type Table string

Table identifies the segregated storage table for atomic values.

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"
	TableStringMaps   Table = "string_maps"
	TableIntMaps      Table = "int_maps"
	TableUintMaps     Table = "uint_maps"
	TableFloatMaps    Table = "float_maps"
	TableBoolMaps     Table = "bool_maps"
	TableTimeMaps     Table = "time_maps"
	TableByteMaps     Table = "byte_maps"
	TableNestedMaps   Table = "nested_maps"
)

Table constants for type-segregated storage.

func AllTables

func AllTables() []Table

AllTables returns all table types in canonical order.

func TableForField

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

TableForField returns the storage table for a field by Spec lookup. Requires the type to have been registered via Use[T]().

func (Table) Prefix

func (t Table) Prefix() string

Prefix returns the storage key prefix for this table.

Directories

Path Synopsis
Package testing provides utilities for testing atom-based applications.
Package testing provides utilities for testing atom-based applications.

Jump to

Keyboard shortcuts

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