atom

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Dec 27, 2025 License: MIT Imports: 8 Imported by: 0

README

atom

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

Type-segregated atomic value decomposition for Go — break structs into typed atoms, reconstruct them later.

The Problem

Storing complex structs often means choosing between:

  • Full serialization — JSON/protobuf blobs that can't be queried field-by-field
  • ORM mapping — heavy frameworks with reflection overhead on every operation
  • Manual decomposition — tedious, error-prone code for each type

The Solution

Atom provides a clean abstraction for decomposing structs into typed atomic values:

type User struct {
    ID        string
    Name      string
    Age       int64
    Balance   float64
    Active    bool
    CreatedAt time.Time
}

// Register the type once
atomizer, _ := atom.Use[User]()

// Decompose to atom
user := &User{Name: "Alice", Age: 30, Balance: 100.50, Active: true}
a := atomizer.Atomize(user)
// a.Strings["Name"] = "Alice"
// a.Ints["Age"] = 30
// a.Floats["Balance"] = 100.50
// a.Bools["Active"] = true

// Reconstruct from atom
restored, _ := atomizer.Deatomize(a)

Atom handles:

  • Type segregation — scalars, pointers, slices, nested objects in separate typed maps
  • Field metadata — automatic introspection via sentinel
  • Numeric width conversion — int8/uint32/etc. safely converted with overflow detection

Features

  • Storage-agnostic — bring your own persistence layer
  • Type-safe genericsAtomizer[T] catches errors at compile time
  • Sentinel integration — automatic field discovery and metadata
  • Nullable fields — pointer types (*string, *int64, etc.) with explicit nil handling
  • Slice support — type-safe storage for []string, []int64, etc.
  • Nested composition — embed Atoms within Atoms for complex object graphs
  • Custom implementations — implement Atomizable/Deatomizable interfaces to bypass reflection

Install

go get github.com/zoobzio/atom@latest

Requires Go 1.23+.

Quick Start

package main

import (
    "fmt"

    "github.com/zoobzio/atom"
)

type Order struct {
    ID     string
    Total  float64
    Status string
}

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

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

    // Decompose
    a := atomizer.Atomize(order)
    fmt.Printf("ID: %s, Total: %.2f\n", a.Strings["ID"], a.Floats["Total"])

    // Reconstruct
    restored, _ := atomizer.Deatomize(a)
    fmt.Printf("Restored: %+v\n", restored)
}

Usage

Basic Types

All Go primitive types are supported and stored in type-segregated maps:

type Example struct {
    Name    string     // → Atom.Strings
    Age     int        // → Atom.Ints (as int64)
    Count   uint64     // → Atom.Uints
    Rate    float64    // → Atom.Floats
    Active  bool       // → Atom.Bools
    Created time.Time  // → Atom.Times
    Data    []byte     // → Atom.Bytes
}
Nullable Fields (Pointers)

Use pointer types to represent optional/nullable fields:

type Profile struct {
    ID       string
    Nickname *string  // → Atom.StringPtrs (nil preserved)
    Age      *int64   // → Atom.IntPtrs
    Bio      *[]byte  // → Atom.BytePtrs
}

atomizer, _ := atom.Use[Profile]()
profile := &Profile{ID: "user-1", Nickname: nil}

a := atomizer.Atomize(profile)
// a.StringPtrs["Nickname"] == nil (explicitly stored)
Slice Fields

Store collections of primitive values:

type Article struct {
    ID     string
    Tags   []string   // → Atom.StringSlices
    Scores []int64    // → Atom.IntSlices
    Counts []uint32   // → Atom.UintSlices (as []uint64)
}
Nested Structs

Compose complex objects using nested Atoms:

type Address struct {
    Street string
    City   string
}

type Person struct {
    ID      string
    Name    string
    Address Address    // → Atom.Nested["Address"]
    Friends []Person   // → Atom.NestedSlices["Friends"]
}

atomizer, _ := atom.Use[Person]()
Custom Atomization

Implement Atomizable and/or Deatomizable to bypass reflection:

type Custom struct {
    Value int
}

func (c *Custom) Atomize(a *atom.Atom) {
    a.Ints["Value"] = int64(c.Value * 2) // custom logic
}

func (c *Custom) Deatomize(a *atom.Atom) error {
    c.Value = int(a.Ints["Value"] / 2)
    return nil
}

This enables code generation for high-performance scenarios.

API Reference

Core Types
Type Purpose
Atomizer[T] Generic atomizer for type T
Atom Container for decomposed atomic values
Field Maps field name to storage table
Atomizable Interface for custom atomization
Deatomizable Interface for custom deatomization
Functions
Function Purpose
Use[T]() Register and return an Atomizer[T] for type T
AllTables() Return all table types in canonical order
Atomizer Methods
Method Purpose
Atomize(obj) Decompose object to atom
Deatomize(atom) Reconstruct object from atom
NewAtom() Create an Atom with pre-sized maps for type T
Spec() Get type specification for type T
Fields() Get all field descriptors
FieldsIn(table) Get field names for a table type
TableFor(field) Get table type for a field name
Table Types

Scalars:

TableType Go Types Atom Field
TableStrings string Strings
TableInts int, int8...int64 Ints
TableUints uint, uint8...uint64 Uints
TableFloats float32, float64 Floats
TableBools bool Bools
TableTimes time.Time Times
TableBytes []byte Bytes

Pointers (nullable):

TableType Go Types Atom Field
TableStringPtrs *string StringPtrs
TableIntPtrs *int, *int8...*int64 IntPtrs
TableUintPtrs *uint, *uint8...*uint64 UintPtrs
TableFloatPtrs *float32, *float64 FloatPtrs
TableBoolPtrs *bool BoolPtrs
TableTimePtrs *time.Time TimePtrs
TableBytePtrs *[]byte BytePtrs

Slices:

TableType Go Types Atom Field
TableStringSlices []string StringSlices
TableIntSlices []int, []int64, etc. IntSlices
TableUintSlices []uint, []uint64, etc. UintSlices
TableFloatSlices []float32, []float64 FloatSlices
TableBoolSlices []bool BoolSlices
TableTimeSlices []time.Time TimeSlices
TableByteSlices [][]byte ByteSlices

Nested:

Field Purpose
Nested map[string]Atom for single nested structs
NestedSlices map[string][]Atom for slices of nested structs

Design

Atom is intentionally minimal. It provides:

  1. Decomposition abstraction — the Atom container and transformation types
  2. Field introspection — via sentinel integration

It does not provide:

  • Storage backends
  • Query interfaces
  • Caching layers

This design allows atom to be used within storage libraries without circular dependencies.

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT License — see LICENSE for details.

Documentation

Overview

Package atom provides type-segregated atomic value storage.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Atom added in v0.0.2

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

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

	// Metadata (placed last for optimal alignment)
	Spec Spec
}

Atom holds decomposed atomic values by type.

func Unflatten added in v0.0.2

func Unflatten(data map[string]any, spec Spec, tagKey string) *Atom

Unflatten reconstructs an Atom from a struct-shaped map using spec and tag key.

func (*Atom) Flatten added in v0.0.2

func (a *Atom) Flatten(tagKey string) map[string]any

Flatten converts an Atom to a struct-shaped map using the specified tag key. Field names are resolved from struct tags (e.g., "json", "bson", "db"). Falls back to field name if tag is missing or "-".

type Atomizable added in v0.0.2

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 added in v0.0.2

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 added in v0.0.2

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

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

func (*Atomizer[T]) Spec added in v0.0.2

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 added in v0.0.2

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 added in v0.0.2

type Field struct {
	Name  string
	Table Table
}

Field maps a field name to its storage table.

type Spec added in v0.0.2

type Spec = sentinel.Metadata

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

type Table added in v0.0.2

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"
)

Table constants for type-segregated storage.

func AllTables

func AllTables() []Table

AllTables returns all table types in canonical order.

func (Table) Prefix added in v0.0.2

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