[{"data":1,"prerenderedAt":7119},["ShallowReactive",2],{"search-sections-atom":3,"nav-atom":1642,"content-tree-atom":1699,"footer-resources":1725,"content-/v1.0.2/cookbook/serialization":3849,"surround-/v1.0.2/cookbook/serialization":7116},[4,10,15,21,26,31,36,42,47,52,57,62,67,72,76,81,85,90,95,100,105,110,115,120,125,129,134,139,144,149,154,159,164,169,174,179,184,188,193,197,201,206,211,216,221,226,231,236,241,246,250,255,260,265,270,274,278,282,286,291,296,301,305,310,315,320,325,329,334,338,343,347,352,357,362,366,371,376,380,385,390,394,399,404,408,413,418,423,427,432,436,440,445,450,455,459,464,469,473,478,483,488,492,497,502,507,510,515,520,524,529,533,538,543,548,553,558,563,568,573,578,583,588,593,596,601,606,611,615,620,624,628,633,638,643,648,652,657,662,667,672,677,682,687,692,695,700,705,710,714,719,723,727,732,737,741,746,751,755,760,764,768,773,778,783,788,793,798,801,806,811,815,819,823,827,832,837,842,846,851,856,860,864,869,873,878,881,886,891,896,900,905,909,913,917,922,927,932,937,942,946,951,956,960,965,970,975,980,985,988,993,998,1002,1007,1011,1015,1019,1024,1029,1034,1038,1043,1048,1052,1056,1061,1065,1070,1074,1079,1084,1088,1093,1098,1102,1107,1112,1117,1120,1125,1130,1135,1139,1144,1148,1153,1157,1162,1167,1172,1177,1181,1186,1191,1196,1200,1205,1210,1214,1219,1224,1228,1231,1236,1241,1246,1249,1254,1258,1263,1266,1271,1276,1281,1286,1291,1296,1301,1306,1310,1314,1318,1323,1328,1333,1338,1343,1348,1353,1358,1363,1368,1373,1378,1383,1388,1393,1398,1403,1408,1413,1418,1423,1428,1433,1438,1443,1448,1453,1458,1463,1468,1473,1478,1483,1487,1491,1494,1499,1504,1509,1514,1518,1522,1527,1532,1536,1541,1546,1550,1554,1559,1563,1567,1571,1576,1580,1584,1589,1593,1598,1602,1606,1611,1615,1619,1624,1627,1632,1637],{"id":5,"title":6,"titles":7,"content":8,"level":9},"/v1.0.2/overview","Overview",[],"Type-segregated atomic value decomposition for Go structs",1,{"id":11,"title":12,"titles":13,"content":14,"level":9},"/v1.0.2/overview#atom","Atom",[],"Type-segregated atomic value decomposition for Go.",{"id":16,"title":17,"titles":18,"content":19,"level":20},"/v1.0.2/overview#the-problem","The Problem",[12],"Serializing Go structs for storage or transmission typically involves either: Reflection-heavy marshaling (JSON, gob) - flexible but slow, with runtime type discoveryCode generation (protobuf, msgpack) - fast but requires build steps and schema filesManual serialization - fast and flexible but tedious and error-prone None of these approaches make it easy to: Store individual fields in type-appropriate backends (strings in Redis, numbers in time-series DBs)Query or index specific fields without deserializing the entire objectEvolve schemas while maintaining type safety",2,{"id":22,"title":23,"titles":24,"content":25,"level":20},"/v1.0.2/overview#the-solution","The Solution",[12],"Atom decomposes structs into type-segregated maps, where each primitive type gets its own storage table: type User struct {\n    Name      string\n    Age       int64\n    Balance   float64\n    Active    bool\n    CreatedAt time.Time\n}\n\n// Decomposed into:\natom.Strings[\"Name\"] = \"alice\"\natom.Ints[\"Age\"] = 30\natom.Floats[\"Balance\"] = 100.50\natom.Bools[\"Active\"] = true\natom.Times[\"CreatedAt\"] = time.Now() This separation enables: Type-native storage: Store strings in one system, numbers in anotherField-level access: Read or write individual fields without full deserializationSchema introspection: Query which fields exist and their types at runtime",{"id":27,"title":28,"titles":29,"content":30,"level":20},"/v1.0.2/overview#how-it-works","How It Works",[12],"┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐\n│   Your Struct   │────▶│    Atomizer     │────▶│      Atom       │\n│                 │     │                 │     │                 │\n│  Name: \"alice\"  │     │  - Reflection   │     │  Strings: {...} │\n│  Age: 30        │     │  - Type plans   │     │  Ints: {...}    │\n│  Balance: 100.5 │     │  - Validation   │     │  Floats: {...}  │\n└─────────────────┘     └─────────────────┘     └─────────────────┘\n                                │\n                                ▼\n                        ┌─────────────────┐\n                        │    Registry     │\n                        │                 │\n                        │  Cached plans   │\n                        │  per type       │\n                        └─────────────────┘ UseT registers a type and builds an execution planAtomize() decomposes a struct into an Atom using the cached planDeatomize() reconstructs a struct from an Atom",{"id":32,"title":33,"titles":34,"content":35,"level":20},"/v1.0.2/overview#key-features","Key Features",[12],"",{"id":37,"title":38,"titles":39,"content":40,"level":41},"/v1.0.2/overview#type-safety","Type Safety",[12,33],"Compile-time generic type parametersRuntime overflow detection for numeric conversionsValidation of supported field types at registration",3,{"id":43,"title":44,"titles":45,"content":46,"level":41},"/v1.0.2/overview#performance","Performance",[12,33],"One-time reflection cost per type (cached in registry)Pre-allocated maps sized to exact field countsAtomizable/Deatomizable interfaces enable code generation to bypass reflection",{"id":48,"title":49,"titles":50,"content":51,"level":41},"/v1.0.2/overview#flexibility","Flexibility",[12,33],"Supports all Go primitive types and their pointersNested structs and slices of structsNamed types (enums, type aliases)Custom serialization via Atomizable/Deatomizable interfaces",{"id":53,"title":54,"titles":55,"content":56,"level":20},"/v1.0.2/overview#supported-types","Supported Types",[12],"CategoryTypesPrimitivesstring, int*, uint*, float*, bool, time.Time, []bytePointers*string, *int64, *float64, *bool, *time.Time, *[]byteSlices[]string, []int64, []float64, []bool, []time.Time, [][]byteNamedtype Status int, type UserID string, type IP []byteNestedEmbedded structs, *Struct, []Struct",{"id":58,"title":59,"titles":60,"content":61,"level":20},"/v1.0.2/overview#quick-example","Quick Example",[12],"package main\n\nimport \"github.com/zoobz-io/atom\"\n\ntype User struct {\n    ID    int64\n    Name  string\n    Email string\n}\n\nfunc main() {\n    // Register the type (cached after first call)\n    atomizer, err := atom.Use[User]()\n    if err != nil {\n        panic(err)\n    }\n\n    // Decompose\n    user := &User{ID: 1, Name: \"Alice\", Email: \"alice@example.com\"}\n    a := atomizer.Atomize(user)\n\n    // Access individual fields by type\n    fmt.Println(a.Ints[\"ID\"])      // 1\n    fmt.Println(a.Strings[\"Name\"]) // Alice\n\n    // Reconstruct\n    restored, err := atomizer.Deatomize(a)\n    if err != nil {\n        panic(err)\n    }\n    fmt.Println(restored.Name) // Alice\n}",{"id":63,"title":64,"titles":65,"content":66,"level":20},"/v1.0.2/overview#next-steps","Next Steps",[12],"Quickstart - Get up and running in 5 minutesConcepts - Understand atoms, tables, and specsAPI Reference - Complete API documentation html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}",{"id":68,"title":69,"titles":70,"content":71,"level":9},"/v1.0.2/learn/quickstart","Quickstart",[],"Get started with atom in 5 minutes",{"id":73,"title":69,"titles":74,"content":75,"level":9},"/v1.0.2/learn/quickstart#quickstart",[],"Get started with atom in 5 minutes.",{"id":77,"title":78,"titles":79,"content":80,"level":20},"/v1.0.2/learn/quickstart#installation","Installation",[69],"go get github.com/zoobz-io/atom",{"id":82,"title":83,"titles":84,"content":35,"level":20},"/v1.0.2/learn/quickstart#basic-usage","Basic Usage",[69],{"id":86,"title":87,"titles":88,"content":89,"level":41},"/v1.0.2/learn/quickstart#_1-define-your-struct","1. Define Your Struct",[69,83],"type User struct {\n    ID        int64\n    Name      string\n    Email     string\n    Age       int64\n    Active    bool\n    CreatedAt time.Time\n}",{"id":91,"title":92,"titles":93,"content":94,"level":41},"/v1.0.2/learn/quickstart#_2-register-the-type","2. Register the Type",[69,83],"atomizer, err := atom.Use[User]()\nif err != nil {\n    log.Fatal(err)\n} The first call to Use[T]() builds an execution plan using reflection. Subsequent calls return the cached atomizer instantly.",{"id":96,"title":97,"titles":98,"content":99,"level":41},"/v1.0.2/learn/quickstart#_3-atomize-struct-atom","3. Atomize (Struct → Atom)",[69,83],"user := &User{\n    ID:        1,\n    Name:      \"Alice\",\n    Email:     \"alice@example.com\",\n    Age:       30,\n    Active:    true,\n    CreatedAt: time.Now(),\n}\n\natom := atomizer.Atomize(user)",{"id":101,"title":102,"titles":103,"content":104,"level":41},"/v1.0.2/learn/quickstart#_4-access-fields-by-type","4. Access Fields by Type",[69,83],"// Each type has its own map\nfmt.Println(atom.Ints[\"ID\"])       // 1\nfmt.Println(atom.Ints[\"Age\"])      // 30\nfmt.Println(atom.Strings[\"Name\"])  // Alice\nfmt.Println(atom.Strings[\"Email\"]) // alice@example.com\nfmt.Println(atom.Bools[\"Active\"])  // true\nfmt.Println(atom.Times[\"CreatedAt\"]) // 2024-...",{"id":106,"title":107,"titles":108,"content":109,"level":41},"/v1.0.2/learn/quickstart#_5-deatomize-atom-struct","5. Deatomize (Atom → Struct)",[69,83],"restored, err := atomizer.Deatomize(atom)\nif err != nil {\n    log.Fatal(err)\n}\n\nfmt.Println(restored.Name) // Alice",{"id":111,"title":112,"titles":113,"content":114,"level":20},"/v1.0.2/learn/quickstart#complete-example","Complete Example",[69],"package main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"time\"\n\n    \"github.com/zoobz-io/atom\"\n)\n\ntype User struct {\n    ID        int64\n    Name      string\n    Email     string\n    Age       int64\n    Active    bool\n    CreatedAt time.Time\n}\n\nfunc main() {\n    // Register type\n    atomizer, err := atom.Use[User]()\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    // Create and atomize\n    user := &User{\n        ID:        1,\n        Name:      \"Alice\",\n        Email:     \"alice@example.com\",\n        Age:       30,\n        Active:    true,\n        CreatedAt: time.Now(),\n    }\n    a := atomizer.Atomize(user)\n\n    // Inspect the atom\n    fmt.Printf(\"Strings: %v\\n\", a.Strings)\n    fmt.Printf(\"Ints: %v\\n\", a.Ints)\n    fmt.Printf(\"Bools: %v\\n\", a.Bools)\n\n    // Reconstruct\n    restored, err := atomizer.Deatomize(a)\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"Restored: %+v\\n\", restored)\n} Output: Strings: map[Email:alice@example.com Name:Alice]\nInts: map[Age:30 ID:1]\nBools: map[Active:true]\nRestored: &{ID:1 Name:Alice Email:alice@example.com Age:30 Active:true CreatedAt:...}",{"id":116,"title":117,"titles":118,"content":119,"level":20},"/v1.0.2/learn/quickstart#whats-next","What's Next?",[69],"Concepts - Understand atoms, tables, and specsArchitecture - How atom works internallyBasic Usage Guide - More detailed usage patterns html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}",{"id":121,"title":122,"titles":123,"content":124,"level":9},"/v1.0.2/learn/concepts","Core Concepts",[],"Understanding atoms, tables, specs, and atomizers",{"id":126,"title":122,"titles":127,"content":128,"level":9},"/v1.0.2/learn/concepts#core-concepts",[],"Understanding the building blocks of atom.",{"id":130,"title":131,"titles":132,"content":133,"level":20},"/v1.0.2/learn/concepts#atoms","Atoms",[122],"An Atom is the decomposed representation of a struct. It contains type-segregated maps where each field is stored under its name in the appropriate map for its type. type Atom struct {\n    Spec    Spec                    // Type metadata\n    Strings map[string]string       // String fields\n    Ints    map[string]int64        // Integer fields (all sizes)\n    Uints   map[string]uint64       // Unsigned integer fields\n    Floats  map[string]float64      // Float fields\n    Bools   map[string]bool         // Boolean fields\n    Times   map[string]time.Time    // Time fields\n    Bytes   map[string][]byte       // Byte slice fields\n    // ... pointers, slices, nested\n}",{"id":135,"title":136,"titles":137,"content":138,"level":41},"/v1.0.2/learn/concepts#why-type-segregation","Why Type Segregation?",[122,131],"Type segregation enables: Type-native storage: Store integers in Redis sorted sets, strings in search indicesEfficient serialization: Each map can use optimal encoding for its typePartial access: Read only the fields you need without deserializing everything",{"id":140,"title":141,"titles":142,"content":143,"level":20},"/v1.0.2/learn/concepts#tables","Tables",[122],"A Table identifies which map in an Atom stores a particular field type. type Table string\n\nconst (\n    TableStrings      Table = \"strings\"\n    TableInts         Table = \"ints\"\n    TableUints        Table = \"uints\"\n    TableFloats       Table = \"floats\"\n    TableBools        Table = \"bools\"\n    TableTimes        Table = \"times\"\n    TableBytes        Table = \"bytes\"\n    // ... plus pointers and slices\n)",{"id":145,"title":146,"titles":147,"content":148,"level":41},"/v1.0.2/learn/concepts#table-categories","Table Categories",[122,141],"CategoryTablesGo TypesScalarsstrings, ints, uints, floats, bools, times, bytesPrimitive typesPointersstring_ptrs, int_ptrs, etc.*T where T is scalarSlicesstring_slices, int_slices, etc.[]T where T is scalar",{"id":150,"title":151,"titles":152,"content":153,"level":41},"/v1.0.2/learn/concepts#querying-tables","Querying Tables",[122,141],"atomizer, _ := atom.Use[User]()\n\n// Get all fields\nfields := atomizer.Fields()\n// [{Name: \"ID\", Table: \"ints\"}, {Name: \"Name\", Table: \"strings\"}, ...]\n\n// Get fields in a specific table\nstringFields := atomizer.FieldsIn(atom.TableStrings)\n// [\"Name\", \"Email\"]\n\n// Get table for a field\ntable, ok := atomizer.TableFor(\"Age\")\n// table = \"ints\", ok = true",{"id":155,"title":156,"titles":157,"content":158,"level":20},"/v1.0.2/learn/concepts#specs","Specs",[122],"A Spec contains metadata about a type, including its name and package path. It is an alias for sentinel.Metadata: type Spec = sentinel.Metadata Key fields: spec.TypeName    // e.g., \"User\"\nspec.PackageName // e.g., \"github.com/example/app\" Specs are automatically populated from the sentinel library and can be accessed: atomizer, _ := atom.Use[User]()\nspec := atomizer.Spec()\nfmt.Println(spec.TypeName)    // \"User\"\nfmt.Println(spec.PackageName) // \"github.com/example/app\"",{"id":160,"title":161,"titles":162,"content":163,"level":20},"/v1.0.2/learn/concepts#fields","Fields",[122],"A Field describes a single struct field and its storage location. type Field struct {\n    Name  string // Field name (e.g., \"Age\")\n    Table Table  // Storage table (e.g., \"ints\")\n}",{"id":165,"title":166,"titles":167,"content":168,"level":20},"/v1.0.2/learn/concepts#atomizers","Atomizers",[122],"An AtomizerT is the typed interface for converting between structs and atoms. type Atomizer[T any] struct {\n    // internal\n}\n\nfunc (a *Atomizer[T]) Atomize(obj *T) *Atom\nfunc (a *Atomizer[T]) Deatomize(atom *Atom) (*T, error)\nfunc (a *Atomizer[T]) NewAtom() *Atom\nfunc (a *Atomizer[T]) Spec() Spec\nfunc (a *Atomizer[T]) Fields() []Field\nfunc (a *Atomizer[T]) FieldsIn(table Table) []string\nfunc (a *Atomizer[T]) TableFor(field string) (Table, bool) Atomizers are obtained via the Use[T]() function and are cached in a global registry.",{"id":170,"title":171,"titles":172,"content":173,"level":20},"/v1.0.2/learn/concepts#the-registry","The Registry",[122],"The registry maintains a cache of atomizers indexed by type. When you call Use[T](): Check if an atomizer exists for type TIf yes, return the cached instanceIf no, build an execution plan via reflection and cache it // First call: builds atomizer (~microseconds)\natomizer1, _ := atom.Use[User]()\n\n// Subsequent calls: returns cached (~nanoseconds)\natomizer2, _ := atom.Use[User]()\n\n// atomizer1 and atomizer2 share the same internal state",{"id":175,"title":176,"titles":177,"content":178,"level":20},"/v1.0.2/learn/concepts#type-width-conversion","Type Width Conversion",[122],"Atom normalizes integer and float types to their widest representation: Go TypeAtom Storageint8, int16, int32, int, int64int64uint8, uint16, uint32, uint, uint64uint64float32, float64float64 Overflow detection occurs during deatomization: type Small struct {\n    Value int8 // Range: -128 to 127\n}\n\natom := &Atom{Ints: map[string]int64{\"Value\": 200}}\n_, err := atomizer.Deatomize(atom)\n// err: \"value 200 overflows int8 (range -128 to 127)\"",{"id":180,"title":181,"titles":182,"content":183,"level":20},"/v1.0.2/learn/concepts#named-types","Named Types",[122],"Named types (type aliases) are fully supported: type UserID string\ntype Status int\n\nconst (\n    StatusActive Status = iota\n    StatusInactive\n)\n\ntype User struct {\n    ID     UserID\n    Status Status\n}\n\n// UserID stored in Strings table\n// Status stored in Ints table",{"id":185,"title":64,"titles":186,"content":187,"level":20},"/v1.0.2/learn/concepts#next-steps",[122],"Architecture - How atom works internallyBasic Usage Guide - Common usage patterns html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sfm-E, html code.shiki .sfm-E{--shiki-default:var(--shiki-variable)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}",{"id":189,"title":190,"titles":191,"content":192,"level":9},"/v1.0.2/learn/architecture","Architecture",[],"Internal design and implementation of atom",{"id":194,"title":190,"titles":195,"content":196,"level":9},"/v1.0.2/learn/architecture#architecture",[],"How atom works under the hood.",{"id":198,"title":6,"titles":199,"content":200,"level":20},"/v1.0.2/learn/architecture#overview",[190],"Atom uses a two-phase approach: Registration Phase: Build an execution plan via reflection (once per type)Execution Phase: Apply the cached plan to convert structs (many times) ┌─────────────────────────────────────────────────────────────────┐\n│                      Registration Phase                          │\n│                                                                   │\n│  reflect.Type ──▶ buildFieldPlan() ──▶ []fieldPlan ──▶ registry │\n│                                                                   │\n└─────────────────────────────────────────────────────────────────┘\n                              │\n                              ▼ (cached)\n┌─────────────────────────────────────────────────────────────────┐\n│                       Execution Phase                            │\n│                                                                   │\n│  struct ──▶ atomize(plan) ──▶ Atom ──▶ deatomize(plan) ──▶ struct│\n│                                                                   │\n└─────────────────────────────────────────────────────────────────┘",{"id":202,"title":203,"titles":204,"content":205,"level":20},"/v1.0.2/learn/architecture#field-plans","Field Plans",[190],"A fieldPlan describes how to process a single struct field: type fieldPlan struct {\n    name      string           // Field name (map key)\n    index     []int            // Reflect index path\n    table     Table            // Target table\n    kind      fieldKind        // Scalar, pointer, slice, nested\n    elemType  reflect.Type     // Element type for slices/pointers\n    converter typeConverter    // Width conversion (int8→int64)\n    nested    *reflectAtomizer // For nested structs\n}",{"id":207,"title":208,"titles":209,"content":210,"level":41},"/v1.0.2/learn/architecture#field-kinds","Field Kinds",[190,203],"const (\n    kindScalar      // string, int64, bool, etc.\n    kindPointer     // *string, *int64, etc.\n    kindSlice       // []string, []int64, etc.\n    kindNested      // embedded struct\n    kindNestedSlice // []Struct\n    kindNestedPtr   // *Struct\n)",{"id":212,"title":213,"titles":214,"content":215,"level":41},"/v1.0.2/learn/architecture#plan-building","Plan Building",[190,203],"When Use[T]() is called: func buildFieldPlan(typ reflect.Type) ([]fieldPlan, error) {\n    var plans []fieldPlan\n    for i := 0; i \u003C typ.NumField(); i++ {\n        sf := typ.Field(i)\n\n        // Skip unexported fields\n        if !sf.IsExported() {\n            continue\n        }\n\n        fp, err := planField(sf, []int{i})\n        if err != nil {\n            return nil, err\n        }\n        plans = append(plans, fp)\n    }\n    return plans, nil\n}",{"id":217,"title":218,"titles":219,"content":220,"level":20},"/v1.0.2/learn/architecture#type-converters","Type Converters",[190],"Converters handle width normalization between Go types and Atom storage: type typeConverter struct {\n    toInt64     func(reflect.Value) int64\n    fromInt64   func(int64) (reflect.Value, error)\n    toUint64    func(reflect.Value) uint64\n    fromUint64  func(uint64) (reflect.Value, error)\n    toFloat64   func(reflect.Value) float64\n    fromFloat64 func(float64) (reflect.Value, error)\n    origType    reflect.Type\n}",{"id":222,"title":223,"titles":224,"content":225,"level":41},"/v1.0.2/learn/architecture#overflow-detection","Overflow Detection",[190,218],"The fromXxx functions validate that values fit in the target type: func intConverter(t reflect.Type) typeConverter {\n    var minVal, maxVal int64\n    switch t.Kind() {\n    case reflect.Int8:\n        minVal, maxVal = math.MinInt8, math.MaxInt8\n    case reflect.Int16:\n        minVal, maxVal = math.MinInt16, math.MaxInt16\n    // ...\n    }\n\n    return typeConverter{\n        fromInt64: func(i int64) (reflect.Value, error) {\n            if i \u003C minVal || i > maxVal {\n                return reflect.Value{}, fmt.Errorf(\"overflow\")\n            }\n            rv := reflect.New(t).Elem()\n            rv.SetInt(i)\n            return rv, nil\n        },\n    }\n}",{"id":227,"title":228,"titles":229,"content":230,"level":20},"/v1.0.2/learn/architecture#registry","Registry",[190],"The registry caches atomizers by type: var (\n    registry   = make(map[reflect.Type]*reflectAtomizer)\n    registryMu sync.RWMutex\n)",{"id":232,"title":233,"titles":234,"content":235,"level":41},"/v1.0.2/learn/architecture#thread-safety","Thread Safety",[190,228],"The registry uses a read-write mutex: Read path: Check cache with RLock() (fast, concurrent)Write path: Build and cache with Lock() (exclusive, rare) func Use[T any]() (*Atomizer[T], error) {\n    typ := reflect.TypeFor[T]()\n\n    // Fast path: check cache\n    registryMu.RLock()\n    if ra, ok := registry[typ]; ok {\n        registryMu.RUnlock()\n        return &Atomizer[T]{inner: ra}, nil\n    }\n    registryMu.RUnlock()\n\n    // Slow path: build and cache\n    registryMu.Lock()\n    defer registryMu.Unlock()\n\n    // Double-check after acquiring write lock\n    if ra, ok := registry[typ]; ok {\n        return &Atomizer[T]{inner: ra}, nil\n    }\n\n    ra, err := buildReflectAtomizerWithSpec(typ, spec)\n    // ...\n}",{"id":237,"title":238,"titles":239,"content":240,"level":20},"/v1.0.2/learn/architecture#nested-type-handling","Nested Type Handling",[190],"Nested structs are handled recursively: type Address struct {\n    Street string\n    City   string\n}\n\ntype User struct {\n    Name    string\n    Address Address // Nested struct\n} When atomizing: // User atom\natom.Strings[\"Name\"] = \"Alice\"\natom.Nested[\"Address\"] = Atom{\n    Strings: map[string]string{\n        \"Street\": \"123 Main St\",\n        \"City\":   \"Springfield\",\n    },\n}",{"id":242,"title":243,"titles":244,"content":245,"level":41},"/v1.0.2/learn/architecture#circular-references","Circular References",[190,238],"The registry uses a shell pattern to handle self-referential types: type Node struct {\n    Value    int\n    Children []Node // Self-referential\n} func ensureRegistered(typ reflect.Type) *reflectAtomizer {\n    if ra, ok := registry[typ]; ok {\n        return ra\n    }\n\n    // Register shell first (breaks circular dependency)\n    ra := &reflectAtomizer{typ: typ}\n    registry[typ] = ra\n\n    // Now build plan (may recursively call ensureRegistered)\n    plan, err := buildFieldPlan(typ)\n    ra.plan = plan\n    // ...\n}",{"id":247,"title":248,"titles":249,"content":35,"level":20},"/v1.0.2/learn/architecture#atomizedeatomize-flow","Atomize/Deatomize Flow",[190],{"id":251,"title":252,"titles":253,"content":254,"level":41},"/v1.0.2/learn/architecture#atomize","Atomize",[190,248],"func (ra *reflectAtomizer) atomize(src any, dst *Atom) {\n    v := reflect.ValueOf(src)\n    if v.Kind() == reflect.Ptr {\n        v = v.Elem()\n    }\n\n    for i := range ra.plan {\n        fp := &ra.plan[i]\n        fv := v.FieldByIndex(fp.index)\n        atomizeField(fp, fv, dst)\n    }\n}",{"id":256,"title":257,"titles":258,"content":259,"level":41},"/v1.0.2/learn/architecture#deatomize","Deatomize",[190,248],"func (ra *reflectAtomizer) deatomize(src *Atom, dst any) error {\n    v := reflect.ValueOf(dst)\n    if v.Kind() == reflect.Ptr {\n        v = v.Elem()\n    }\n\n    for i := range ra.plan {\n        fp := &ra.plan[i]\n        fv := v.FieldByIndex(fp.index)\n        if err := deatomizeField(fp, src, fv); err != nil {\n            return err\n        }\n    }\n    return nil\n}",{"id":261,"title":262,"titles":263,"content":264,"level":20},"/v1.0.2/learn/architecture#memory-allocation","Memory Allocation",[190],"Atom pre-allocates maps based on field counts: func allocateAtom(spec Spec, tableSet map[Table]int) *Atom {\n    atom := &Atom{\n        Spec:         spec,\n        Nested:       make(map[string]Atom),\n        NestedSlices: make(map[string][]Atom),\n    }\n\n    if n := tableSet[TableStrings]; n > 0 {\n        atom.Strings = make(map[string]string, n)\n    }\n    if n := tableSet[TableInts]; n > 0 {\n        atom.Ints = make(map[string]int64, n)\n    }\n    // ... only allocate maps that will be used\n} This avoids allocating empty maps for unused tables.",{"id":266,"title":267,"titles":268,"content":269,"level":20},"/v1.0.2/learn/architecture#interface-bypass","Interface Bypass",[190],"Types can implement Atomizable and Deatomizable to bypass reflection: type Atomizable interface {\n    Atomize(*Atom)\n}\n\ntype Deatomizable interface {\n    Deatomize(*Atom) error\n} When these interfaces are implemented: func (a *Atomizer[T]) Atomize(obj *T) *Atom {\n    atom := a.inner.newAtom()\n\n    if a.inner.hasAtomizable {\n        if az, ok := any(obj).(Atomizable); ok {\n            az.Atomize(atom)\n            return atom\n        }\n    }\n\n    // Fall back to reflection\n    a.inner.atomize(obj, atom)\n    return atom\n} This enables code generation to eliminate reflection overhead entirely.",{"id":271,"title":64,"titles":272,"content":273,"level":20},"/v1.0.2/learn/architecture#next-steps",[190],"Basic Usage Guide - Common usage patternsInterfaces Guide - Custom serializationCode Generation Cookbook - Generating atomizers html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sfm-E, html code.shiki .sfm-E{--shiki-default:var(--shiki-variable)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}",{"id":275,"title":83,"titles":276,"content":277,"level":9},"/v1.0.2/guides/basic-usage",[],"Common patterns for registration, atomization, and deatomization",{"id":279,"title":83,"titles":280,"content":281,"level":9},"/v1.0.2/guides/basic-usage#basic-usage",[],"Common patterns for working with atom.",{"id":283,"title":284,"titles":285,"content":35,"level":20},"/v1.0.2/guides/basic-usage#registration","Registration",[83],{"id":287,"title":288,"titles":289,"content":290,"level":41},"/v1.0.2/guides/basic-usage#single-type","Single Type",[83,284],"atomizer, err := atom.Use[User]()\nif err != nil {\n    log.Fatalf(\"failed to register User: %v\", err)\n}",{"id":292,"title":293,"titles":294,"content":295,"level":41},"/v1.0.2/guides/basic-usage#multiple-types","Multiple Types",[83,284],"Register all types at startup for predictable initialization: func init() {\n    types := []func() error{\n        func() error { _, err := atom.Use[User](); return err },\n        func() error { _, err := atom.Use[Order](); return err },\n        func() error { _, err := atom.Use[Product](); return err },\n    }\n\n    for _, register := range types {\n        if err := register(); err != nil {\n            log.Fatalf(\"type registration failed: %v\", err)\n        }\n    }\n}",{"id":297,"title":298,"titles":299,"content":300,"level":41},"/v1.0.2/guides/basic-usage#handling-errors","Handling Errors",[83,284],"Registration fails for unsupported types: type Invalid struct {\n    Data map[string]any // Maps are not supported\n}\n\n_, err := atom.Use[Invalid]()\n// err: \"type Invalid: field \"Data\": map types are not supported\"",{"id":302,"title":303,"titles":304,"content":35,"level":20},"/v1.0.2/guides/basic-usage#atomization","Atomization",[83],{"id":306,"title":307,"titles":308,"content":309,"level":41},"/v1.0.2/guides/basic-usage#basic-atomize","Basic Atomize",[83,303],"user := &User{Name: \"Alice\", Age: 30}\natom := atomizer.Atomize(user)",{"id":311,"title":312,"titles":313,"content":314,"level":41},"/v1.0.2/guides/basic-usage#creating-empty-atoms","Creating Empty Atoms",[83,303],"Use NewAtom() to create a properly sized empty atom: atom := atomizer.NewAtom()\natom.Strings[\"Name\"] = \"Bob\"\natom.Ints[\"Age\"] = 25",{"id":316,"title":317,"titles":318,"content":319,"level":41},"/v1.0.2/guides/basic-usage#reading-fields","Reading Fields",[83,303],"Access fields through type-specific maps: atom := atomizer.Atomize(user)\n\nname := atom.Strings[\"Name\"]\nage := atom.Ints[\"Age\"]\nactive := atom.Bools[\"Active\"]\ncreated := atom.Times[\"CreatedAt\"]",{"id":321,"title":322,"titles":323,"content":324,"level":41},"/v1.0.2/guides/basic-usage#checking-field-existence","Checking Field Existence",[83,303],"if name, ok := atom.Strings[\"Name\"]; ok {\n    fmt.Println(\"Name:\", name)\n}",{"id":326,"title":327,"titles":328,"content":35,"level":20},"/v1.0.2/guides/basic-usage#deatomization","Deatomization",[83],{"id":330,"title":331,"titles":332,"content":333,"level":41},"/v1.0.2/guides/basic-usage#basic-deatomize","Basic Deatomize",[83,327],"restored, err := atomizer.Deatomize(atom)\nif err != nil {\n    log.Printf(\"deatomize failed: %v\", err)\n    return\n}",{"id":335,"title":298,"titles":336,"content":337,"level":41},"/v1.0.2/guides/basic-usage#handling-errors-1",[83,327],"Deatomization can fail for overflow: type Small struct {\n    Value int8\n}\n\natomizer, _ := atom.Use[Small]()\na := &atom.Atom{Ints: map[string]int64{\"Value\": 200}}\n\n_, err := atomizer.Deatomize(a)\n// err: \"value 200 overflows int8 (range -128 to 127)\"",{"id":339,"title":340,"titles":341,"content":342,"level":41},"/v1.0.2/guides/basic-usage#missing-fields","Missing Fields",[83,327],"Missing fields are left at their zero value: atom := &atom.Atom{\n    Strings: map[string]string{\"Name\": \"Alice\"},\n    // Age not set\n}\n\nuser, _ := atomizer.Deatomize(atom)\nfmt.Println(user.Name) // \"Alice\"\nfmt.Println(user.Age)  // 0 (zero value)",{"id":344,"title":345,"titles":346,"content":35,"level":20},"/v1.0.2/guides/basic-usage#field-introspection","Field Introspection",[83],{"id":348,"title":349,"titles":350,"content":351,"level":41},"/v1.0.2/guides/basic-usage#list-all-fields","List All Fields",[83,345],"fields := atomizer.Fields()\nfor _, f := range fields {\n    fmt.Printf(\"%s -> %s\\n\", f.Name, f.Table)\n}\n// ID -> ints\n// Name -> strings\n// Email -> strings",{"id":353,"title":354,"titles":355,"content":356,"level":41},"/v1.0.2/guides/basic-usage#fields-by-table","Fields by Table",[83,345],"stringFields := atomizer.FieldsIn(atom.TableStrings)\n// [\"Name\", \"Email\"]\n\nintFields := atomizer.FieldsIn(atom.TableInts)\n// [\"ID\", \"Age\"]",{"id":358,"title":359,"titles":360,"content":361,"level":41},"/v1.0.2/guides/basic-usage#get-table-for-field","Get Table for Field",[83,345],"table, ok := atomizer.TableFor(\"Age\")\nif ok {\n    fmt.Println(\"Age is stored in:\", table) // \"ints\"\n}",{"id":363,"title":364,"titles":365,"content":35,"level":20},"/v1.0.2/guides/basic-usage#working-with-pointers","Working with Pointers",[83],{"id":367,"title":368,"titles":369,"content":370,"level":41},"/v1.0.2/guides/basic-usage#pointer-fields","Pointer Fields",[83,364],"Pointer fields use separate tables and can be nil: type Config struct {\n    Name     string\n    MaxRetry *int64\n    Timeout  *float64\n}\n\natomizer, _ := atom.Use[Config]()\ncfg := &Config{Name: \"default\", MaxRetry: nil}\n\natom := atomizer.Atomize(cfg)\n// atom.Strings[\"Name\"] = \"default\"\n// atom.IntPtrs[\"MaxRetry\"] = nil",{"id":372,"title":373,"titles":374,"content":375,"level":41},"/v1.0.2/guides/basic-usage#setting-pointer-values","Setting Pointer Values",[83,364],"atom := atomizer.NewAtom()\natom.Strings[\"Name\"] = \"custom\"\n\nretries := int64(3)\natom.IntPtrs[\"MaxRetry\"] = &retries\n\ncfg, _ := atomizer.Deatomize(atom)\nfmt.Println(*cfg.MaxRetry) // 3",{"id":377,"title":378,"titles":379,"content":35,"level":20},"/v1.0.2/guides/basic-usage#working-with-slices","Working with Slices",[83],{"id":381,"title":382,"titles":383,"content":384,"level":41},"/v1.0.2/guides/basic-usage#slice-fields","Slice Fields",[83,378],"type User struct {\n    Name   string\n    Tags   []string\n    Scores []int64\n}\n\natomizer, _ := atom.Use[User]()\nuser := &User{\n    Name:   \"Alice\",\n    Tags:   []string{\"admin\", \"verified\"},\n    Scores: []int64{95, 87, 92},\n}\n\natom := atomizer.Atomize(user)\n// atom.Strings[\"Name\"] = \"Alice\"\n// atom.StringSlices[\"Tags\"] = [\"admin\", \"verified\"]\n// atom.IntSlices[\"Scores\"] = [95, 87, 92]",{"id":386,"title":387,"titles":388,"content":389,"level":41},"/v1.0.2/guides/basic-usage#empty-vs-nil-slices","Empty vs Nil Slices",[83,378],"// Nil slice - no entry in atom\nuser := &User{Tags: nil}\natom := atomizer.Atomize(user)\n_, ok := atom.StringSlices[\"Tags\"] // ok = false\n\n// Empty slice - empty entry in atom\nuser := &User{Tags: []string{}}\natom := atomizer.Atomize(user)\ntags := atom.StringSlices[\"Tags\"] // tags = []",{"id":391,"title":392,"titles":393,"content":35,"level":20},"/v1.0.2/guides/basic-usage#type-metadata","Type Metadata",[83],{"id":395,"title":396,"titles":397,"content":398,"level":41},"/v1.0.2/guides/basic-usage#access-spec","Access Spec",[83,392],"spec := atomizer.Spec()\nfmt.Println(spec.TypeName)    // \"User\"\nfmt.Println(spec.PackageName) // \"github.com/example/app\"",{"id":400,"title":401,"titles":402,"content":403,"level":41},"/v1.0.2/guides/basic-usage#atom-spec","Atom Spec",[83,392],"Each atom carries its type spec: atom := atomizer.Atomize(user)\nfmt.Println(atom.Spec.TypeName) // \"User\"",{"id":405,"title":406,"titles":407,"content":35,"level":20},"/v1.0.2/guides/basic-usage#best-practices","Best Practices",[83],{"id":409,"title":410,"titles":411,"content":412,"level":41},"/v1.0.2/guides/basic-usage#do-register-early","Do: Register Early",[83,406],"Register types at application startup: func main() {\n    // Register all types first\n    userAtomizer, _ = atom.Use[User]()\n    orderAtomizer, _ = atom.Use[Order]()\n\n    // Then use them\n    runApp()\n}",{"id":414,"title":415,"titles":416,"content":417,"level":41},"/v1.0.2/guides/basic-usage#do-reuse-atomizers","Do: Reuse Atomizers",[83,406],"Store atomizers rather than calling Use repeatedly: // Good\nvar userAtomizer *atom.Atomizer[User]\n\nfunc init() {\n    userAtomizer, _ = atom.Use[User]()\n}\n\nfunc ProcessUser(u *User) *atom.Atom {\n    return userAtomizer.Atomize(u)\n}",{"id":419,"title":420,"titles":421,"content":422,"level":41},"/v1.0.2/guides/basic-usage#dont-ignore-errors","Don't: Ignore Errors",[83,406],"Always check deatomization errors: // Bad\nuser, _ := atomizer.Deatomize(atom)\n\n// Good\nuser, err := atomizer.Deatomize(atom)\nif err != nil {\n    return fmt.Errorf(\"deatomize: %w\", err)\n}",{"id":424,"title":64,"titles":425,"content":426,"level":20},"/v1.0.2/guides/basic-usage#next-steps",[83],"Custom Types Guide - Named types and byte arraysNested Structs Guide - Working with nested dataInterfaces Guide - Custom serialization html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}",{"id":428,"title":429,"titles":430,"content":431,"level":9},"/v1.0.2/guides/custom-types","Custom Types",[],"Working with named types, enums, and byte arrays",{"id":433,"title":429,"titles":434,"content":435,"level":9},"/v1.0.2/guides/custom-types#custom-types",[],"Working with named types, enums, and byte arrays.",{"id":437,"title":181,"titles":438,"content":439,"level":20},"/v1.0.2/guides/custom-types#named-types",[429],"Named types (type aliases with semantic meaning) are fully supported.",{"id":441,"title":442,"titles":443,"content":444,"level":41},"/v1.0.2/guides/custom-types#string-based-types","String-based Types",[429,181],"type UserID string\ntype Email string\ntype Currency string\n\ntype User struct {\n    ID       UserID\n    Email    Email\n    Currency Currency\n}\n\natomizer, _ := atom.Use[User]()\nuser := &User{\n    ID:       \"usr_123\",\n    Email:    \"alice@example.com\",\n    Currency: \"USD\",\n}\n\natom := atomizer.Atomize(user)\n// All stored in Strings table\n// atom.Strings[\"ID\"] = \"usr_123\"\n// atom.Strings[\"Email\"] = \"alice@example.com\"\n// atom.Strings[\"Currency\"] = \"USD\"",{"id":446,"title":447,"titles":448,"content":449,"level":41},"/v1.0.2/guides/custom-types#integer-based-types-enums","Integer-based Types (Enums)",[429,181],"type Status int\ntype Priority int\n\nconst (\n    StatusPending Status = iota\n    StatusActive\n    StatusComplete\n)\n\nconst (\n    PriorityLow Priority = iota\n    PriorityMedium\n    PriorityHigh\n)\n\ntype Task struct {\n    Name     string\n    Status   Status\n    Priority Priority\n}\n\natomizer, _ := atom.Use[Task]()\ntask := &Task{\n    Name:     \"Review PR\",\n    Status:   StatusActive,\n    Priority: PriorityHigh,\n}\n\natom := atomizer.Atomize(task)\n// atom.Strings[\"Name\"] = \"Review PR\"\n// atom.Ints[\"Status\"] = 1\n// atom.Ints[\"Priority\"] = 2",{"id":451,"title":452,"titles":453,"content":454,"level":41},"/v1.0.2/guides/custom-types#float-based-types","Float-based Types",[429,181],"type Score float64\ntype Percentage float64\n\ntype Result struct {\n    Score      Score\n    Confidence Percentage\n}\n\natomizer, _ := atom.Use[Result]()\nresult := &Result{Score: 95.5, Confidence: 0.87}\n\natom := atomizer.Atomize(result)\n// atom.Floats[\"Score\"] = 95.5\n// atom.Floats[\"Confidence\"] = 0.87",{"id":456,"title":457,"titles":458,"content":35,"level":20},"/v1.0.2/guides/custom-types#named-slices","Named Slices",[429],{"id":460,"title":461,"titles":462,"content":463,"level":41},"/v1.0.2/guides/custom-types#slice-of-named-types","Slice of Named Types",[429,457],"type Tag string\ntype UserID string\n\ntype User struct {\n    ID   UserID\n    Tags []Tag\n}\n\natomizer, _ := atom.Use[User]()\nuser := &User{\n    ID:   \"usr_123\",\n    Tags: []Tag{\"admin\", \"verified\", \"premium\"},\n}\n\natom := atomizer.Atomize(user)\n// atom.Strings[\"ID\"] = \"usr_123\"\n// atom.StringSlices[\"Tags\"] = [\"admin\", \"verified\", \"premium\"]",{"id":465,"title":466,"titles":467,"content":468,"level":41},"/v1.0.2/guides/custom-types#round-trip-preservation","Round-trip Preservation",[429,457],"Named types are preserved through atomization: restored, _ := atomizer.Deatomize(atom)\nfmt.Printf(\"%T\\n\", restored.ID)   // main.UserID\nfmt.Printf(\"%T\\n\", restored.Tags) // []main.Tag",{"id":470,"title":471,"titles":472,"content":35,"level":20},"/v1.0.2/guides/custom-types#byte-slice-types","Byte Slice Types",[429],{"id":474,"title":475,"titles":476,"content":477,"level":41},"/v1.0.2/guides/custom-types#named-byte-types","Named byte Types",[429,471],"Common examples: net.IP, json.RawMessage, custom binary types. type IP []byte\ntype Hash []byte\ntype RawJSON []byte\n\ntype Record struct {\n    ClientIP IP\n    Checksum Hash\n    Payload  RawJSON\n}\n\natomizer, _ := atom.Use[Record]()\nrecord := &Record{\n    ClientIP: IP{192, 168, 1, 1},\n    Checksum: Hash{0xde, 0xad, 0xbe, 0xef},\n    Payload:  RawJSON(`{\"key\": \"value\"}`),\n}\n\natom := atomizer.Atomize(record)\n// All stored in Bytes table\n// atom.Bytes[\"ClientIP\"] = [192, 168, 1, 1]\n// atom.Bytes[\"Checksum\"] = [0xde, 0xad, 0xbe, 0xef]\n// atom.Bytes[\"Payload\"] = [123, 34, 107, ...]",{"id":479,"title":480,"titles":481,"content":482,"level":41},"/v1.0.2/guides/custom-types#netip-example","net.IP Example",[429,471],"import \"net\"\n\ntype Connection struct {\n    LocalAddr  net.IP\n    RemoteAddr net.IP\n    Port       int64\n}\n\natomizer, _ := atom.Use[Connection]()\nconn := &Connection{\n    LocalAddr:  net.ParseIP(\"127.0.0.1\"),\n    RemoteAddr: net.ParseIP(\"10.0.0.1\"),\n    Port:       8080,\n}\n\natom := atomizer.Atomize(conn)\n// atom.Bytes[\"LocalAddr\"] = [127, 0, 0, 1]\n// atom.Bytes[\"RemoteAddr\"] = [10, 0, 0, 1]\n// atom.Ints[\"Port\"] = 8080",{"id":484,"title":485,"titles":486,"content":487,"level":20},"/v1.0.2/guides/custom-types#fixed-size-byte-arrays","Fixed-Size Byte Arrays",[429],"Arrays of bytes [N]byte are supported and stored in the Bytes table.",{"id":489,"title":83,"titles":490,"content":491,"level":41},"/v1.0.2/guides/custom-types#basic-usage",[429,485],"type HashID [32]byte\ntype UUID [16]byte\n\ntype Document struct {\n    ID   UUID\n    Hash HashID\n}\n\natomizer, _ := atom.Use[Document]()\n\nvar id UUID\ncopy(id[:], \"0123456789abcdef\")\n\nvar hash HashID\ncopy(hash[:], \"abcdefghijklmnopqrstuvwxyz012345\")\n\ndoc := &Document{ID: id, Hash: hash}\natom := atomizer.Atomize(doc)\n// atom.Bytes[\"ID\"] = [48, 49, 50, 51, ...] (16 bytes)\n// atom.Bytes[\"Hash\"] = [97, 98, 99, 100, ...] (32 bytes)",{"id":493,"title":494,"titles":495,"content":496,"level":41},"/v1.0.2/guides/custom-types#size-validation","Size Validation",[429,485],"During deatomization, byte slice length must match array size: type Fixed struct {\n    Data [4]byte\n}\n\natomizer, _ := atom.Use[Fixed]()\n\n// Correct size\natom := &atom.Atom{Bytes: map[string][]byte{\"Data\": {1, 2, 3, 4}}}\nfixed, err := atomizer.Deatomize(atom) // OK\n\n// Wrong size\natom = &atom.Atom{Bytes: map[string][]byte{\"Data\": {1, 2}}}\n_, err = atomizer.Deatomize(atom)\n// err: \"field Data: expected 4 bytes, got 2\"",{"id":498,"title":499,"titles":500,"content":501,"level":20},"/v1.0.2/guides/custom-types#pointer-to-named-types","Pointer to Named Types",[429],"type UserID string\ntype Score float64\n\ntype OptionalUser struct {\n    ID    *UserID\n    Score *Score\n}\n\natomizer, _ := atom.Use[OptionalUser]()\n\nid := UserID(\"usr_123\")\nscore := Score(95.5)\n\nuser := &OptionalUser{ID: &id, Score: &score}\natom := atomizer.Atomize(user)\n// atom.StringPtrs[\"ID\"] = &\"usr_123\"\n// atom.FloatPtrs[\"Score\"] = &95.5\n\n// With nil values\nuser = &OptionalUser{ID: nil, Score: nil}\natom = atomizer.Atomize(user)\n// atom.StringPtrs[\"ID\"] = nil\n// atom.FloatPtrs[\"Score\"] = nil",{"id":503,"title":504,"titles":505,"content":506,"level":20},"/v1.0.2/guides/custom-types#type-mapping-reference","Type Mapping Reference",[429],"Named Type BaseStorage Tabletype X stringstringstype X int*intstype X uint*uintstype X float*floatstype X boolboolstype X []bytebytestype X [N]bytebytestype X []string (etc)corresponding slice tabletype X *string (etc)corresponding pointer table",{"id":508,"title":406,"titles":509,"content":35,"level":20},"/v1.0.2/guides/custom-types#best-practices",[429],{"id":511,"title":512,"titles":513,"content":514,"level":41},"/v1.0.2/guides/custom-types#use-named-types-for-domain-concepts","Use Named Types for Domain Concepts",[429,406],"// Good - clear semantics\ntype UserID string\ntype Email string\ntype Money int64 // cents\n\ntype User struct {\n    ID      UserID\n    Email   Email\n    Balance Money\n}\n\n// Bad - primitive soup\ntype User struct {\n    ID      string\n    Email   string\n    Balance int64\n}",{"id":516,"title":517,"titles":518,"content":519,"level":41},"/v1.0.2/guides/custom-types#use-enums-instead-of-strings","Use Enums Instead of Strings",[429,406],"// Good - type-safe\ntype Status int\nconst (\n    StatusActive Status = iota\n    StatusInactive\n)\n\n// Bad - error-prone\ntype User struct {\n    Status string // \"active\", \"inactive\", typos possible\n}",{"id":521,"title":64,"titles":522,"content":523,"level":20},"/v1.0.2/guides/custom-types#next-steps",[429],"Nested Structs Guide - Working with nested dataInterfaces Guide - Custom serialization html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sfm-E, html code.shiki .sfm-E{--shiki-default:var(--shiki-variable)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}",{"id":525,"title":526,"titles":527,"content":528,"level":9},"/v1.0.2/guides/nested-structs","Nested Structs",[],"Working with embedded and nested struct types",{"id":530,"title":526,"titles":531,"content":532,"level":9},"/v1.0.2/guides/nested-structs#nested-structs",[],"Working with embedded and nested struct types.",{"id":534,"title":535,"titles":536,"content":537,"level":20},"/v1.0.2/guides/nested-structs#embedded-structs","Embedded Structs",[526],"Embedded structs are stored in the Nested map: type Address struct {\n    Street  string\n    City    string\n    ZipCode string\n}\n\ntype User struct {\n    Name    string\n    Email   string\n    Address Address // Embedded struct\n}\n\natomizer, _ := atom.Use[User]()\nuser := &User{\n    Name:  \"Alice\",\n    Email: \"alice@example.com\",\n    Address: Address{\n        Street:  \"123 Main St\",\n        City:    \"Springfield\",\n        ZipCode: \"12345\",\n    },\n}\n\natom := atomizer.Atomize(user)\n// atom.Strings[\"Name\"] = \"Alice\"\n// atom.Strings[\"Email\"] = \"alice@example.com\"\n// atom.Nested[\"Address\"] = Atom{\n//     Strings: {\"Street\": \"123 Main St\", \"City\": \"Springfield\", \"ZipCode\": \"12345\"}\n// }",{"id":539,"title":540,"titles":541,"content":542,"level":20},"/v1.0.2/guides/nested-structs#accessing-nested-atoms","Accessing Nested Atoms",[526],"// Access the nested atom\naddressAtom := atom.Nested[\"Address\"]\n\n// Read nested fields\nstreet := addressAtom.Strings[\"Street\"]\ncity := addressAtom.Strings[\"City\"]\n\nfmt.Println(street) // \"123 Main St\"\nfmt.Println(city)   // \"Springfield\"",{"id":544,"title":545,"titles":546,"content":547,"level":20},"/v1.0.2/guides/nested-structs#pointer-to-struct","Pointer to Struct",[526],"Pointer fields allow optional nested structs: type User struct {\n    Name    string\n    Address *Address // Optional\n}\n\natomizer, _ := atom.Use[User]()\n\n// With address\nuser := &User{\n    Name:    \"Alice\",\n    Address: &Address{Street: \"123 Main St\", City: \"Springfield\"},\n}\natom := atomizer.Atomize(user)\n// atom.Nested[\"Address\"] exists\n\n// Without address\nuser = &User{Name: \"Bob\", Address: nil}\natom = atomizer.Atomize(user)\n// atom.Nested[\"Address\"] does not exist",{"id":549,"title":550,"titles":551,"content":552,"level":41},"/v1.0.2/guides/nested-structs#checking-for-nil","Checking for Nil",[526,545],"if addressAtom, ok := atom.Nested[\"Address\"]; ok {\n    fmt.Println(\"Has address:\", addressAtom.Strings[\"City\"])\n} else {\n    fmt.Println(\"No address\")\n}",{"id":554,"title":555,"titles":556,"content":557,"level":20},"/v1.0.2/guides/nested-structs#slice-of-structs","Slice of Structs",[526],"Slices of structs are stored in NestedSlices: type Order struct {\n    ID    int64\n    Items []OrderItem\n}\n\ntype OrderItem struct {\n    ProductID int64\n    Quantity  int64\n    Price     float64\n}\n\natomizer, _ := atom.Use[Order]()\norder := &Order{\n    ID: 1001,\n    Items: []OrderItem{\n        {ProductID: 1, Quantity: 2, Price: 19.99},\n        {ProductID: 2, Quantity: 1, Price: 49.99},\n    },\n}\n\natom := atomizer.Atomize(order)\n// atom.Ints[\"ID\"] = 1001\n// atom.NestedSlices[\"Items\"] = [\n//     Atom{Ints: {\"ProductID\": 1, \"Quantity\": 2}, Floats: {\"Price\": 19.99}},\n//     Atom{Ints: {\"ProductID\": 2, \"Quantity\": 1}, Floats: {\"Price\": 49.99}},\n// ]",{"id":559,"title":560,"titles":561,"content":562,"level":41},"/v1.0.2/guides/nested-structs#iterating-nested-slices","Iterating Nested Slices",[526,555],"for i, itemAtom := range atom.NestedSlices[\"Items\"] {\n    productID := itemAtom.Ints[\"ProductID\"]\n    quantity := itemAtom.Ints[\"Quantity\"]\n    price := itemAtom.Floats[\"Price\"]\n    fmt.Printf(\"Item %d: product=%d qty=%d price=%.2f\\n\",\n        i, productID, quantity, price)\n}",{"id":564,"title":565,"titles":566,"content":567,"level":20},"/v1.0.2/guides/nested-structs#deeply-nested-structures","Deeply Nested Structures",[526],"Atom handles arbitrary nesting depth: type Company struct {\n    Name        string\n    Departments []Department\n}\n\ntype Department struct {\n    Name    string\n    Manager Employee\n    Staff   []Employee\n}\n\ntype Employee struct {\n    Name   string\n    Title  string\n    Salary float64\n}\n\natomizer, _ := atom.Use[Company]()\ncompany := &Company{\n    Name: \"Acme Corp\",\n    Departments: []Department{\n        {\n            Name: \"Engineering\",\n            Manager: Employee{Name: \"Alice\", Title: \"VP\", Salary: 150000},\n            Staff: []Employee{\n                {Name: \"Bob\", Title: \"Senior\", Salary: 120000},\n                {Name: \"Carol\", Title: \"Junior\", Salary: 80000},\n            },\n        },\n    },\n}\n\natom := atomizer.Atomize(company)\n// atom.Strings[\"Name\"] = \"Acme Corp\"\n// atom.NestedSlices[\"Departments\"][0].Strings[\"Name\"] = \"Engineering\"\n// atom.NestedSlices[\"Departments\"][0].Nested[\"Manager\"].Strings[\"Name\"] = \"Alice\"\n// atom.NestedSlices[\"Departments\"][0].NestedSlices[\"Staff\"][0].Strings[\"Name\"] = \"Bob\"",{"id":569,"title":570,"titles":571,"content":572,"level":20},"/v1.0.2/guides/nested-structs#self-referential-types","Self-Referential Types",[526],"Atom handles recursive type definitions: type Node struct {\n    Value    int64\n    Children []Node\n}\n\natomizer, _ := atom.Use[Node]()\ntree := &Node{\n    Value: 1,\n    Children: []Node{\n        {Value: 2, Children: nil},\n        {Value: 3, Children: []Node{\n            {Value: 4, Children: nil},\n        }},\n    },\n}\n\natom := atomizer.Atomize(tree)\n// atom.Ints[\"Value\"] = 1\n// atom.NestedSlices[\"Children\"][0].Ints[\"Value\"] = 2\n// atom.NestedSlices[\"Children\"][1].Ints[\"Value\"] = 3\n// atom.NestedSlices[\"Children\"][1].NestedSlices[\"Children\"][0].Ints[\"Value\"] = 4",{"id":574,"title":575,"titles":576,"content":577,"level":41},"/v1.0.2/guides/nested-structs#pointer-self-reference","Pointer Self-Reference",[526,570],"type LinkedNode struct {\n    Value int64\n    Next  *LinkedNode\n}\n\natomizer, _ := atom.Use[LinkedNode]()\nlist := &LinkedNode{\n    Value: 1,\n    Next: &LinkedNode{\n        Value: 2,\n        Next:  nil,\n    },\n}\n\natom := atomizer.Atomize(list)\n// atom.Ints[\"Value\"] = 1\n// atom.Nested[\"Next\"].Ints[\"Value\"] = 2\n// atom.Nested[\"Next\"].Nested[\"Next\"] does not exist (nil)",{"id":579,"title":580,"titles":581,"content":582,"level":20},"/v1.0.2/guides/nested-structs#slice-of-pointer-to-struct","Slice of Pointer to Struct",[526],"type Team struct {\n    Name    string\n    Members []*Person\n}\n\ntype Person struct {\n    Name string\n    Role string\n}\n\natomizer, _ := atom.Use[Team]()\nteam := &Team{\n    Name: \"Alpha\",\n    Members: []*Person{\n        {Name: \"Alice\", Role: \"Lead\"},\n        {Name: \"Bob\", Role: \"Developer\"},\n    },\n}\n\natom := atomizer.Atomize(team)\n// Works the same as []Person\n// atom.NestedSlices[\"Members\"][0].Strings[\"Name\"] = \"Alice\"",{"id":584,"title":585,"titles":586,"content":587,"level":20},"/v1.0.2/guides/nested-structs#building-nested-atoms-manually","Building Nested Atoms Manually",[526],"// Create parent atomizer\norderAtomizer, _ := atom.Use[Order]()\nitemAtomizer, _ := atom.Use[OrderItem]()\n\n// Build nested atoms\nitem1 := itemAtomizer.NewAtom()\nitem1.Ints[\"ProductID\"] = 1\nitem1.Ints[\"Quantity\"] = 2\nitem1.Floats[\"Price\"] = 19.99\n\nitem2 := itemAtomizer.NewAtom()\nitem2.Ints[\"ProductID\"] = 2\nitem2.Ints[\"Quantity\"] = 1\nitem2.Floats[\"Price\"] = 49.99\n\n// Assemble parent\norderAtom := orderAtomizer.NewAtom()\norderAtom.Ints[\"ID\"] = 1001\norderAtom.NestedSlices[\"Items\"] = []atom.Atom{*item1, *item2}\n\n// Deatomize\norder, _ := orderAtomizer.Deatomize(orderAtom)",{"id":589,"title":590,"titles":591,"content":592,"level":20},"/v1.0.2/guides/nested-structs#spec-propagation","Spec Propagation",[526],"Nested atoms carry their own type specs: atom := atomizer.Atomize(user)\n\nfmt.Println(atom.Spec.TypeName)                    // \"User\"\nfmt.Println(atom.Nested[\"Address\"].Spec.TypeName)  // \"Address\"",{"id":594,"title":406,"titles":595,"content":35,"level":20},"/v1.0.2/guides/nested-structs#best-practices",[526],{"id":597,"title":598,"titles":599,"content":600,"level":41},"/v1.0.2/guides/nested-structs#flatten-when-possible","Flatten When Possible",[526,406],"For simple cases, consider flattening: // Deeply nested (harder to query)\ntype User struct {\n    Profile struct {\n        Contact struct {\n            Email string\n        }\n    }\n}\n\n// Flattened (easier to query)\ntype User struct {\n    ProfileContactEmail string\n}",{"id":602,"title":603,"titles":604,"content":605,"level":41},"/v1.0.2/guides/nested-structs#use-pointers-for-optional","Use Pointers for Optional",[526,406],"// Good - explicit optionality\ntype User struct {\n    BillingAddress  *Address // Optional\n    ShippingAddress Address  // Required\n}\n\n// Ambiguous - is empty address intentional or missing?\ntype User struct {\n    BillingAddress  Address\n    ShippingAddress Address\n}",{"id":607,"title":608,"titles":609,"content":610,"level":41},"/v1.0.2/guides/nested-structs#limit-nesting-depth","Limit Nesting Depth",[526,406],"Deep nesting increases complexity: // Hard to work with\natom.NestedSlices[\"A\"][0].Nested[\"B\"].NestedSlices[\"C\"][0].Strings[\"D\"]\n\n// Consider restructuring or using IDs/references",{"id":612,"title":64,"titles":613,"content":614,"level":20},"/v1.0.2/guides/nested-structs#next-steps",[526],"Interfaces Guide - Custom serializationTesting Guide - Testing atom-based code html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}",{"id":616,"title":617,"titles":618,"content":619,"level":9},"/v1.0.2/guides/interfaces","Interfaces",[],"Custom serialization with Atomizable and Deatomizable interfaces",{"id":621,"title":617,"titles":622,"content":623,"level":9},"/v1.0.2/guides/interfaces#interfaces",[],"Custom serialization with Atomizable and Deatomizable.",{"id":625,"title":6,"titles":626,"content":627,"level":20},"/v1.0.2/guides/interfaces#overview",[617],"By default, atom uses reflection to convert structs to atoms. You can bypass reflection by implementing the Atomizable and Deatomizable interfaces: type Atomizable interface {\n    Atomize(*Atom)\n}\n\ntype Deatomizable interface {\n    Deatomize(*Atom) error\n}",{"id":629,"title":630,"titles":631,"content":632,"level":20},"/v1.0.2/guides/interfaces#why-use-interfaces","Why Use Interfaces?",[617],"Performance: Eliminate reflection overheadCustom Logic: Transform data during serializationComputed Fields: Derive values not stored in the structBackwards Compatibility: Handle schema migrations",{"id":634,"title":635,"titles":636,"content":637,"level":20},"/v1.0.2/guides/interfaces#implementing-atomizable","Implementing Atomizable",[617],"type User struct {\n    FirstName string\n    LastName  string\n    BirthYear int64\n}\n\nfunc (u *User) Atomize(a *Atom) {\n    a.Strings[\"FirstName\"] = u.FirstName\n    a.Strings[\"LastName\"] = u.LastName\n    a.Ints[\"BirthYear\"] = u.BirthYear\n\n    // Computed field\n    a.Strings[\"FullName\"] = u.FirstName + \" \" + u.LastName\n}",{"id":639,"title":640,"titles":641,"content":642,"level":41},"/v1.0.2/guides/interfaces#usage","Usage",[617,635],"atomizer, _ := atom.Use[User]()\nuser := &User{FirstName: \"Alice\", LastName: \"Smith\", BirthYear: 1990}\n\natom := atomizer.Atomize(user)\n// Uses User.Atomize() instead of reflection\n\nfmt.Println(atom.Strings[\"FullName\"]) // \"Alice Smith\"",{"id":644,"title":645,"titles":646,"content":647,"level":20},"/v1.0.2/guides/interfaces#implementing-deatomizable","Implementing Deatomizable",[617],"func (u *User) Deatomize(a *Atom) error {\n    u.FirstName = a.Strings[\"FirstName\"]\n    u.LastName = a.Strings[\"LastName\"]\n    u.BirthYear = a.Ints[\"BirthYear\"]\n\n    // Validation\n    if u.BirthYear \u003C 1900 || u.BirthYear > 2100 {\n        return fmt.Errorf(\"invalid birth year: %d\", u.BirthYear)\n    }\n\n    return nil\n}",{"id":649,"title":640,"titles":650,"content":651,"level":41},"/v1.0.2/guides/interfaces#usage-1",[617,645],"atom := &Atom{\n    Strings: map[string]string{\"FirstName\": \"Bob\", \"LastName\": \"Jones\"},\n    Ints:    map[string]int64{\"BirthYear\": 1985},\n}\n\nuser, err := atomizer.Deatomize(atom)\n// Uses User.Deatomize() instead of reflection",{"id":653,"title":654,"titles":655,"content":656,"level":20},"/v1.0.2/guides/interfaces#partial-implementation","Partial Implementation",[617],"You can implement only one interface:",{"id":658,"title":659,"titles":660,"content":661,"level":41},"/v1.0.2/guides/interfaces#atomizable-only","Atomizable Only",[617,654],"type Metrics struct {\n    RequestCount int64\n    ErrorCount   int64\n    // Internal, not serialized\n    lastUpdate time.Time\n}\n\nfunc (m *Metrics) Atomize(a *Atom) {\n    a.Ints[\"RequestCount\"] = m.RequestCount\n    a.Ints[\"ErrorCount\"] = m.ErrorCount\n    // Computed\n    a.Floats[\"ErrorRate\"] = float64(m.ErrorCount) / float64(m.RequestCount)\n}\n\n// No Deatomize - uses reflection",{"id":663,"title":664,"titles":665,"content":666,"level":41},"/v1.0.2/guides/interfaces#deatomizable-only","Deatomizable Only",[617,654],"type Config struct {\n    Host string\n    Port int64\n}\n\n// No Atomize - uses reflection\n\nfunc (c *Config) Deatomize(a *Atom) error {\n    c.Host = a.Strings[\"Host\"]\n    c.Port = a.Ints[\"Port\"]\n\n    // Apply defaults\n    if c.Host == \"\" {\n        c.Host = \"localhost\"\n    }\n    if c.Port == 0 {\n        c.Port = 8080\n    }\n\n    return nil\n}",{"id":668,"title":669,"titles":670,"content":671,"level":20},"/v1.0.2/guides/interfaces#handling-nested-types","Handling Nested Types",[617],"For nested structs, call their methods: type Order struct {\n    ID    int64\n    Items []OrderItem\n}\n\ntype OrderItem struct {\n    ProductID int64\n    Quantity  int64\n}\n\nfunc (o *Order) Atomize(a *Atom) {\n    a.Ints[\"ID\"] = o.ID\n\n    items := make([]Atom, len(o.Items))\n    for i, item := range o.Items {\n        itemAtom := Atom{Ints: make(map[string]int64)}\n        item.Atomize(&itemAtom)\n        items[i] = itemAtom\n    }\n    a.NestedSlices[\"Items\"] = items\n}\n\nfunc (item *OrderItem) Atomize(a *Atom) {\n    a.Ints[\"ProductID\"] = item.ProductID\n    a.Ints[\"Quantity\"] = item.Quantity\n}",{"id":673,"title":674,"titles":675,"content":676,"level":20},"/v1.0.2/guides/interfaces#schema-migration","Schema Migration",[617],"Handle old and new field names: type User struct {\n    Email string // Was \"EmailAddress\" in v1\n}\n\nfunc (u *User) Deatomize(a *Atom) error {\n    // Try new name first\n    if email, ok := a.Strings[\"Email\"]; ok {\n        u.Email = email\n    } else if email, ok := a.Strings[\"EmailAddress\"]; ok {\n        // Fall back to old name\n        u.Email = email\n    }\n    return nil\n}\n\nfunc (u *User) Atomize(a *Atom) {\n    // Always write new name\n    a.Strings[\"Email\"] = u.Email\n}",{"id":678,"title":679,"titles":680,"content":681,"level":20},"/v1.0.2/guides/interfaces#encryptionencoding","Encryption/Encoding",[617],"Transform sensitive data: type Secret struct {\n    Data []byte\n}\n\nfunc (s *Secret) Atomize(a *Atom) {\n    // Encrypt before storing\n    encrypted := encrypt(s.Data)\n    a.Bytes[\"Data\"] = encrypted\n}\n\nfunc (s *Secret) Deatomize(a *Atom) error {\n    encrypted := a.Bytes[\"Data\"]\n    // Decrypt after loading\n    decrypted, err := decrypt(encrypted)\n    if err != nil {\n        return err\n    }\n    s.Data = decrypted\n    return nil\n}",{"id":683,"title":684,"titles":685,"content":686,"level":20},"/v1.0.2/guides/interfaces#validation","Validation",[617],"Validate during deatomization: type User struct {\n    Age   int64\n    Email string\n}\n\nfunc (u *User) Deatomize(a *Atom) error {\n    u.Age = a.Ints[\"Age\"]\n    u.Email = a.Strings[\"Email\"]\n\n    // Validation\n    if u.Age \u003C 0 || u.Age > 150 {\n        return fmt.Errorf(\"invalid age: %d\", u.Age)\n    }\n    if !strings.Contains(u.Email, \"@\") {\n        return fmt.Errorf(\"invalid email: %s\", u.Email)\n    }\n\n    return nil\n}",{"id":688,"title":689,"titles":690,"content":691,"level":20},"/v1.0.2/guides/interfaces#code-generation","Code Generation",[617],"The interface pattern enables code generation to eliminate reflection: // Generated by atomgen\nfunc (u *User) Atomize(a *Atom) {\n    a.Strings[\"FirstName\"] = u.FirstName\n    a.Strings[\"LastName\"] = u.LastName\n    a.Ints[\"Age\"] = u.Age\n    a.Bools[\"Active\"] = u.Active\n}\n\nfunc (u *User) Deatomize(a *Atom) error {\n    u.FirstName = a.Strings[\"FirstName\"]\n    u.LastName = a.Strings[\"LastName\"]\n    u.Age = a.Ints[\"Age\"]\n    u.Active = a.Bools[\"Active\"]\n    return nil\n} See Code Generation Cookbook for details.",{"id":693,"title":406,"titles":694,"content":35,"level":20},"/v1.0.2/guides/interfaces#best-practices",[617],{"id":696,"title":697,"titles":698,"content":699,"level":41},"/v1.0.2/guides/interfaces#keep-it-simple","Keep It Simple",[617,406],"// Good - straightforward mapping\nfunc (u *User) Atomize(a *Atom) {\n    a.Strings[\"Name\"] = u.Name\n    a.Ints[\"Age\"] = u.Age\n}\n\n// Avoid - complex logic in serialization\nfunc (u *User) Atomize(a *Atom) {\n    // Don't do heavy computation here\n    a.Strings[\"Name\"] = expensiveTransform(u.Name)\n}",{"id":701,"title":702,"titles":703,"content":704,"level":41},"/v1.0.2/guides/interfaces#match-the-structure","Match the Structure",[617,406],"// Good - mirrors struct fields\nfunc (u *User) Atomize(a *Atom) {\n    a.Strings[\"Name\"] = u.Name\n    a.Strings[\"Email\"] = u.Email\n}\n\n// Confusing - different structure\nfunc (u *User) Atomize(a *Atom) {\n    a.Strings[\"user_name\"] = u.Name      // Different key\n    a.Ints[\"email_hash\"] = hash(u.Email) // Different type/value\n}",{"id":706,"title":707,"titles":708,"content":709,"level":41},"/v1.0.2/guides/interfaces#document-migrations","Document Migrations",[617,406],"func (u *User) Deatomize(a *Atom) error {\n    // Migration: \"name\" was renamed to \"full_name\" in v2.0\n    if name, ok := a.Strings[\"full_name\"]; ok {\n        u.Name = name\n    } else {\n        u.Name = a.Strings[\"name\"] // Legacy\n    }\n    return nil\n}",{"id":711,"title":64,"titles":712,"content":713,"level":20},"/v1.0.2/guides/interfaces#next-steps",[617],"Testing Guide - Testing atom-based codeCode Generation Cookbook - Generating implementations html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}",{"id":715,"title":716,"titles":717,"content":718,"level":9},"/v1.0.2/guides/testing","Testing",[],"Testing strategies for atom-based code",{"id":720,"title":716,"titles":721,"content":722,"level":9},"/v1.0.2/guides/testing#testing",[],"Testing strategies for atom-based code.",{"id":724,"title":725,"titles":726,"content":35,"level":20},"/v1.0.2/guides/testing#basic-testing","Basic Testing",[716],{"id":728,"title":729,"titles":730,"content":731,"level":41},"/v1.0.2/guides/testing#round-trip-tests","Round-Trip Tests",[716,725],"Verify that atomize/deatomize preserves data: func TestUserRoundTrip(t *testing.T) {\n    atomizer, err := atom.Use[User]()\n    if err != nil {\n        t.Fatalf(\"Use failed: %v\", err)\n    }\n\n    original := &User{\n        Name:   \"Alice\",\n        Age:    30,\n        Active: true,\n    }\n\n    // Atomize\n    a := atomizer.Atomize(original)\n\n    // Deatomize\n    restored, err := atomizer.Deatomize(a)\n    if err != nil {\n        t.Fatalf(\"Deatomize failed: %v\", err)\n    }\n\n    // Compare\n    if restored.Name != original.Name {\n        t.Errorf(\"Name: got %q, want %q\", restored.Name, original.Name)\n    }\n    if restored.Age != original.Age {\n        t.Errorf(\"Age: got %d, want %d\", restored.Age, original.Age)\n    }\n    if restored.Active != original.Active {\n        t.Errorf(\"Active: got %v, want %v\", restored.Active, original.Active)\n    }\n}",{"id":733,"title":734,"titles":735,"content":736,"level":41},"/v1.0.2/guides/testing#using-reflectdeepequal","Using reflect.DeepEqual",[716,725],"For complex structs: func TestComplexRoundTrip(t *testing.T) {\n    atomizer, _ := atom.Use[ComplexType]()\n\n    original := &ComplexType{\n        // ... many fields\n    }\n\n    a := atomizer.Atomize(original)\n    restored, err := atomizer.Deatomize(a)\n    if err != nil {\n        t.Fatalf(\"Deatomize failed: %v\", err)\n    }\n\n    if !reflect.DeepEqual(original, restored) {\n        t.Errorf(\"round-trip failed:\\noriginal: %+v\\nrestored: %+v\",\n            original, restored)\n    }\n}",{"id":738,"title":739,"titles":740,"content":35,"level":20},"/v1.0.2/guides/testing#testing-specific-fields","Testing Specific Fields",[716],{"id":742,"title":743,"titles":744,"content":745,"level":41},"/v1.0.2/guides/testing#verify-atomization-output","Verify Atomization Output",[716,739],"func TestUserAtomization(t *testing.T) {\n    atomizer, _ := atom.Use[User]()\n\n    user := &User{Name: \"Alice\", Age: 30}\n    a := atomizer.Atomize(user)\n\n    // Check specific fields\n    if got := a.Strings[\"Name\"]; got != \"Alice\" {\n        t.Errorf(\"Strings[Name]: got %q, want %q\", got, \"Alice\")\n    }\n    if got := a.Ints[\"Age\"]; got != 30 {\n        t.Errorf(\"Ints[Age]: got %d, want %d\", got, 30)\n    }\n}",{"id":747,"title":748,"titles":749,"content":750,"level":41},"/v1.0.2/guides/testing#verify-deatomization-input","Verify Deatomization Input",[716,739],"func TestUserDeatomization(t *testing.T) {\n    atomizer, _ := atom.Use[User]()\n\n    a := &atom.Atom{\n        Strings: map[string]string{\"Name\": \"Bob\"},\n        Ints:    map[string]int64{\"Age\": 25},\n    }\n\n    user, err := atomizer.Deatomize(a)\n    if err != nil {\n        t.Fatalf(\"Deatomize failed: %v\", err)\n    }\n\n    if user.Name != \"Bob\" {\n        t.Errorf(\"Name: got %q, want %q\", user.Name, \"Bob\")\n    }\n    if user.Age != 25 {\n        t.Errorf(\"Age: got %d, want %d\", user.Age, 25)\n    }\n}",{"id":752,"title":753,"titles":754,"content":35,"level":20},"/v1.0.2/guides/testing#table-driven-tests","Table-Driven Tests",[716],{"id":756,"title":757,"titles":758,"content":759,"level":41},"/v1.0.2/guides/testing#testing-multiple-cases","Testing Multiple Cases",[716,753],"func TestUserRoundTrips(t *testing.T) {\n    atomizer, _ := atom.Use[User]()\n\n    tests := []struct {\n        name string\n        user User\n    }{\n        {\"empty\", User{}},\n        {\"basic\", User{Name: \"Alice\", Age: 30}},\n        {\"all fields\", User{Name: \"Bob\", Age: 25, Active: true}},\n        {\"unicode\", User{Name: \"日本語\", Age: 100}},\n        {\"max age\", User{Name: \"Elder\", Age: math.MaxInt64}},\n    }\n\n    for _, tt := range tests {\n        t.Run(tt.name, func(t *testing.T) {\n            a := atomizer.Atomize(&tt.user)\n            restored, err := atomizer.Deatomize(a)\n            if err != nil {\n                t.Fatalf(\"Deatomize failed: %v\", err)\n            }\n            if !reflect.DeepEqual(&tt.user, restored) {\n                t.Errorf(\"mismatch:\\noriginal: %+v\\nrestored: %+v\",\n                    tt.user, restored)\n            }\n        })\n    }\n}",{"id":761,"title":762,"titles":763,"content":35,"level":20},"/v1.0.2/guides/testing#testing-error-cases","Testing Error Cases",[716],{"id":765,"title":223,"titles":766,"content":767,"level":41},"/v1.0.2/guides/testing#overflow-detection",[716,762],"func TestOverflowDetection(t *testing.T) {\n    type Small struct {\n        Value int8\n    }\n\n    atomizer, _ := atom.Use[Small]()\n\n    tests := []struct {\n        name    string\n        value   int64\n        wantErr bool\n    }{\n        {\"valid positive\", 100, false},\n        {\"valid negative\", -100, false},\n        {\"overflow positive\", 200, true},\n        {\"overflow negative\", -200, true},\n        {\"max\", 127, false},\n        {\"min\", -128, false},\n    }\n\n    for _, tt := range tests {\n        t.Run(tt.name, func(t *testing.T) {\n            a := &atom.Atom{Ints: map[string]int64{\"Value\": tt.value}}\n            _, err := atomizer.Deatomize(a)\n            if (err != nil) != tt.wantErr {\n                t.Errorf(\"error = %v, wantErr = %v\", err, tt.wantErr)\n            }\n        })\n    }\n}",{"id":769,"title":770,"titles":771,"content":772,"level":41},"/v1.0.2/guides/testing#unsupported-types","Unsupported Types",[716,762],"func TestUnsupportedType(t *testing.T) {\n    type Invalid struct {\n        Data map[string]any\n    }\n\n    _, err := atom.Use[Invalid]()\n    if err == nil {\n        t.Fatal(\"expected error for unsupported type\")\n    }\n    if !strings.Contains(err.Error(), \"map types are not supported\") {\n        t.Errorf(\"unexpected error: %v\", err)\n    }\n}",{"id":774,"title":775,"titles":776,"content":777,"level":20},"/v1.0.2/guides/testing#testing-nested-structs","Testing Nested Structs",[716],"func TestNestedRoundTrip(t *testing.T) {\n    type Address struct {\n        Street string\n        City   string\n    }\n    type User struct {\n        Name    string\n        Address Address\n    }\n\n    atomizer, _ := atom.Use[User]()\n\n    original := &User{\n        Name: \"Alice\",\n        Address: Address{\n            Street: \"123 Main St\",\n            City:   \"Springfield\",\n        },\n    }\n\n    a := atomizer.Atomize(original)\n\n    // Verify nested structure\n    if _, ok := a.Nested[\"Address\"]; !ok {\n        t.Fatal(\"expected Address in Nested map\")\n    }\n\n    restored, err := atomizer.Deatomize(a)\n    if err != nil {\n        t.Fatalf(\"Deatomize failed: %v\", err)\n    }\n\n    if restored.Address.Street != original.Address.Street {\n        t.Errorf(\"Street: got %q, want %q\",\n            restored.Address.Street, original.Address.Street)\n    }\n}",{"id":779,"title":780,"titles":781,"content":782,"level":20},"/v1.0.2/guides/testing#testing-custom-interfaces","Testing Custom Interfaces",[716],"type CustomUser struct {\n    Name string\n}\n\nfunc (u *CustomUser) Atomize(a *atom.Atom) {\n    a.Strings[\"Name\"] = \"custom:\" + u.Name\n}\n\nfunc (u *CustomUser) Deatomize(a *atom.Atom) error {\n    name := a.Strings[\"Name\"]\n    if !strings.HasPrefix(name, \"custom:\") {\n        return fmt.Errorf(\"invalid format\")\n    }\n    u.Name = strings.TrimPrefix(name, \"custom:\")\n    return nil\n}\n\nfunc TestCustomInterface(t *testing.T) {\n    atomizer, _ := atom.Use[CustomUser]()\n\n    original := &CustomUser{Name: \"Alice\"}\n    a := atomizer.Atomize(original)\n\n    // Verify custom atomization was used\n    if got := a.Strings[\"Name\"]; got != \"custom:Alice\" {\n        t.Errorf(\"expected custom format, got %q\", got)\n    }\n\n    restored, err := atomizer.Deatomize(a)\n    if err != nil {\n        t.Fatalf(\"Deatomize failed: %v\", err)\n    }\n\n    if restored.Name != original.Name {\n        t.Errorf(\"Name: got %q, want %q\", restored.Name, original.Name)\n    }\n}",{"id":784,"title":785,"titles":786,"content":787,"level":20},"/v1.0.2/guides/testing#concurrent-testing","Concurrent Testing",[716],"func TestConcurrentUse(t *testing.T) {\n    var wg sync.WaitGroup\n\n    for i := 0; i \u003C 100; i++ {\n        wg.Add(1)\n        go func() {\n            defer wg.Done()\n            atomizer, err := atom.Use[User]()\n            if err != nil {\n                t.Errorf(\"Use failed: %v\", err)\n                return\n            }\n            _ = atomizer.Atomize(&User{Name: \"test\"})\n        }()\n    }\n\n    wg.Wait()\n}",{"id":789,"title":790,"titles":791,"content":792,"level":20},"/v1.0.2/guides/testing#benchmarking","Benchmarking",[716],"func BenchmarkAtomize(b *testing.B) {\n    atomizer, _ := atom.Use[User]()\n    user := &User{Name: \"Alice\", Age: 30, Active: true}\n\n    b.ResetTimer()\n    for i := 0; i \u003C b.N; i++ {\n        _ = atomizer.Atomize(user)\n    }\n}\n\nfunc BenchmarkDeatomize(b *testing.B) {\n    atomizer, _ := atom.Use[User]()\n    a := atomizer.Atomize(&User{Name: \"Alice\", Age: 30, Active: true})\n\n    b.ResetTimer()\n    for i := 0; i \u003C b.N; i++ {\n        _, _ = atomizer.Deatomize(a)\n    }\n}\n\nfunc BenchmarkRoundTrip(b *testing.B) {\n    atomizer, _ := atom.Use[User]()\n    user := &User{Name: \"Alice\", Age: 30, Active: true}\n\n    b.ResetTimer()\n    for i := 0; i \u003C b.N; i++ {\n        a := atomizer.Atomize(user)\n        _, _ = atomizer.Deatomize(a)\n    }\n}",{"id":794,"title":795,"titles":796,"content":797,"level":20},"/v1.0.2/guides/testing#test-helpers","Test Helpers",[716],"See Testing Reference for the testing package utilities: AtomBuilder - Fluent atom constructionAtomMatcher - Deep comparisonRoundTripValidator - Automated round-trip testing",{"id":799,"title":406,"titles":800,"content":35,"level":20},"/v1.0.2/guides/testing#best-practices",[716],{"id":802,"title":803,"titles":804,"content":805,"level":41},"/v1.0.2/guides/testing#test-at-registration","Test at Registration",[716,406],"func TestMain(m *testing.M) {\n    // Verify all types register successfully\n    types := []func() error{\n        func() error { _, err := atom.Use[User](); return err },\n        func() error { _, err := atom.Use[Order](); return err },\n    }\n\n    for _, register := range types {\n        if err := register(); err != nil {\n            log.Fatalf(\"registration failed: %v\", err)\n        }\n    }\n\n    os.Exit(m.Run())\n}",{"id":807,"title":808,"titles":809,"content":810,"level":41},"/v1.0.2/guides/testing#test-edge-cases","Test Edge Cases",[716,406],"Empty structsNil pointersEmpty slices vs nil slicesMaximum/minimum valuesUnicode stringsLarge byte slices",{"id":812,"title":64,"titles":813,"content":814,"level":20},"/v1.0.2/guides/testing#next-steps",[716],"Testing Reference - Testing package APIAPI Reference - Complete API documentation html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}",{"id":816,"title":689,"titles":817,"content":818,"level":9},"/v1.0.2/cookbook/codegen",[],"Eliminating reflection overhead with generated implementations",{"id":820,"title":689,"titles":821,"content":822,"level":9},"/v1.0.2/cookbook/codegen#code-generation",[],"Eliminating reflection overhead with generated code.",{"id":824,"title":6,"titles":825,"content":826,"level":20},"/v1.0.2/cookbook/codegen#overview",[689],"While atom's reflection-based approach is convenient, generated implementations of Atomizable and Deatomizable provide: Zero reflection overhead at runtimeCompile-time type checking for field mappingsFaster serialization (typically 5-10x)",{"id":828,"title":829,"titles":830,"content":831,"level":20},"/v1.0.2/cookbook/codegen#manual-implementation","Manual Implementation",[689],"Before automating, understand the pattern: type User struct {\n    Name   string\n    Age    int64\n    Active bool\n    Score  float64\n}\n\n// Generated or hand-written\nfunc (u *User) Atomize(a *atom.Atom) {\n    a.Strings[\"Name\"] = u.Name\n    a.Ints[\"Age\"] = u.Age\n    a.Bools[\"Active\"] = u.Active\n    a.Floats[\"Score\"] = u.Score\n}\n\nfunc (u *User) Deatomize(a *atom.Atom) error {\n    u.Name = a.Strings[\"Name\"]\n    u.Age = a.Ints[\"Age\"]\n    u.Active = a.Bools[\"Active\"]\n    u.Score = a.Floats[\"Score\"]\n    return nil\n}",{"id":833,"title":834,"titles":835,"content":836,"level":20},"/v1.0.2/cookbook/codegen#generator-template","Generator Template",[689],"A simple Go template for generating implementations: // generator.go\npackage main\n\nimport (\n    \"os\"\n    \"text/template\"\n)\n\nvar tmpl = template.Must(template.New(\"atomizer\").Parse(`\n// Code generated by atomgen. DO NOT EDIT.\npackage {{.Package}}\n\nimport \"github.com/zoobz-io/atom\"\n\n{{range .Types}}\nfunc (x *{{.Name}}) Atomize(a *atom.Atom) {\n{{- range .Fields}}\n{{- if eq .Table \"strings\"}}\n    a.Strings[\"{{.Name}}\"] = x.{{.Name}}\n{{- else if eq .Table \"ints\"}}\n    a.Ints[\"{{.Name}}\"] = x.{{.Name}}\n{{- else if eq .Table \"floats\"}}\n    a.Floats[\"{{.Name}}\"] = x.{{.Name}}\n{{- else if eq .Table \"bools\"}}\n    a.Bools[\"{{.Name}}\"] = x.{{.Name}}\n{{- end}}\n{{- end}}\n}\n\nfunc (x *{{.Name}}) Deatomize(a *atom.Atom) error {\n{{- range .Fields}}\n{{- if eq .Table \"strings\"}}\n    x.{{.Name}} = a.Strings[\"{{.Name}}\"]\n{{- else if eq .Table \"ints\"}}\n    x.{{.Name}} = a.Ints[\"{{.Name}}\"]\n{{- else if eq .Table \"floats\"}}\n    x.{{.Name}} = a.Floats[\"{{.Name}}\"]\n{{- else if eq .Table \"bools\"}}\n    x.{{.Name}} = a.Bools[\"{{.Name}}\"]\n{{- end}}\n{{- end}}\n    return nil\n}\n{{end}}\n`))",{"id":838,"title":839,"titles":840,"content":841,"level":20},"/v1.0.2/cookbook/codegen#using-gogenerate","Using go:generate",[689],"Add a directive to your types file: //go:generate atomgen -type=User,Order,Product\n\ntype User struct {\n    Name   string\n    Age    int64\n    Active bool\n} Run generation: go generate ./...",{"id":843,"title":844,"titles":845,"content":35,"level":20},"/v1.0.2/cookbook/codegen#handling-complex-types","Handling Complex Types",[689],{"id":847,"title":848,"titles":849,"content":850,"level":41},"/v1.0.2/cookbook/codegen#pointers","Pointers",[689,844],"func (u *User) Atomize(a *atom.Atom) {\n    // Pointer field\n    a.StringPtrs[\"Nickname\"] = u.Nickname // *string\n}\n\nfunc (u *User) Deatomize(a *atom.Atom) error {\n    u.Nickname = a.StringPtrs[\"Nickname\"]\n    return nil\n}",{"id":852,"title":853,"titles":854,"content":855,"level":41},"/v1.0.2/cookbook/codegen#slices","Slices",[689,844],"func (u *User) Atomize(a *atom.Atom) {\n    a.StringSlices[\"Tags\"] = u.Tags\n    a.IntSlices[\"Scores\"] = u.Scores\n}\n\nfunc (u *User) Deatomize(a *atom.Atom) error {\n    u.Tags = a.StringSlices[\"Tags\"]\n    u.Scores = a.IntSlices[\"Scores\"]\n    return nil\n}",{"id":857,"title":526,"titles":858,"content":859,"level":41},"/v1.0.2/cookbook/codegen#nested-structs",[689,844],"func (u *User) Atomize(a *atom.Atom) {\n    a.Strings[\"Name\"] = u.Name\n\n    // Nested struct\n    addressAtom := atom.Atom{\n        Strings: make(map[string]string),\n    }\n    u.Address.Atomize(&addressAtom)\n    a.Nested[\"Address\"] = addressAtom\n}\n\nfunc (u *User) Deatomize(a *atom.Atom) error {\n    u.Name = a.Strings[\"Name\"]\n\n    if addrAtom, ok := a.Nested[\"Address\"]; ok {\n        if err := u.Address.Deatomize(&addrAtom); err != nil {\n            return err\n        }\n    }\n    return nil\n}",{"id":861,"title":555,"titles":862,"content":863,"level":41},"/v1.0.2/cookbook/codegen#slice-of-structs",[689,844],"func (o *Order) Atomize(a *atom.Atom) {\n    a.Ints[\"ID\"] = o.ID\n\n    items := make([]atom.Atom, len(o.Items))\n    for i := range o.Items {\n        itemAtom := atom.Atom{\n            Ints:   make(map[string]int64),\n            Floats: make(map[string]float64),\n        }\n        o.Items[i].Atomize(&itemAtom)\n        items[i] = itemAtom\n    }\n    a.NestedSlices[\"Items\"] = items\n}\n\nfunc (o *Order) Deatomize(a *atom.Atom) error {\n    o.ID = a.Ints[\"ID\"]\n\n    if itemAtoms, ok := a.NestedSlices[\"Items\"]; ok {\n        o.Items = make([]OrderItem, len(itemAtoms))\n        for i := range itemAtoms {\n            if err := o.Items[i].Deatomize(&itemAtoms[i]); err != nil {\n                return err\n            }\n        }\n    }\n    return nil\n}",{"id":865,"title":866,"titles":867,"content":868,"level":20},"/v1.0.2/cookbook/codegen#width-conversion","Width Conversion",[689],"For narrow integer types, add conversion: type Narrow struct {\n    Small int8\n    Med   int16\n}\n\nfunc (n *Narrow) Atomize(a *atom.Atom) {\n    a.Ints[\"Small\"] = int64(n.Small)\n    a.Ints[\"Med\"] = int64(n.Med)\n}\n\nfunc (n *Narrow) Deatomize(a *atom.Atom) error {\n    // Add overflow checking\n    small := a.Ints[\"Small\"]\n    if small \u003C math.MinInt8 || small > math.MaxInt8 {\n        return fmt.Errorf(\"Small overflow: %d\", small)\n    }\n    n.Small = int8(small)\n\n    med := a.Ints[\"Med\"]\n    if med \u003C math.MinInt16 || med > math.MaxInt16 {\n        return fmt.Errorf(\"Med overflow: %d\", med)\n    }\n    n.Med = int16(med)\n\n    return nil\n}",{"id":870,"title":181,"titles":871,"content":872,"level":20},"/v1.0.2/cookbook/codegen#named-types",[689],"Handle named types by using the underlying operations: type UserID string\ntype Status int\n\ntype User struct {\n    ID     UserID\n    Status Status\n}\n\nfunc (u *User) Atomize(a *atom.Atom) {\n    a.Strings[\"ID\"] = string(u.ID)\n    a.Ints[\"Status\"] = int64(u.Status)\n}\n\nfunc (u *User) Deatomize(a *atom.Atom) error {\n    u.ID = UserID(a.Strings[\"ID\"])\n    u.Status = Status(a.Ints[\"Status\"])\n    return nil\n}",{"id":874,"title":875,"titles":876,"content":877,"level":20},"/v1.0.2/cookbook/codegen#performance-comparison","Performance Comparison",[689],"Typical benchmarks (your results may vary): BenchmarkAtomize_Reflection-8     500000    2400 ns/op    1024 B/op    12 allocs/op\nBenchmarkAtomize_Generated-8     5000000     240 ns/op     512 B/op     4 allocs/op\n\nBenchmarkDeatomize_Reflection-8   300000    4100 ns/op    1536 B/op    18 allocs/op\nBenchmarkDeatomize_Generated-8   3000000     450 ns/op     256 B/op     2 allocs/op",{"id":879,"title":406,"titles":880,"content":35,"level":20},"/v1.0.2/cookbook/codegen#best-practices",[689],{"id":882,"title":883,"titles":884,"content":885,"level":41},"/v1.0.2/cookbook/codegen#regenerate-on-change","Regenerate on Change",[689,406],"generate:\n    go generate ./...\n\ntest: generate\n    go test ./...",{"id":887,"title":888,"titles":889,"content":890,"level":41},"/v1.0.2/cookbook/codegen#keep-generated-files","Keep Generated Files",[689,406],"Commit generated files to version control: models/\n├── user.go           # Source\n├── user_atom.go      # Generated\n├── order.go          # Source\n└── order_atom.go     # Generated",{"id":892,"title":893,"titles":894,"content":895,"level":41},"/v1.0.2/cookbook/codegen#validate-generation","Validate Generation",[689,406],"Test that generated code produces correct round-trips: func TestGeneratedRoundTrip(t *testing.T) {\n    atomizer, _ := atom.Use[User]()\n\n    original := &User{Name: \"Alice\", Age: 30, Active: true}\n\n    // Atomize (uses generated Atomize method)\n    a := atomizer.Atomize(original)\n\n    // Verify expected fields\n    if a.Strings[\"Name\"] != \"Alice\" {\n        t.Errorf(\"Name: got %q, want %q\", a.Strings[\"Name\"], \"Alice\")\n    }\n    if a.Ints[\"Age\"] != 30 {\n        t.Errorf(\"Age: got %d, want %d\", a.Ints[\"Age\"], 30)\n    }\n\n    // Deatomize (uses generated Deatomize method)\n    restored, err := atomizer.Deatomize(a)\n    if err != nil {\n        t.Fatal(err)\n    }\n\n    // Verify round-trip\n    if !reflect.DeepEqual(original, restored) {\n        t.Errorf(\"round-trip failed:\\noriginal: %+v\\nrestored: %+v\", original, restored)\n    }\n}",{"id":897,"title":64,"titles":898,"content":899,"level":20},"/v1.0.2/cookbook/codegen#next-steps",[689],"Serialization Cookbook - Encoding atomsAPI Reference - Complete API html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}",{"id":901,"title":902,"titles":903,"content":904,"level":9},"/v1.0.2/cookbook/serialization","Serialization",[],"Encoding atoms for storage and transmission",{"id":906,"title":902,"titles":907,"content":908,"level":9},"/v1.0.2/cookbook/serialization#serialization",[],"Encoding atoms for storage and transmission.",{"id":910,"title":6,"titles":911,"content":912,"level":20},"/v1.0.2/cookbook/serialization#overview",[902],"Atoms are designed for type-segregated storage. This cookbook shows patterns for encoding atoms to various formats.",{"id":914,"title":915,"titles":916,"content":35,"level":20},"/v1.0.2/cookbook/serialization#json-encoding","JSON Encoding",[902],{"id":918,"title":919,"titles":920,"content":921,"level":41},"/v1.0.2/cookbook/serialization#direct-marshaling","Direct Marshaling",[902,915],"Atoms can be marshaled directly to JSON: import \"encoding/json\"\n\natomizer, _ := atom.Use[User]()\nuser := &User{Name: \"Alice\", Age: 30}\na := atomizer.Atomize(user)\n\ndata, err := json.Marshal(a)\nif err != nil {\n    log.Fatal(err)\n}\n// {\"Strings\":{\"Name\":\"Alice\"},\"Ints\":{\"Age\":30},...}",{"id":923,"title":924,"titles":925,"content":926,"level":41},"/v1.0.2/cookbook/serialization#unmarshaling","Unmarshaling",[902,915],"var restored atom.Atom\nif err := json.Unmarshal(data, &restored); err != nil {\n    log.Fatal(err)\n}\n\nuser, err := atomizer.Deatomize(&restored)",{"id":928,"title":929,"titles":930,"content":931,"level":41},"/v1.0.2/cookbook/serialization#compact-json","Compact JSON",[902,915],"For smaller payloads, omit empty maps: type CompactAtom struct {\n    Spec         atom.Spec              `json:\"spec,omitempty\"`\n    Strings      map[string]string      `json:\"s,omitempty\"`\n    Ints         map[string]int64       `json:\"i,omitempty\"`\n    Floats       map[string]float64     `json:\"f,omitempty\"`\n    Bools        map[string]bool        `json:\"b,omitempty\"`\n    // ... other fields\n}\n\nfunc ToCompact(a *atom.Atom) CompactAtom {\n    return CompactAtom{\n        Spec:    a.Spec,\n        Strings: a.Strings,\n        Ints:    a.Ints,\n        Floats:  a.Floats,\n        Bools:   a.Bools,\n    }\n}",{"id":933,"title":934,"titles":935,"content":936,"level":20},"/v1.0.2/cookbook/serialization#messagepack","MessagePack",[902],"For binary efficiency: import \"github.com/vmihailenco/msgpack/v5\"\n\n// Encode\ndata, err := msgpack.Marshal(a)\n\n// Decode\nvar restored atom.Atom\nerr = msgpack.Unmarshal(data, &restored)",{"id":938,"title":939,"titles":940,"content":941,"level":20},"/v1.0.2/cookbook/serialization#protocol-buffers","Protocol Buffers",[902],"Define a proto schema: message Atom {\n    map\u003Cstring, string> strings = 1;\n    map\u003Cstring, int64> ints = 2;\n    map\u003Cstring, double> floats = 3;\n    map\u003Cstring, bool> bools = 4;\n    map\u003Cstring, bytes> bytes = 5;\n    map\u003Cstring, Atom> nested = 6;\n    // ... etc\n} Convert between atom and proto: func ToProto(a *atom.Atom) *pb.Atom {\n    return &pb.Atom{\n        Strings: a.Strings,\n        Ints:    a.Ints,\n        Floats:  a.Floats,\n        Bools:   a.Bools,\n        // ...\n    }\n}\n\nfunc FromProto(p *pb.Atom) *atom.Atom {\n    return &atom.Atom{\n        Strings: p.Strings,\n        Ints:    p.Ints,\n        Floats:  p.Floats,\n        Bools:   p.Bools,\n        // ...\n    }\n}",{"id":943,"title":944,"titles":945,"content":35,"level":20},"/v1.0.2/cookbook/serialization#key-value-storage","Key-Value Storage",[902],{"id":947,"title":948,"titles":949,"content":950,"level":41},"/v1.0.2/cookbook/serialization#redis-hashes","Redis Hashes",[902,944],"Store each table as a hash: import \"github.com/redis/go-redis/v9\"\n\nfunc StoreAtom(ctx context.Context, rdb *redis.Client, key string, a *atom.Atom) error {\n    pipe := rdb.Pipeline()\n\n    // Store strings\n    if len(a.Strings) > 0 {\n        args := make([]any, 0, len(a.Strings)*2)\n        for k, v := range a.Strings {\n            args = append(args, k, v)\n        }\n        pipe.HSet(ctx, key+\":strings\", args...)\n    }\n\n    // Store ints\n    if len(a.Ints) > 0 {\n        args := make([]any, 0, len(a.Ints)*2)\n        for k, v := range a.Ints {\n            args = append(args, k, v)\n        }\n        pipe.HSet(ctx, key+\":ints\", args...)\n    }\n\n    // ... other tables\n\n    _, err := pipe.Exec(ctx)\n    return err\n}",{"id":952,"title":953,"titles":954,"content":955,"level":41},"/v1.0.2/cookbook/serialization#partial-reads","Partial Reads",[902,944],"Read only specific fields: func LoadStringField(ctx context.Context, rdb *redis.Client, key, field string) (string, error) {\n    return rdb.HGet(ctx, key+\":strings\", field).Result()\n}\n\nfunc LoadIntField(ctx context.Context, rdb *redis.Client, key, field string) (int64, error) {\n    return rdb.HGet(ctx, key+\":ints\", field).Int64()\n}",{"id":957,"title":958,"titles":959,"content":35,"level":20},"/v1.0.2/cookbook/serialization#column-oriented-storage","Column-Oriented Storage",[902],{"id":961,"title":962,"titles":963,"content":964,"level":41},"/v1.0.2/cookbook/serialization#per-type-tables","Per-Type Tables",[902,958],"Store each atom table in a separate database table: CREATE TABLE atom_strings (\n    entity_id TEXT,\n    field_name TEXT,\n    value TEXT,\n    PRIMARY KEY (entity_id, field_name)\n);\n\nCREATE TABLE atom_ints (\n    entity_id TEXT,\n    field_name TEXT,\n    value BIGINT,\n    PRIMARY KEY (entity_id, field_name)\n); func StoreAtom(db *sql.DB, entityID string, a *atom.Atom) error {\n    tx, _ := db.Begin()\n\n    for field, value := range a.Strings {\n        tx.Exec(`INSERT INTO atom_strings VALUES (?, ?, ?)\n                 ON CONFLICT DO UPDATE SET value = ?`,\n            entityID, field, value, value)\n    }\n\n    for field, value := range a.Ints {\n        tx.Exec(`INSERT INTO atom_ints VALUES (?, ?, ?)\n                 ON CONFLICT DO UPDATE SET value = ?`,\n            entityID, field, value, value)\n    }\n\n    return tx.Commit()\n}",{"id":966,"title":967,"titles":968,"content":969,"level":41},"/v1.0.2/cookbook/serialization#querying-by-field","Querying by Field",[902,958],"-- Find users over 30\nSELECT entity_id FROM atom_ints\nWHERE field_name = 'Age' AND value > 30;\n\n-- Find users by name\nSELECT entity_id FROM atom_strings\nWHERE field_name = 'Name' AND value = 'Alice';",{"id":971,"title":972,"titles":973,"content":974,"level":20},"/v1.0.2/cookbook/serialization#binary-format","Binary Format",[902],"Custom binary encoding for maximum efficiency: import \"encoding/binary\"\n\nfunc EncodeAtom(a *atom.Atom) []byte {\n    buf := new(bytes.Buffer)\n\n    // Write string count and entries\n    binary.Write(buf, binary.LittleEndian, uint32(len(a.Strings)))\n    for k, v := range a.Strings {\n        writeString(buf, k)\n        writeString(buf, v)\n    }\n\n    // Write int count and entries\n    binary.Write(buf, binary.LittleEndian, uint32(len(a.Ints)))\n    for k, v := range a.Ints {\n        writeString(buf, k)\n        binary.Write(buf, binary.LittleEndian, v)\n    }\n\n    // ... other tables\n\n    return buf.Bytes()\n}\n\nfunc writeString(buf *bytes.Buffer, s string) {\n    binary.Write(buf, binary.LittleEndian, uint32(len(s)))\n    buf.WriteString(s)\n}",{"id":976,"title":977,"titles":978,"content":979,"level":20},"/v1.0.2/cookbook/serialization#streaming","Streaming",[902],"For large datasets, stream atoms: type AtomStream struct {\n    encoder *json.Encoder\n}\n\nfunc (s *AtomStream) Write(a *atom.Atom) error {\n    return s.encoder.Encode(a)\n}\n\n// Usage\nfile, _ := os.Create(\"atoms.jsonl\")\nstream := &AtomStream{encoder: json.NewEncoder(file)}\n\nfor _, user := range users {\n    a := atomizer.Atomize(user)\n    stream.Write(a)\n}",{"id":981,"title":982,"titles":983,"content":984,"level":20},"/v1.0.2/cookbook/serialization#compression","Compression",[902],"Combine with compression: import \"compress/gzip\"\n\nfunc CompressAtom(a *atom.Atom) ([]byte, error) {\n    var buf bytes.Buffer\n    gz := gzip.NewWriter(&buf)\n\n    if err := json.NewEncoder(gz).Encode(a); err != nil {\n        return nil, err\n    }\n    gz.Close()\n\n    return buf.Bytes(), nil\n}",{"id":986,"title":406,"titles":987,"content":35,"level":20},"/v1.0.2/cookbook/serialization#best-practices",[902],{"id":989,"title":990,"titles":991,"content":992,"level":41},"/v1.0.2/cookbook/serialization#version-your-format","Version Your Format",[902,406],"type VersionedAtom struct {\n    Version int        `json:\"v\"`\n    Atom    atom.Atom  `json:\"a\"`\n}\n\nfunc Encode(a *atom.Atom) []byte {\n    va := VersionedAtom{Version: 1, Atom: *a}\n    data, _ := json.Marshal(va)\n    return data\n}",{"id":994,"title":995,"titles":996,"content":997,"level":41},"/v1.0.2/cookbook/serialization#handle-missing-fields","Handle Missing Fields",[902,406],"func LoadAtom(data []byte) (*atom.Atom, error) {\n    var a atom.Atom\n    if err := json.Unmarshal(data, &a); err != nil {\n        return nil, err\n    }\n\n    // Initialize nil maps\n    if a.Strings == nil {\n        a.Strings = make(map[string]string)\n    }\n    // ...\n\n    return &a, nil\n}",{"id":999,"title":64,"titles":1000,"content":1001,"level":20},"/v1.0.2/cookbook/serialization#next-steps",[902],"Migrations Cookbook - Schema evolutionAPI Reference - Complete API html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}",{"id":1003,"title":1004,"titles":1005,"content":1006,"level":9},"/v1.0.2/cookbook/migrations","Schema Migrations",[],"Handling evolving data structures and backwards compatibility",{"id":1008,"title":1004,"titles":1009,"content":1010,"level":9},"/v1.0.2/cookbook/migrations#schema-migrations",[],"Handling evolving data structures.",{"id":1012,"title":6,"titles":1013,"content":1014,"level":20},"/v1.0.2/cookbook/migrations#overview",[1004],"When struct definitions change, atoms serialized with old schemas need migration. This cookbook covers common migration patterns.",{"id":1016,"title":1017,"titles":1018,"content":35,"level":20},"/v1.0.2/cookbook/migrations#field-renames","Field Renames",[1004],{"id":1020,"title":1021,"titles":1022,"content":1023,"level":41},"/v1.0.2/cookbook/migrations#old-schema","Old Schema",[1004,1017],"type User struct {\n    EmailAddress string // v1\n}",{"id":1025,"title":1026,"titles":1027,"content":1028,"level":41},"/v1.0.2/cookbook/migrations#new-schema","New Schema",[1004,1017],"type User struct {\n    Email string // v2: renamed from EmailAddress\n}",{"id":1030,"title":1031,"titles":1032,"content":1033,"level":41},"/v1.0.2/cookbook/migrations#migration","Migration",[1004,1017],"Using Deatomizable: func (u *User) Deatomize(a *atom.Atom) error {\n    // Try new name first\n    if email, ok := a.Strings[\"Email\"]; ok {\n        u.Email = email\n    } else if email, ok := a.Strings[\"EmailAddress\"]; ok {\n        // Fall back to old name\n        u.Email = email\n    }\n    return nil\n}\n\nfunc (u *User) Atomize(a *atom.Atom) {\n    // Always write new name\n    a.Strings[\"Email\"] = u.Email\n}",{"id":1035,"title":1036,"titles":1037,"content":35,"level":20},"/v1.0.2/cookbook/migrations#field-type-changes","Field Type Changes",[1004],{"id":1039,"title":1040,"titles":1041,"content":1042,"level":41},"/v1.0.2/cookbook/migrations#old-string-id","Old: String ID",[1004,1036],"type User struct {\n    ID string // v1: string ID\n}",{"id":1044,"title":1045,"titles":1046,"content":1047,"level":41},"/v1.0.2/cookbook/migrations#new-integer-id","New: Integer ID",[1004,1036],"type User struct {\n    ID int64 // v2: numeric ID\n}",{"id":1049,"title":1031,"titles":1050,"content":1051,"level":41},"/v1.0.2/cookbook/migrations#migration-1",[1004,1036],"func (u *User) Deatomize(a *atom.Atom) error {\n    // Try new type first\n    if id, ok := a.Ints[\"ID\"]; ok {\n        u.ID = id\n    } else if idStr, ok := a.Strings[\"ID\"]; ok {\n        // Convert old string format\n        id, err := strconv.ParseInt(idStr, 10, 64)\n        if err != nil {\n            return fmt.Errorf(\"invalid ID format: %s\", idStr)\n        }\n        u.ID = id\n    }\n    return nil\n}",{"id":1053,"title":1054,"titles":1055,"content":35,"level":20},"/v1.0.2/cookbook/migrations#new-required-fields","New Required Fields",[1004],{"id":1057,"title":1058,"titles":1059,"content":1060,"level":41},"/v1.0.2/cookbook/migrations#adding-with-default","Adding with Default",[1004,1054],"type User struct {\n    Name   string\n    Active bool   // v2: new field\n}\n\nfunc (u *User) Deatomize(a *atom.Atom) error {\n    u.Name = a.Strings[\"Name\"]\n\n    if active, ok := a.Bools[\"Active\"]; ok {\n        u.Active = active\n    } else {\n        // Default for old data\n        u.Active = true\n    }\n    return nil\n}",{"id":1062,"title":1063,"titles":1064,"content":35,"level":20},"/v1.0.2/cookbook/migrations#removed-fields","Removed Fields",[1004],{"id":1066,"title":1067,"titles":1068,"content":1069,"level":41},"/v1.0.2/cookbook/migrations#graceful-ignore","Graceful Ignore",[1004,1063],"Old atoms may have fields that no longer exist: // Old schema had \"MiddleName\" field\n// New schema removed it\n// Deatomize simply doesn't read it\n\nfunc (u *User) Deatomize(a *atom.Atom) error {\n    u.FirstName = a.Strings[\"FirstName\"]\n    u.LastName = a.Strings[\"LastName\"]\n    // MiddleName is ignored if present\n    return nil\n}",{"id":1071,"title":1072,"titles":1073,"content":35,"level":20},"/v1.0.2/cookbook/migrations#nested-structure-changes","Nested Structure Changes",[1004],{"id":1075,"title":1076,"titles":1077,"content":1078,"level":41},"/v1.0.2/cookbook/migrations#flattening","Flattening",[1004,1072],"// Old: nested\ntype UserV1 struct {\n    Profile struct {\n        Bio string\n    }\n}\n\n// New: flattened\ntype UserV2 struct {\n    Bio string\n}\n\nfunc (u *UserV2) Deatomize(a *atom.Atom) error {\n    // Try flat first\n    if bio, ok := a.Strings[\"Bio\"]; ok {\n        u.Bio = bio\n    } else if profile, ok := a.Nested[\"Profile\"]; ok {\n        // Fall back to nested\n        u.Bio = profile.Strings[\"Bio\"]\n    }\n    return nil\n}",{"id":1080,"title":1081,"titles":1082,"content":1083,"level":41},"/v1.0.2/cookbook/migrations#nesting","Nesting",[1004,1072],"// Old: flat\ntype UserV1 struct {\n    Street string\n    City   string\n}\n\n// New: nested\ntype UserV2 struct {\n    Address Address\n}\n\nfunc (u *UserV2) Deatomize(a *atom.Atom) error {\n    if addr, ok := a.Nested[\"Address\"]; ok {\n        // New format\n        u.Address.Street = addr.Strings[\"Street\"]\n        u.Address.City = addr.Strings[\"City\"]\n    } else {\n        // Old flat format\n        u.Address.Street = a.Strings[\"Street\"]\n        u.Address.City = a.Strings[\"City\"]\n    }\n    return nil\n}",{"id":1085,"title":1086,"titles":1087,"content":35,"level":20},"/v1.0.2/cookbook/migrations#versioned-atoms","Versioned Atoms",[1004],{"id":1089,"title":1090,"titles":1091,"content":1092,"level":41},"/v1.0.2/cookbook/migrations#explicit-version-field","Explicit Version Field",[1004,1086],"type VersionedAtom struct {\n    Version int\n    Data    atom.Atom\n}\n\nfunc Migrate(va VersionedAtom) (*atom.Atom, error) {\n    a := &va.Data\n\n    switch va.Version {\n    case 1:\n        migrateV1ToV2(a)\n        fallthrough\n    case 2:\n        migrateV2ToV3(a)\n        fallthrough\n    case 3:\n        // Current version\n    default:\n        return nil, fmt.Errorf(\"unknown version: %d\", va.Version)\n    }\n\n    return a, nil\n}\n\nfunc migrateV1ToV2(a *atom.Atom) {\n    // Rename EmailAddress → Email\n    if email, ok := a.Strings[\"EmailAddress\"]; ok {\n        a.Strings[\"Email\"] = email\n        delete(a.Strings, \"EmailAddress\")\n    }\n}",{"id":1094,"title":1095,"titles":1096,"content":1097,"level":41},"/v1.0.2/cookbook/migrations#version-in-spec","Version in Spec",[1004,1086],"Use the Spec field for version tracking: type User struct {\n    Name string\n}\n\natomizer, _ := atom.Use[User]()\na := atomizer.Atomize(&User{Name: \"Alice\"})\n\n// Spec contains type info\n// Can be extended with version metadata",{"id":1099,"title":1100,"titles":1101,"content":35,"level":20},"/v1.0.2/cookbook/migrations#batch-migration","Batch Migration",[1004],{"id":1103,"title":1104,"titles":1105,"content":1106,"level":41},"/v1.0.2/cookbook/migrations#migrate-stored-data","Migrate Stored Data",[1004,1100],"func MigrateDatabase(db *sql.DB) error {\n    rows, err := db.Query(\"SELECT id, data FROM atoms\")\n    if err != nil {\n        return err\n    }\n    defer rows.Close()\n\n    for rows.Next() {\n        var id string\n        var data []byte\n        rows.Scan(&id, &data)\n\n        var a atom.Atom\n        json.Unmarshal(data, &a)\n\n        // Apply migrations\n        migrateV1ToV2(&a)\n\n        newData, _ := json.Marshal(a)\n        db.Exec(\"UPDATE atoms SET data = ? WHERE id = ?\", newData, id)\n    }\n\n    return nil\n}",{"id":1108,"title":1109,"titles":1110,"content":1111,"level":41},"/v1.0.2/cookbook/migrations#lazy-migration","Lazy Migration",[1004,1100],"Migrate on read: func LoadUser(id string) (*User, error) {\n    data := storage.Get(id)\n\n    var a atom.Atom\n    json.Unmarshal(data, &a)\n\n    // Check if migration needed\n    if _, ok := a.Strings[\"EmailAddress\"]; ok {\n        migrateV1ToV2(&a)\n\n        // Write back migrated version\n        newData, _ := json.Marshal(a)\n        storage.Set(id, newData)\n    }\n\n    return atomizer.Deatomize(&a)\n}",{"id":1113,"title":1114,"titles":1115,"content":1116,"level":20},"/v1.0.2/cookbook/migrations#testing-migrations","Testing Migrations",[1004],"func TestMigrationV1ToV2(t *testing.T) {\n    // V1 format\n    v1 := &atom.Atom{\n        Strings: map[string]string{\n            \"EmailAddress\": \"alice@example.com\",\n        },\n    }\n\n    // Migrate\n    migrateV1ToV2(v1)\n\n    // Verify\n    if v1.Strings[\"Email\"] != \"alice@example.com\" {\n        t.Error(\"Email not migrated\")\n    }\n    if _, ok := v1.Strings[\"EmailAddress\"]; ok {\n        t.Error(\"Old field not removed\")\n    }\n}\n\nfunc TestBackwardsCompatibility(t *testing.T) {\n    atomizer, _ := atom.Use[User]()\n\n    // Load old format\n    oldData := `{\"Strings\":{\"EmailAddress\":\"alice@example.com\"}}`\n    var a atom.Atom\n    json.Unmarshal([]byte(oldData), &a)\n\n    // Deatomize should handle old format\n    user, err := atomizer.Deatomize(&a)\n    if err != nil {\n        t.Fatal(err)\n    }\n    if user.Email != \"alice@example.com\" {\n        t.Errorf(\"expected alice@example.com, got %s\", user.Email)\n    }\n}",{"id":1118,"title":406,"titles":1119,"content":35,"level":20},"/v1.0.2/cookbook/migrations#best-practices",[1004],{"id":1121,"title":1122,"titles":1123,"content":1124,"level":41},"/v1.0.2/cookbook/migrations#backwards-compatibility","Backwards Compatibility",[1004,406],"Always support reading old formats: func (u *User) Deatomize(a *atom.Atom) error {\n    // Support both old and new field names\n    // Support both old and new types\n    // Default missing fields appropriately\n}",{"id":1126,"title":1127,"titles":1128,"content":1129,"level":41},"/v1.0.2/cookbook/migrations#forward-compatibility","Forward Compatibility",[1004,406],"Ignore unknown fields when reading: // Future versions may add fields\n// Current code should not fail on unknown fields",{"id":1131,"title":1132,"titles":1133,"content":1134,"level":41},"/v1.0.2/cookbook/migrations#document-versions","Document Versions",[1004,406],"// Version history:\n// v1 (2024-01): Initial schema\n// v2 (2024-03): Renamed EmailAddress → Email\n// v3 (2024-06): Added Active field (default: true)",{"id":1136,"title":64,"titles":1137,"content":1138,"level":20},"/v1.0.2/cookbook/migrations#next-steps",[1004],"API Reference - Complete APITables Reference - All table types html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}",{"id":1140,"title":1141,"titles":1142,"content":1143,"level":9},"/v1.0.2/reference/api","API Reference",[],"Complete API documentation for the atom package",{"id":1145,"title":1141,"titles":1146,"content":1147,"level":9},"/v1.0.2/reference/api#api-reference",[],"Complete API documentation for the atom package.",{"id":1149,"title":1150,"titles":1151,"content":1152,"level":20},"/v1.0.2/reference/api#package","Package",[1141],"import \"github.com/zoobz-io/atom\"",{"id":1154,"title":1155,"titles":1156,"content":35,"level":20},"/v1.0.2/reference/api#functions","Functions",[1141],{"id":1158,"title":1159,"titles":1160,"content":1161,"level":41},"/v1.0.2/reference/api#use","Use",[1141,1155],"func Use[T any]() (*Atomizer[T], error) Registers and returns an Atomizer for type T. First call builds the atomizer via reflectionSubsequent calls return the cached instanceReturns error if T contains unsupported field types Example: atomizer, err := atom.Use[User]()\nif err != nil {\n    log.Fatalf(\"registration failed: %v\", err)\n} 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",{"id":1163,"title":1164,"titles":1165,"content":1166,"level":41},"/v1.0.2/reference/api#alltables","AllTables",[1141,1155],"func AllTables() []Table Returns all table types in canonical order. Example: for _, t := range atom.AllTables() {\n    fmt.Println(t)\n}",{"id":1168,"title":1169,"titles":1170,"content":1171,"level":41},"/v1.0.2/reference/api#tableforfield","TableForField",[1141,1155],"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 parameterReturns \"\", false if the type was not registered via Use[T]()Returns \"\", false if the field does not exist Example: atomizer, _ := atom.Use[User]()\nspec := atomizer.Spec()\n\n// Later, without knowing T:\ntable, ok := atom.TableForField(spec, \"Age\")\nif ok {\n    fmt.Println(table) // \"ints\"\n}",{"id":1173,"title":1174,"titles":1175,"content":1176,"level":41},"/v1.0.2/reference/api#fieldsfor","FieldsFor",[1141,1155],"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]()\nfields, ok := atom.FieldsFor(atomizer.Spec())\nif ok {\n    for _, f := range fields {\n        fmt.Printf(\"%s -> %s\\n\", f.Name, f.Table)\n    }\n}",{"id":1178,"title":1179,"titles":1180,"content":35,"level":20},"/v1.0.2/reference/api#types","Types",[1141],{"id":1182,"title":1183,"titles":1184,"content":1185,"level":41},"/v1.0.2/reference/api#atomizert","AtomizerT",[1141,1179],"type Atomizer[T any] struct {\n    // contains filtered or unexported fields\n} Provides typed bidirectional conversion for type T.",{"id":1187,"title":1188,"titles":1189,"content":35,"level":1190},"/v1.0.2/reference/api#methods","Methods",[1141,1179,1183],4,{"id":1192,"title":252,"titles":1193,"content":1194,"level":1195},"/v1.0.2/reference/api#atomize",[1141,1179,1183,1188],"func (a *Atomizer[T]) Atomize(obj *T) *Atom Converts a struct to its atomic representation. If T implements Atomizable, that method is usedOtherwise, reflection is used Example: user := &User{Name: \"Alice\", Age: 30}\natom := atomizer.Atomize(user)",5,{"id":1197,"title":257,"titles":1198,"content":1199,"level":1195},"/v1.0.2/reference/api#deatomize",[1141,1179,1183,1188],"func (a *Atomizer[T]) Deatomize(atom *Atom) (*T, error) Reconstructs a struct from an Atom. If T implements Deatomizable, that method is usedOtherwise, reflection is usedReturns error on overflow or validation failure Example: user, err := atomizer.Deatomize(atom)\nif err != nil {\n    log.Printf(\"deatomize failed: %v\", err)\n}",{"id":1201,"title":1202,"titles":1203,"content":1204,"level":1195},"/v1.0.2/reference/api#newatom","NewAtom",[1141,1179,1183,1188],"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()\natom.Strings[\"Name\"] = \"Bob\"",{"id":1206,"title":1207,"titles":1208,"content":1209,"level":1195},"/v1.0.2/reference/api#spec","Spec",[1141,1179,1183,1188],"func (a *Atomizer[T]) Spec() Spec Returns the type specification for T. Example: spec := atomizer.Spec()\nfmt.Println(spec.TypeName)    // \"User\"\nfmt.Println(spec.PackageName) // \"github.com/example/app\"",{"id":1211,"title":161,"titles":1212,"content":1213,"level":1195},"/v1.0.2/reference/api#fields",[1141,1179,1183,1188],"func (a *Atomizer[T]) Fields() []Field Returns all fields with their table mappings. Example: for _, f := range atomizer.Fields() {\n    fmt.Printf(\"%s -> %s\\n\", f.Name, f.Table)\n}\n// ID -> ints\n// Name -> strings",{"id":1215,"title":1216,"titles":1217,"content":1218,"level":1195},"/v1.0.2/reference/api#fieldsin","FieldsIn",[1141,1179,1183,1188],"func (a *Atomizer[T]) FieldsIn(table Table) []string Returns field names stored in the given table. Example: stringFields := atomizer.FieldsIn(atom.TableStrings)\n// [\"Name\", \"Email\"]",{"id":1220,"title":1221,"titles":1222,"content":1223,"level":1195},"/v1.0.2/reference/api#tablefor","TableFor",[1141,1179,1183,1188],"func (a *Atomizer[T]) TableFor(field string) (Table, bool) Returns the table for a field name. Example: table, ok := atomizer.TableFor(\"Age\")\nif ok {\n    fmt.Println(table) // \"ints\"\n}",{"id":1225,"title":12,"titles":1226,"content":1227,"level":41},"/v1.0.2/reference/api#atom",[1141,1179],"type Atom struct {\n    // Scalars\n    Strings map[string]string\n    Ints    map[string]int64\n    Uints   map[string]uint64\n    Floats  map[string]float64\n    Bools   map[string]bool\n    Times   map[string]time.Time\n    Bytes   map[string][]byte\n\n    // Pointers (nullable)\n    StringPtrs map[string]*string\n    IntPtrs    map[string]*int64\n    UintPtrs   map[string]*uint64\n    FloatPtrs  map[string]*float64\n    BoolPtrs   map[string]*bool\n    TimePtrs   map[string]*time.Time\n    BytePtrs   map[string]*[]byte\n\n    // Slices\n    StringSlices map[string][]string\n    IntSlices    map[string][]int64\n    UintSlices   map[string][]uint64\n    FloatSlices  map[string][]float64\n    BoolSlices   map[string][]bool\n    TimeSlices   map[string][]time.Time\n    ByteSlices   map[string][][]byte\n\n    // Maps (string-keyed)\n    StringMaps map[string]map[string]string\n    IntMaps    map[string]map[string]int64\n    UintMaps   map[string]map[string]uint64\n    FloatMaps  map[string]map[string]float64\n    BoolMaps   map[string]map[string]bool\n    TimeMaps   map[string]map[string]time.Time\n    ByteMaps   map[string]map[string][]byte\n\n    // Nested\n    Nested       map[string]Atom\n    NestedSlices map[string][]Atom\n    NestedMaps   map[string]map[string]Atom\n\n    // Metadata\n    Spec Spec\n} Holds decomposed atomic values by type.",{"id":1229,"title":1188,"titles":1230,"content":35,"level":1190},"/v1.0.2/reference/api#methods-1",[1141,1179,12],{"id":1232,"title":1233,"titles":1234,"content":1235,"level":1195},"/v1.0.2/reference/api#clone","Clone",[1141,1179,12,1188],"func (a *Atom) Clone() *Atom Returns a deep copy of the Atom. All maps are copied (not shared)Pointer values are dereferenced and copiedNested atoms are recursively clonedNil entries in pointer maps are preservedReturns nil if called on nil Example: original := atomizer.Atomize(user)\ncopy := original.Clone()\n\n// Modify copy without affecting original\ncopy.Strings[\"Name\"] = \"Modified\"\nfmt.Println(original.Strings[\"Name\"]) // Unchanged",{"id":1237,"title":1238,"titles":1239,"content":1240,"level":41},"/v1.0.2/reference/api#table","Table",[1141,1179],"type Table string Identifies the segregated storage table for atomic values.",{"id":1242,"title":1243,"titles":1244,"content":1245,"level":1190},"/v1.0.2/reference/api#constants","Constants",[1141,1179,1238],"const (\n    TableStrings      Table = \"strings\"\n    TableInts         Table = \"ints\"\n    TableUints        Table = \"uints\"\n    TableFloats       Table = \"floats\"\n    TableBools        Table = \"bools\"\n    TableTimes        Table = \"times\"\n    TableBytes        Table = \"bytes\"\n    TableBytePtrs     Table = \"byte_ptrs\"\n    TableStringPtrs   Table = \"string_ptrs\"\n    TableIntPtrs      Table = \"int_ptrs\"\n    TableUintPtrs     Table = \"uint_ptrs\"\n    TableFloatPtrs    Table = \"float_ptrs\"\n    TableBoolPtrs     Table = \"bool_ptrs\"\n    TableTimePtrs     Table = \"time_ptrs\"\n    TableStringSlices Table = \"string_slices\"\n    TableIntSlices    Table = \"int_slices\"\n    TableUintSlices   Table = \"uint_slices\"\n    TableFloatSlices  Table = \"float_slices\"\n    TableBoolSlices   Table = \"bool_slices\"\n    TableTimeSlices   Table = \"time_slices\"\n    TableByteSlices   Table = \"byte_slices\"\n)",{"id":1247,"title":1188,"titles":1248,"content":35,"level":1190},"/v1.0.2/reference/api#methods-2",[1141,1179,1238],{"id":1250,"title":1251,"titles":1252,"content":1253,"level":1195},"/v1.0.2/reference/api#prefix","Prefix",[1141,1179,1238,1188],"func (t Table) Prefix() string Returns the storage key prefix for this table. Example: prefix := atom.TableStrings.Prefix()\n// \"strings:\"",{"id":1255,"title":1207,"titles":1256,"content":1257,"level":41},"/v1.0.2/reference/api#spec-1",[1141,1179],"type Spec = sentinel.Metadata Type metadata describing a struct type. Aliased from sentinel.Metadata: type Metadata struct {\n    TypeName      string             // e.g., \"User\"\n    PackageName   string             // e.g., \"github.com/example/app\"\n    Fields        []FieldMetadata    // All exported fields\n    Relationships []TypeRelationship // References to other types\n} Most commonly used fields: TypeName - The short type namePackageName - The full package path",{"id":1259,"title":1260,"titles":1261,"content":1262,"level":41},"/v1.0.2/reference/api#field","Field",[1141,1179],"type Field struct {\n    Name  string\n    Table Table\n} Maps a field name to its storage table.",{"id":1264,"title":617,"titles":1265,"content":35,"level":20},"/v1.0.2/reference/api#interfaces",[1141],{"id":1267,"title":1268,"titles":1269,"content":1270,"level":41},"/v1.0.2/reference/api#atomizable","Atomizable",[1141,617],"type Atomizable interface {\n    Atomize(*Atom)\n} 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) {\n    a.Strings[\"Name\"] = u.Name\n    a.Ints[\"Age\"] = u.Age\n}",{"id":1272,"title":1273,"titles":1274,"content":1275,"level":41},"/v1.0.2/reference/api#deatomizable","Deatomizable",[1141,617],"type Deatomizable interface {\n    Deatomize(*Atom) error\n} 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 {\n    u.Name = a.Strings[\"Name\"]\n    u.Age = a.Ints[\"Age\"]\n    return nil\n}",{"id":1277,"title":1278,"titles":1279,"content":1280,"level":20},"/v1.0.2/reference/api#errors","Errors",[1141],"Sentinel errors for programmatic error handling via errors.Is().",{"id":1282,"title":1283,"titles":1284,"content":1285,"level":41},"/v1.0.2/reference/api#erroverflow","ErrOverflow",[1141,1278],"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)\nif errors.Is(err, atom.ErrOverflow) {\n    // Handle overflow\n}",{"id":1287,"title":1288,"titles":1289,"content":1290,"level":41},"/v1.0.2/reference/api#errunsupportedtype","ErrUnsupportedType",[1141,1278],"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]()\nif errors.Is(err, atom.ErrUnsupportedType) {\n    // Handle unsupported type\n}",{"id":1292,"title":1293,"titles":1294,"content":1295,"level":41},"/v1.0.2/reference/api#errsizemismatch","ErrSizeMismatch",[1141,1278],"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)\nif errors.Is(err, atom.ErrSizeMismatch) {\n    // Handle size mismatch\n}",{"id":1297,"title":1298,"titles":1299,"content":1300,"level":20},"/v1.0.2/reference/api#type-mappings","Type Mappings",[1141],"Go TypeTableAtom FieldstringstringsStringsint, int8, int16, int32, int64intsIntsuint, uint8, uint16, uint32, uint64uintsUintsfloat32, float64floatsFloatsboolboolsBoolstime.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_slicesByteSlicesmap[string]stringstring_mapsStringMapsmap[string]int64int_mapsIntMapsmap[string]uint64uint_mapsUintMapsmap[string]float64float_mapsFloatMapsmap[string]boolbool_mapsBoolMapsmap[string]time.Timetime_mapsTimeMapsmap[string][]bytebyte_mapsByteMapsstruct-Nested*struct-Nested[]struct-NestedSlicesmap[string]structnested_mapsNestedMaps html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sfm-E, html code.shiki .sfm-E{--shiki-default:var(--shiki-variable)}",{"id":1302,"title":1303,"titles":1304,"content":1305,"level":9},"/v1.0.2/reference/tables","Tables Reference",[],"Complete reference for all storage table types",{"id":1307,"title":1303,"titles":1308,"content":1309,"level":9},"/v1.0.2/reference/tables#tables-reference",[],"Complete reference for all storage tables.",{"id":1311,"title":6,"titles":1312,"content":1313,"level":20},"/v1.0.2/reference/tables#overview",[1303],"Tables organize atomic values by type. Each table stores a specific category of values.",{"id":1315,"title":1316,"titles":1317,"content":35,"level":20},"/v1.0.2/reference/tables#scalar-tables","Scalar Tables",[1303],{"id":1319,"title":1320,"titles":1321,"content":1322,"level":41},"/v1.0.2/reference/tables#tablestrings","TableStrings",[1303,1316],"const TableStrings Table = \"strings\" Stores string values. Go TypesStorage TypestringstringNamed string types (type X string)string Atom Field: Strings map[string]string",{"id":1324,"title":1325,"titles":1326,"content":1327,"level":41},"/v1.0.2/reference/tables#tableints","TableInts",[1303,1316],"const TableInts Table = \"ints\" Stores signed integer values (normalized to int64). Go TypesStorage Typeint, int8, int16, int32, int64int64Named int types (type X int)int64 Atom Field: Ints map[string]int64 Overflow: Values are validated during deatomization. Overflow returns an error.",{"id":1329,"title":1330,"titles":1331,"content":1332,"level":41},"/v1.0.2/reference/tables#tableuints","TableUints",[1303,1316],"const TableUints Table = \"uints\" Stores unsigned integer values (normalized to uint64). Go TypesStorage Typeuint, uint8, uint16, uint32, uint64uint64Named uint types (type X uint)uint64 Atom Field: Uints map[string]uint64",{"id":1334,"title":1335,"titles":1336,"content":1337,"level":41},"/v1.0.2/reference/tables#tablefloats","TableFloats",[1303,1316],"const TableFloats Table = \"floats\" Stores floating-point values (normalized to float64). Go TypesStorage Typefloat32, float64float64Named float types (type X float64)float64 Atom Field: Floats map[string]float64 Overflow: float32 overflow detected during deatomization.",{"id":1339,"title":1340,"titles":1341,"content":1342,"level":41},"/v1.0.2/reference/tables#tablebools","TableBools",[1303,1316],"const TableBools Table = \"bools\" Stores boolean values. Go TypesStorage TypeboolboolNamed bool types (type X bool)bool Atom Field: Bools map[string]bool",{"id":1344,"title":1345,"titles":1346,"content":1347,"level":41},"/v1.0.2/reference/tables#tabletimes","TableTimes",[1303,1316],"const TableTimes Table = \"times\" Stores time values. Go TypesStorage Typetime.Timetime.Time Atom Field: Times map[string]time.Time",{"id":1349,"title":1350,"titles":1351,"content":1352,"level":41},"/v1.0.2/reference/tables#tablebytes","TableBytes",[1303,1316],"const TableBytes Table = \"bytes\" Stores byte slice values. Go TypesStorage Type[]byte[]byte[N]byte (fixed arrays)[]byteNamed byte slices (type X []byte, e.g., net.IP)[]byte Atom Field: Bytes map[string][]byte Note: Fixed arrays are validated for correct length during deatomization.",{"id":1354,"title":1355,"titles":1356,"content":1357,"level":20},"/v1.0.2/reference/tables#pointer-tables","Pointer Tables",[1303],"Pointer tables store optional values that can be nil.",{"id":1359,"title":1360,"titles":1361,"content":1362,"level":41},"/v1.0.2/reference/tables#tablestringptrs","TableStringPtrs",[1303,1355],"const TableStringPtrs Table = \"string_ptrs\" Go Type: *stringAtom Field: StringPtrs map[string]*string",{"id":1364,"title":1365,"titles":1366,"content":1367,"level":41},"/v1.0.2/reference/tables#tableintptrs","TableIntPtrs",[1303,1355],"const TableIntPtrs Table = \"int_ptrs\" Go Types: *int, *int8, *int16, *int32, *int64Atom Field: IntPtrs map[string]*int64",{"id":1369,"title":1370,"titles":1371,"content":1372,"level":41},"/v1.0.2/reference/tables#tableuintptrs","TableUintPtrs",[1303,1355],"const TableUintPtrs Table = \"uint_ptrs\" Go Types: *uint, *uint8, *uint16, *uint32, *uint64Atom Field: UintPtrs map[string]*uint64",{"id":1374,"title":1375,"titles":1376,"content":1377,"level":41},"/v1.0.2/reference/tables#tablefloatptrs","TableFloatPtrs",[1303,1355],"const TableFloatPtrs Table = \"float_ptrs\" Go Types: *float32, *float64Atom Field: FloatPtrs map[string]*float64",{"id":1379,"title":1380,"titles":1381,"content":1382,"level":41},"/v1.0.2/reference/tables#tableboolptrs","TableBoolPtrs",[1303,1355],"const TableBoolPtrs Table = \"bool_ptrs\" Go Type: *boolAtom Field: BoolPtrs map[string]*bool",{"id":1384,"title":1385,"titles":1386,"content":1387,"level":41},"/v1.0.2/reference/tables#tabletimeptrs","TableTimePtrs",[1303,1355],"const TableTimePtrs Table = \"time_ptrs\" Go Type: *time.TimeAtom Field: TimePtrs map[string]*time.Time",{"id":1389,"title":1390,"titles":1391,"content":1392,"level":41},"/v1.0.2/reference/tables#tablebyteptrs","TableBytePtrs",[1303,1355],"const TableBytePtrs Table = \"byte_ptrs\" Go Type: *[]byteAtom Field: BytePtrs map[string]*[]byte",{"id":1394,"title":1395,"titles":1396,"content":1397,"level":20},"/v1.0.2/reference/tables#slice-tables","Slice Tables",[1303],"Slice tables store arrays of values.",{"id":1399,"title":1400,"titles":1401,"content":1402,"level":41},"/v1.0.2/reference/tables#tablestringslices","TableStringSlices",[1303,1395],"const TableStringSlices Table = \"string_slices\" Go Type: []stringAtom Field: StringSlices map[string][]string",{"id":1404,"title":1405,"titles":1406,"content":1407,"level":41},"/v1.0.2/reference/tables#tableintslices","TableIntSlices",[1303,1395],"const TableIntSlices Table = \"int_slices\" Go Types: []int, []int8, []int16, []int32, []int64Atom Field: IntSlices map[string][]int64",{"id":1409,"title":1410,"titles":1411,"content":1412,"level":41},"/v1.0.2/reference/tables#tableuintslices","TableUintSlices",[1303,1395],"const TableUintSlices Table = \"uint_slices\" Go Types: []uint, []uint8, []uint16, []uint32, []uint64Atom Field: UintSlices map[string][]uint64 Note: []byte is stored in TableBytes, not here.",{"id":1414,"title":1415,"titles":1416,"content":1417,"level":41},"/v1.0.2/reference/tables#tablefloatslices","TableFloatSlices",[1303,1395],"const TableFloatSlices Table = \"float_slices\" Go Types: []float32, []float64Atom Field: FloatSlices map[string][]float64",{"id":1419,"title":1420,"titles":1421,"content":1422,"level":41},"/v1.0.2/reference/tables#tableboolslices","TableBoolSlices",[1303,1395],"const TableBoolSlices Table = \"bool_slices\" Go Type: []boolAtom Field: BoolSlices map[string][]bool",{"id":1424,"title":1425,"titles":1426,"content":1427,"level":41},"/v1.0.2/reference/tables#tabletimeslices","TableTimeSlices",[1303,1395],"const TableTimeSlices Table = \"time_slices\" Go Type: []time.TimeAtom Field: TimeSlices map[string][]time.Time",{"id":1429,"title":1430,"titles":1431,"content":1432,"level":41},"/v1.0.2/reference/tables#tablebyteslices","TableByteSlices",[1303,1395],"const TableByteSlices Table = \"byte_slices\" Go Type: [][]byteAtom Field: ByteSlices map[string][][]byte",{"id":1434,"title":1435,"titles":1436,"content":1437,"level":20},"/v1.0.2/reference/tables#map-tables","Map Tables",[1303],"Map tables store string-keyed maps of values.",{"id":1439,"title":1440,"titles":1441,"content":1442,"level":41},"/v1.0.2/reference/tables#tablestringmaps","TableStringMaps",[1303,1435],"const TableStringMaps Table = \"string_maps\" Go Type: map[string]stringAtom Field: StringMaps map[string]map[string]string",{"id":1444,"title":1445,"titles":1446,"content":1447,"level":41},"/v1.0.2/reference/tables#tableintmaps","TableIntMaps",[1303,1435],"const TableIntMaps Table = \"int_maps\" Go Types: map[string]int, map[string]int8, map[string]int16, map[string]int32, map[string]int64Atom Field: IntMaps map[string]map[string]int64",{"id":1449,"title":1450,"titles":1451,"content":1452,"level":41},"/v1.0.2/reference/tables#tableuintmaps","TableUintMaps",[1303,1435],"const TableUintMaps Table = \"uint_maps\" Go Types: map[string]uint, map[string]uint8, map[string]uint16, map[string]uint32, map[string]uint64Atom Field: UintMaps map[string]map[string]uint64",{"id":1454,"title":1455,"titles":1456,"content":1457,"level":41},"/v1.0.2/reference/tables#tablefloatmaps","TableFloatMaps",[1303,1435],"const TableFloatMaps Table = \"float_maps\" Go Types: map[string]float32, map[string]float64Atom Field: FloatMaps map[string]map[string]float64",{"id":1459,"title":1460,"titles":1461,"content":1462,"level":41},"/v1.0.2/reference/tables#tableboolmaps","TableBoolMaps",[1303,1435],"const TableBoolMaps Table = \"bool_maps\" Go Type: map[string]boolAtom Field: BoolMaps map[string]map[string]bool",{"id":1464,"title":1465,"titles":1466,"content":1467,"level":41},"/v1.0.2/reference/tables#tabletimemaps","TableTimeMaps",[1303,1435],"const TableTimeMaps Table = \"time_maps\" Go Type: map[string]time.TimeAtom Field: TimeMaps map[string]map[string]time.Time",{"id":1469,"title":1470,"titles":1471,"content":1472,"level":41},"/v1.0.2/reference/tables#tablebytemaps","TableByteMaps",[1303,1435],"const TableByteMaps Table = \"byte_maps\" Go Type: map[string][]byteAtom Field: ByteMaps map[string]map[string][]byte",{"id":1474,"title":1475,"titles":1476,"content":1477,"level":41},"/v1.0.2/reference/tables#tablenestedmaps","TableNestedMaps",[1303,1435],"const TableNestedMaps Table = \"nested_maps\" Go Type: map[string]structAtom Field: NestedMaps map[string]map[string]Atom Stores maps where values are structs, recursively atomized.",{"id":1479,"title":1480,"titles":1481,"content":1482,"level":20},"/v1.0.2/reference/tables#nested-tables","Nested Tables",[1303],"Nested structs don't use a Table constant. They're stored in: TypeAtom FieldstructNested map[string]Atom*structNested map[string]Atom (nil = no entry)[]structNestedSlices map[string][]Atom[]*structNestedSlices map[string][]Atommap[string]structNestedMaps map[string]map[string]Atom",{"id":1484,"title":1485,"titles":1486,"content":35,"level":20},"/v1.0.2/reference/tables#table-methods","Table Methods",[1303],{"id":1488,"title":1251,"titles":1489,"content":1490,"level":41},"/v1.0.2/reference/tables#prefix",[1303,1485],"func (t Table) Prefix() string Returns string(t) + \":\". Useful for namespaced storage: key := atom.TableStrings.Prefix() + \"Name\"\n// \"strings:Name\"",{"id":1492,"title":146,"titles":1493,"content":35,"level":20},"/v1.0.2/reference/tables#table-categories",[1303],{"id":1495,"title":1496,"titles":1497,"content":1498,"level":41},"/v1.0.2/reference/tables#scalar-vs-non-scalar","Scalar vs Non-Scalar",[1303,146],"CategoryTablesScalarstrings, ints, uints, floats, bools, times, bytesPointer*_ptrs variantsSlice*_slices variantsMap*_maps variants",{"id":1500,"title":1501,"titles":1502,"content":1503,"level":41},"/v1.0.2/reference/tables#by-storage-size","By Storage Size",[1303,146],"SizeTablesVariablestrings, bytes, all slices8 bytesints, uints, floats, times1 bytebools",{"id":1505,"title":1506,"titles":1507,"content":1508,"level":20},"/v1.0.2/reference/tables#alltables","AllTables()",[1303],"Returns all 29 tables in canonical order: tables := atom.AllTables()\n// [strings ints uints floats bools times bytes\n//  byte_ptrs string_ptrs int_ptrs uint_ptrs float_ptrs bool_ptrs time_ptrs\n//  string_slices int_slices uint_slices float_slices bool_slices time_slices byte_slices\n//  string_maps int_maps uint_maps float_maps bool_maps time_maps byte_maps nested_maps] html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sfm-E, html code.shiki .sfm-E{--shiki-default:var(--shiki-variable)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}",{"id":1510,"title":1511,"titles":1512,"content":1513,"level":9},"/v1.0.2/reference/testing","Testing Reference",[],"Test utilities and helpers for atom-based applications",{"id":1515,"title":1511,"titles":1516,"content":1517,"level":9},"/v1.0.2/reference/testing#testing-reference",[],"Test utilities for atom-based applications.",{"id":1519,"title":6,"titles":1520,"content":1521,"level":20},"/v1.0.2/reference/testing#overview",[1511],"The atom/testing package provides utilities for testing code that uses atom. import atomtest \"github.com/zoobz-io/atom/testing\"",{"id":1523,"title":1524,"titles":1525,"content":1526,"level":20},"/v1.0.2/reference/testing#atombuilder","AtomBuilder",[1511],"Fluent builder for constructing test atoms.",{"id":1528,"title":1529,"titles":1530,"content":1531,"level":41},"/v1.0.2/reference/testing#constructor","Constructor",[1511,1524],"func NewAtomBuilder() *AtomBuilder",{"id":1533,"title":1188,"titles":1534,"content":1535,"level":41},"/v1.0.2/reference/testing#methods",[1511,1524],"// Scalar types\nfunc (b *AtomBuilder) String(key, value string) *AtomBuilder\nfunc (b *AtomBuilder) Int(key string, value int64) *AtomBuilder\nfunc (b *AtomBuilder) Uint(key string, value uint64) *AtomBuilder\nfunc (b *AtomBuilder) Float(key string, value float64) *AtomBuilder\nfunc (b *AtomBuilder) Bool(key string, value bool) *AtomBuilder\nfunc (b *AtomBuilder) Time(key string, value time.Time) *AtomBuilder\nfunc (b *AtomBuilder) Bytes(key string, value []byte) *AtomBuilder\n\n// Pointer types\nfunc (b *AtomBuilder) StringPtr(key string, value *string) *AtomBuilder\nfunc (b *AtomBuilder) IntPtr(key string, value *int64) *AtomBuilder\n\n// Slice types\nfunc (b *AtomBuilder) StringSlice(key string, value []string) *AtomBuilder\nfunc (b *AtomBuilder) IntSlice(key string, value []int64) *AtomBuilder\n\n// Nested types\nfunc (b *AtomBuilder) Nested(key string, value *atom.Atom) *AtomBuilder\nfunc (b *AtomBuilder) NestedSlice(key string, value []atom.Atom) *AtomBuilder\n\n// Metadata\nfunc (b *AtomBuilder) WithSpec(spec atom.Spec) *AtomBuilder\nfunc (b *AtomBuilder) Build() *atom.Atom",{"id":1537,"title":1538,"titles":1539,"content":1540,"level":41},"/v1.0.2/reference/testing#example","Example",[1511,1524],"a := atomtest.NewAtomBuilder().\n    String(\"Name\", \"Alice\").\n    Int(\"Age\", 30).\n    Bool(\"Active\", true).\n    Build()\n\nuser, _ := atomizer.Deatomize(a)",{"id":1542,"title":1543,"titles":1544,"content":1545,"level":20},"/v1.0.2/reference/testing#atommatcher","AtomMatcher",[1511],"Deep comparison utilities for atoms.",{"id":1547,"title":1155,"titles":1548,"content":1549,"level":41},"/v1.0.2/reference/testing#functions",[1511,1543],"func Equal(a, b *atom.Atom) bool\nfunc EqualFields(a, b *atom.Atom, fields ...string) bool\nfunc Diff(a, b *atom.Atom) string",{"id":1551,"title":1538,"titles":1552,"content":1553,"level":41},"/v1.0.2/reference/testing#example-1",[1511,1543],"func TestAtomization(t *testing.T) {\n    original := &User{Name: \"Alice\", Age: 30}\n    a := atomizer.Atomize(original)\n\n    expected := atomtest.NewAtomBuilder().\n        String(\"Name\", \"Alice\").\n        Int(\"Age\", 30).\n        Build()\n\n    if !atomtest.Equal(a, expected) {\n        t.Errorf(\"mismatch:\\n%s\", atomtest.Diff(a, expected))\n    }\n}",{"id":1555,"title":1556,"titles":1557,"content":1558,"level":20},"/v1.0.2/reference/testing#roundtripvalidator","RoundTripValidator",[1511],"Automated round-trip testing.",{"id":1560,"title":1529,"titles":1561,"content":1562,"level":41},"/v1.0.2/reference/testing#constructor-1",[1511,1556],"func NewRoundTripValidator[T any](atomizer *atom.Atomizer[T]) *RoundTripValidator[T]",{"id":1564,"title":1188,"titles":1565,"content":1566,"level":41},"/v1.0.2/reference/testing#methods-1",[1511,1556],"func (v *RoundTripValidator[T]) Validate(t *testing.T, original *T)\nfunc (v *RoundTripValidator[T]) ValidateAll(t *testing.T, cases []*T)",{"id":1568,"title":1538,"titles":1569,"content":1570,"level":41},"/v1.0.2/reference/testing#example-2",[1511,1556],"func TestUserRoundTrip(t *testing.T) {\n    atomizer, _ := atom.Use[User]()\n    validator := atomtest.NewRoundTripValidator(atomizer)\n\n    cases := []*User{\n        {Name: \"Alice\", Age: 30},\n        {Name: \"Bob\", Age: 25},\n        {Name: \"\", Age: 0}, // Edge case\n    }\n\n    validator.ValidateAll(t, cases)\n}",{"id":1572,"title":1573,"titles":1574,"content":1575,"level":20},"/v1.0.2/reference/testing#fieldchecker","FieldChecker",[1511],"Type-safe field value assertions.",{"id":1577,"title":1155,"titles":1578,"content":1579,"level":41},"/v1.0.2/reference/testing#functions-1",[1511,1573],"func GetString(a *atom.Atom, key string) (string, bool)\nfunc GetInt(a *atom.Atom, key string) (int64, bool)\nfunc GetUint(a *atom.Atom, key string) (uint64, bool)\nfunc GetFloat(a *atom.Atom, key string) (float64, bool)\nfunc GetBool(a *atom.Atom, key string) (bool, bool)\nfunc GetTime(a *atom.Atom, key string) (time.Time, bool)\nfunc GetBytes(a *atom.Atom, key string) ([]byte, bool)",{"id":1581,"title":1538,"titles":1582,"content":1583,"level":41},"/v1.0.2/reference/testing#example-3",[1511,1573],"func TestUserFields(t *testing.T) {\n    a := atomizer.Atomize(&User{Name: \"Alice\", Age: 30})\n\n    name, ok := atomtest.GetString(a, \"Name\")\n    if !ok || name != \"Alice\" {\n        t.Errorf(\"expected Name=Alice, got %s\", name)\n    }\n\n    age, ok := atomtest.GetInt(a, \"Age\")\n    if !ok || age != 30 {\n        t.Errorf(\"expected Age=30, got %d\", age)\n    }\n}",{"id":1585,"title":1586,"titles":1587,"content":1588,"level":20},"/v1.0.2/reference/testing#mustuse","MustUse",[1511],"Panics on registration failure (for test setup). func MustUse[T any](t *testing.T) *atom.Atomizer[T]",{"id":1590,"title":1538,"titles":1591,"content":1592,"level":41},"/v1.0.2/reference/testing#example-4",[1511,1586],"func TestUser(t *testing.T) {\n    atomizer := atomtest.MustUse[User](t)\n\n    // Test code...\n}",{"id":1594,"title":1595,"titles":1596,"content":1597,"level":20},"/v1.0.2/reference/testing#typefixtures","TypeFixtures",[1511],"Common test data generators.",{"id":1599,"title":1155,"titles":1600,"content":1601,"level":41},"/v1.0.2/reference/testing#functions-2",[1511,1595],"func RandomString(length int) string\nfunc RandomInt(min, max int64) int64\nfunc RandomFloat(min, max float64) float64\nfunc RandomBytes(length int) []byte\nfunc RandomTime() time.Time",{"id":1603,"title":1538,"titles":1604,"content":1605,"level":41},"/v1.0.2/reference/testing#example-5",[1511,1595],"func TestRandomUser(t *testing.T) {\n    user := &User{\n        Name:   atomtest.RandomString(10),\n        Age:    atomtest.RandomInt(18, 100),\n        Score:  atomtest.RandomFloat(0, 100),\n    }\n\n    validator.Validate(t, user)\n}",{"id":1607,"title":1608,"titles":1609,"content":1610,"level":20},"/v1.0.2/reference/testing#assertatom","AssertAtom",[1511],"Testing assertions for atoms.",{"id":1612,"title":1155,"titles":1613,"content":1614,"level":41},"/v1.0.2/reference/testing#functions-3",[1511,1608],"func AssertHasString(t *testing.T, a *atom.Atom, key, expected string)\nfunc AssertHasInt(t *testing.T, a *atom.Atom, key string, expected int64)\nfunc AssertHasFloat(t *testing.T, a *atom.Atom, key string, expected float64)\nfunc AssertHasBool(t *testing.T, a *atom.Atom, key string, expected bool)\nfunc AssertHasNested(t *testing.T, a *atom.Atom, key string)\nfunc AssertMissingField(t *testing.T, a *atom.Atom, table atom.Table, key string)",{"id":1616,"title":1538,"titles":1617,"content":1618,"level":41},"/v1.0.2/reference/testing#example-6",[1511,1608],"func TestUserAtom(t *testing.T) {\n    a := atomizer.Atomize(&User{Name: \"Alice\", Age: 30})\n\n    atomtest.AssertHasString(t, a, \"Name\", \"Alice\")\n    atomtest.AssertHasInt(t, a, \"Age\", 30)\n    atomtest.AssertMissingField(t, a, atom.TableStrings, \"NotAField\")\n}",{"id":1620,"title":1621,"titles":1622,"content":1623,"level":20},"/v1.0.2/reference/testing#example-test-file","Example Test File",[1511],"package myapp\n\nimport (\n    \"testing\"\n\n    \"github.com/zoobz-io/atom\"\n    atomtest \"github.com/zoobz-io/atom/testing\"\n)\n\nfunc TestUserAtomization(t *testing.T) {\n    atomizer := atomtest.MustUse[User](t)\n\n    t.Run(\"basic round trip\", func(t *testing.T) {\n        original := &User{Name: \"Alice\", Age: 30}\n        a := atomizer.Atomize(original)\n\n        atomtest.AssertHasString(t, a, \"Name\", \"Alice\")\n        atomtest.AssertHasInt(t, a, \"Age\", 30)\n\n        restored, err := atomizer.Deatomize(a)\n        if err != nil {\n            t.Fatal(err)\n        }\n\n        if restored.Name != original.Name {\n            t.Errorf(\"Name: got %q, want %q\", restored.Name, original.Name)\n        }\n    })\n\n    t.Run(\"builder pattern\", func(t *testing.T) {\n        a := atomtest.NewAtomBuilder().\n            String(\"Name\", \"Bob\").\n            Int(\"Age\", 25).\n            Build()\n\n        user, err := atomizer.Deatomize(a)\n        if err != nil {\n            t.Fatal(err)\n        }\n\n        if user.Name != \"Bob\" {\n            t.Errorf(\"expected Bob, got %s\", user.Name)\n        }\n    })\n\n    t.Run(\"random data\", func(t *testing.T) {\n        validator := atomtest.NewRoundTripValidator(atomizer)\n\n        for i := 0; i \u003C 100; i++ {\n            user := &User{\n                Name: atomtest.RandomString(20),\n                Age:  atomtest.RandomInt(0, 150),\n            }\n            validator.Validate(t, user)\n        }\n    })\n}",{"id":1625,"title":406,"titles":1626,"content":35,"level":20},"/v1.0.2/reference/testing#best-practices",[1511],{"id":1628,"title":1629,"titles":1630,"content":1631,"level":41},"/v1.0.2/reference/testing#use-mustuse-in-tests","Use MustUse in Tests",[1511,406],"// Good - fails test on error\natomizer := atomtest.MustUse[User](t)\n\n// Verbose - manual error handling\natomizer, err := atom.Use[User]()\nif err != nil {\n    t.Fatalf(\"Use failed: %v\", err)\n}",{"id":1633,"title":1634,"titles":1635,"content":1636,"level":41},"/v1.0.2/reference/testing#use-builders-for-clarity","Use Builders for Clarity",[1511,406],"// Good - clear intent\na := atomtest.NewAtomBuilder().\n    String(\"Name\", \"Alice\").\n    Int(\"Age\", 30).\n    Build()\n\n// Less clear\na := &atom.Atom{\n    Strings: map[string]string{\"Name\": \"Alice\"},\n    Ints:    map[string]int64{\"Age\": 30},\n}",{"id":1638,"title":1639,"titles":1640,"content":1641,"level":41},"/v1.0.2/reference/testing#use-validators-for-round-trips","Use Validators for Round-Trips",[1511,406],"// Good - automated validation\nvalidator.ValidateAll(t, testCases)\n\n// Manual - error-prone\nfor _, tc := range testCases {\n    a := atomizer.Atomize(tc)\n    restored, _ := atomizer.Deatomize(a)\n    // Manual comparison...\n} html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}",[1643],{"title":1644,"path":1645,"stem":1646,"children":1647,"page":1661},"V102","/v1.0.2","v1.0.2",[1648,1650,1662,1677,1688],{"title":6,"path":5,"stem":1649,"description":8},"v1.0.2/1.overview",{"title":1651,"path":1652,"stem":1653,"children":1654,"page":1661},"Learn","/v1.0.2/learn","v1.0.2/2.learn",[1655,1657,1659],{"title":69,"path":68,"stem":1656,"description":71},"v1.0.2/2.learn/1.quickstart",{"title":122,"path":121,"stem":1658,"description":124},"v1.0.2/2.learn/2.concepts",{"title":190,"path":189,"stem":1660,"description":192},"v1.0.2/2.learn/3.architecture",false,{"title":1663,"path":1664,"stem":1665,"children":1666,"page":1661},"Guides","/v1.0.2/guides","v1.0.2/3.guides",[1667,1669,1671,1673,1675],{"title":83,"path":275,"stem":1668,"description":277},"v1.0.2/3.guides/1.basic-usage",{"title":429,"path":428,"stem":1670,"description":431},"v1.0.2/3.guides/2.custom-types",{"title":526,"path":525,"stem":1672,"description":528},"v1.0.2/3.guides/3.nested-structs",{"title":617,"path":616,"stem":1674,"description":619},"v1.0.2/3.guides/4.interfaces",{"title":716,"path":715,"stem":1676,"description":718},"v1.0.2/3.guides/5.testing",{"title":1678,"path":1679,"stem":1680,"children":1681,"page":1661},"Cookbook","/v1.0.2/cookbook","v1.0.2/4.cookbook",[1682,1684,1686],{"title":689,"path":816,"stem":1683,"description":818},"v1.0.2/4.cookbook/1.codegen",{"title":902,"path":901,"stem":1685,"description":904},"v1.0.2/4.cookbook/2.serialization",{"title":1004,"path":1003,"stem":1687,"description":1006},"v1.0.2/4.cookbook/3.migrations",{"title":1689,"path":1690,"stem":1691,"children":1692,"page":1661},"Reference","/v1.0.2/reference","v1.0.2/5.reference",[1693,1695,1697],{"title":1141,"path":1140,"stem":1694,"description":1143},"v1.0.2/5.reference/1.api",{"title":1303,"path":1302,"stem":1696,"description":1305},"v1.0.2/5.reference/2.tables",{"title":1511,"path":1510,"stem":1698,"description":1513},"v1.0.2/5.reference/3.testing",[1700],{"title":1644,"path":1645,"stem":1646,"children":1701,"page":1661},[1702,1703,1708,1715,1720],{"title":6,"path":5,"stem":1649},{"title":1651,"path":1652,"stem":1653,"children":1704,"page":1661},[1705,1706,1707],{"title":69,"path":68,"stem":1656},{"title":122,"path":121,"stem":1658},{"title":190,"path":189,"stem":1660},{"title":1663,"path":1664,"stem":1665,"children":1709,"page":1661},[1710,1711,1712,1713,1714],{"title":83,"path":275,"stem":1668},{"title":429,"path":428,"stem":1670},{"title":526,"path":525,"stem":1672},{"title":617,"path":616,"stem":1674},{"title":716,"path":715,"stem":1676},{"title":1678,"path":1679,"stem":1680,"children":1716,"page":1661},[1717,1718,1719],{"title":689,"path":816,"stem":1683},{"title":902,"path":901,"stem":1685},{"title":1004,"path":1003,"stem":1687},{"title":1689,"path":1690,"stem":1691,"children":1721,"page":1661},[1722,1723,1724],{"title":1141,"path":1140,"stem":1694},{"title":1303,"path":1302,"stem":1696},{"title":1511,"path":1510,"stem":1698},[1726,3422,3577],{"id":1727,"title":1728,"body":1729,"description":35,"extension":3415,"icon":3416,"meta":3417,"navigation":1906,"path":3418,"seo":3419,"stem":3420,"__hash__":3421},"resources/readme.md","README",{"type":1730,"value":1731,"toc":3399},"minimark",[1732,1736,1804,1807,1810,1815,1818,2156,2159,2407,2410,2414,2431,2434,2438,2875,2879,3009,3013,3052,3056,3062,3065,3263,3266,3270,3274,3293,3296,3325,3328,3348,3351,3370,3374,3386,3389,3395],[1733,1734,1735],"h1",{"id":1735},"atom",[1737,1738,1739,1750,1758,1766,1774,1782,1789,1796],"p",{},[1740,1741,1745],"a",{"href":1742,"rel":1743},"https://github.com/zoobz-io/atom/actions/workflows/ci.yml",[1744],"nofollow",[1746,1747],"img",{"alt":1748,"src":1749},"CI Status","https://github.com/zoobz-io/atom/workflows/CI/badge.svg",[1740,1751,1754],{"href":1752,"rel":1753},"https://codecov.io/gh/zoobz-io/atom",[1744],[1746,1755],{"alt":1756,"src":1757},"codecov","https://codecov.io/gh/zoobz-io/atom/graph/badge.svg?branch=main",[1740,1759,1762],{"href":1760,"rel":1761},"https://goreportcard.com/report/github.com/zoobz-io/atom",[1744],[1746,1763],{"alt":1764,"src":1765},"Go Report Card","https://goreportcard.com/badge/github.com/zoobz-io/atom",[1740,1767,1770],{"href":1768,"rel":1769},"https://github.com/zoobz-io/atom/security/code-scanning",[1744],[1746,1771],{"alt":1772,"src":1773},"CodeQL","https://github.com/zoobz-io/atom/workflows/CodeQL/badge.svg",[1740,1775,1778],{"href":1776,"rel":1777},"https://pkg.go.dev/github.com/zoobz-io/atom",[1744],[1746,1779],{"alt":1780,"src":1781},"Go Reference","https://pkg.go.dev/badge/github.com/zoobz-io/atom.svg",[1740,1783,1785],{"href":1784},"LICENSE",[1746,1786],{"alt":1787,"src":1788},"License","https://img.shields.io/github/license/zoobz-io/atom",[1740,1790,1792],{"href":1791},"go.mod",[1746,1793],{"alt":1794,"src":1795},"Go Version","https://img.shields.io/github/go-mod/go-version/zoobz-io/atom",[1740,1797,1800],{"href":1798,"rel":1799},"https://github.com/zoobz-io/atom/releases",[1744],[1746,1801],{"alt":1802,"src":1803},"Release","https://img.shields.io/github/v/release/zoobz-io/atom",[1737,1805,1806],{},"Type-segregated struct decomposition for Go.",[1737,1808,1809],{},"Break structs into typed maps, work with fields programmatically, reconstruct later — without knowing T.",[1811,1812,1814],"h2",{"id":1813},"normalized-type-safe-data-without-the-types","Normalized Type-Safe Data Without the Types",[1737,1816,1817],{},"User code knows its types. Infrastructure code doesn't need to.",[1819,1820,1824],"pre",{"className":1821,"code":1822,"language":1823,"meta":35,"style":35},"language-go shiki shiki-themes","// User side: knows T\ntype User struct {\n    ID      string\n    Name    string\n    Age     int64\n    Balance float64\n    Active  bool\n}\n\natomizer, _ := atom.Use[User]()\nuser := &User{ID: \"usr-1\", Name: \"Alice\", Age: 30, Balance: 100.50, Active: true}\na := atomizer.Atomize(user)\n\n// Pass atom to any library...\nresult := storage.Save(a)            // storage never imports User\nvalidated := validator.Check(a)      // validator never imports User\ntransformed := migrator.Upgrade(a)   // migrator never imports User\n\n// ...get it back\nrestored, _ := atomizer.Deatomize(result)\n","go",[1825,1826,1827,1835,1852,1861,1869,1877,1886,1895,1901,1908,1942,2012,2034,2039,2045,2071,2096,2121,2126,2132],"code",{"__ignoreMap":35},[1828,1829,1831],"span",{"class":1830,"line":9},"line",[1828,1832,1834],{"class":1833},"sLkEo","// User side: knows T\n",[1828,1836,1837,1841,1845,1848],{"class":1830,"line":20},[1828,1838,1840],{"class":1839},"sUt3r","type",[1828,1842,1844],{"class":1843},"sYBwO"," User",[1828,1846,1847],{"class":1839}," struct",[1828,1849,1851],{"class":1850},"sq5bi"," {\n",[1828,1853,1854,1858],{"class":1830,"line":41},[1828,1855,1857],{"class":1856},"sBGCq","    ID",[1828,1859,1860],{"class":1843},"      string\n",[1828,1862,1863,1866],{"class":1830,"line":1190},[1828,1864,1865],{"class":1856},"    Name",[1828,1867,1868],{"class":1843},"    string\n",[1828,1870,1871,1874],{"class":1830,"line":1195},[1828,1872,1873],{"class":1856},"    Age",[1828,1875,1876],{"class":1843},"     int64\n",[1828,1878,1880,1883],{"class":1830,"line":1879},6,[1828,1881,1882],{"class":1856},"    Balance",[1828,1884,1885],{"class":1843}," float64\n",[1828,1887,1889,1892],{"class":1830,"line":1888},7,[1828,1890,1891],{"class":1856},"    Active",[1828,1893,1894],{"class":1843},"  bool\n",[1828,1896,1898],{"class":1830,"line":1897},8,[1828,1899,1900],{"class":1850},"}\n",[1828,1902,1904],{"class":1830,"line":1903},9,[1828,1905,1907],{"emptyLinePlaceholder":1906},true,"\n",[1828,1909,1911,1915,1918,1921,1924,1927,1930,1933,1936,1939],{"class":1830,"line":1910},10,[1828,1912,1914],{"class":1913},"sh8_p","atomizer",[1828,1916,1917],{"class":1850},",",[1828,1919,1920],{"class":1913}," _",[1828,1922,1923],{"class":1913}," :=",[1828,1925,1926],{"class":1913}," atom",[1828,1928,1929],{"class":1850},".",[1828,1931,1159],{"class":1932},"s5klm",[1828,1934,1935],{"class":1850},"[",[1828,1937,1938],{"class":1843},"User",[1828,1940,1941],{"class":1850},"]()\n",[1828,1943,1945,1948,1950,1954,1956,1959,1962,1965,1969,1971,1974,1976,1979,1981,1984,1986,1990,1992,1995,1997,2000,2002,2005,2007,2010],{"class":1830,"line":1944},11,[1828,1946,1947],{"class":1913},"user",[1828,1949,1923],{"class":1913},[1828,1951,1953],{"class":1952},"sW3Qg"," &",[1828,1955,1938],{"class":1843},[1828,1957,1958],{"class":1850},"{",[1828,1960,1961],{"class":1856},"ID",[1828,1963,1964],{"class":1850},":",[1828,1966,1968],{"class":1967},"sxAnc"," \"usr-1\"",[1828,1970,1917],{"class":1850},[1828,1972,1973],{"class":1856}," Name",[1828,1975,1964],{"class":1850},[1828,1977,1978],{"class":1967}," \"Alice\"",[1828,1980,1917],{"class":1850},[1828,1982,1983],{"class":1856}," Age",[1828,1985,1964],{"class":1850},[1828,1987,1989],{"class":1988},"sMAmT"," 30",[1828,1991,1917],{"class":1850},[1828,1993,1994],{"class":1856}," Balance",[1828,1996,1964],{"class":1850},[1828,1998,1999],{"class":1988}," 100.50",[1828,2001,1917],{"class":1850},[1828,2003,2004],{"class":1856}," Active",[1828,2006,1964],{"class":1850},[1828,2008,2009],{"class":1839}," true",[1828,2011,1900],{"class":1850},[1828,2013,2015,2017,2019,2022,2024,2026,2029,2031],{"class":1830,"line":2014},12,[1828,2016,1740],{"class":1913},[1828,2018,1923],{"class":1913},[1828,2020,2021],{"class":1913}," atomizer",[1828,2023,1929],{"class":1850},[1828,2025,252],{"class":1932},[1828,2027,2028],{"class":1850},"(",[1828,2030,1947],{"class":1913},[1828,2032,2033],{"class":1850},")\n",[1828,2035,2037],{"class":1830,"line":2036},13,[1828,2038,1907],{"emptyLinePlaceholder":1906},[1828,2040,2042],{"class":1830,"line":2041},14,[1828,2043,2044],{"class":1833},"// Pass atom to any library...\n",[1828,2046,2048,2051,2053,2056,2058,2061,2063,2065,2068],{"class":1830,"line":2047},15,[1828,2049,2050],{"class":1913},"result",[1828,2052,1923],{"class":1913},[1828,2054,2055],{"class":1913}," storage",[1828,2057,1929],{"class":1850},[1828,2059,2060],{"class":1932},"Save",[1828,2062,2028],{"class":1850},[1828,2064,1740],{"class":1913},[1828,2066,2067],{"class":1850},")",[1828,2069,2070],{"class":1833},"            // storage never imports User\n",[1828,2072,2074,2077,2079,2082,2084,2087,2089,2091,2093],{"class":1830,"line":2073},16,[1828,2075,2076],{"class":1913},"validated",[1828,2078,1923],{"class":1913},[1828,2080,2081],{"class":1913}," validator",[1828,2083,1929],{"class":1850},[1828,2085,2086],{"class":1932},"Check",[1828,2088,2028],{"class":1850},[1828,2090,1740],{"class":1913},[1828,2092,2067],{"class":1850},[1828,2094,2095],{"class":1833},"      // validator never imports User\n",[1828,2097,2099,2102,2104,2107,2109,2112,2114,2116,2118],{"class":1830,"line":2098},17,[1828,2100,2101],{"class":1913},"transformed",[1828,2103,1923],{"class":1913},[1828,2105,2106],{"class":1913}," migrator",[1828,2108,1929],{"class":1850},[1828,2110,2111],{"class":1932},"Upgrade",[1828,2113,2028],{"class":1850},[1828,2115,1740],{"class":1913},[1828,2117,2067],{"class":1850},[1828,2119,2120],{"class":1833},"   // migrator never imports User\n",[1828,2122,2124],{"class":1830,"line":2123},18,[1828,2125,1907],{"emptyLinePlaceholder":1906},[1828,2127,2129],{"class":1830,"line":2128},19,[1828,2130,2131],{"class":1833},"// ...get it back\n",[1828,2133,2135,2138,2140,2142,2144,2146,2148,2150,2152,2154],{"class":1830,"line":2134},20,[1828,2136,2137],{"class":1913},"restored",[1828,2139,1917],{"class":1850},[1828,2141,1920],{"class":1913},[1828,2143,1923],{"class":1913},[1828,2145,2021],{"class":1913},[1828,2147,1929],{"class":1850},[1828,2149,257],{"class":1932},[1828,2151,2028],{"class":1850},[1828,2153,2050],{"class":1913},[1828,2155,2033],{"class":1850},[1737,2157,2158],{},"The receiving library sees typed maps and metadata — not T:",[1819,2160,2162],{"className":1821,"code":2161,"language":1823,"meta":35,"style":35},"// Library side: doesn't know T, doesn't need T\nfunc Save(a *atom.Atom) *atom.Atom {\n    // Spec describes the struct\n    fmt.Println(a.Spec.TypeName) // \"User\"\n\n    // Typed maps hold the values\n    for field, value := range a.Strings {\n        db.SetString(field, value)\n    }\n    for field, value := range a.Ints {\n        db.SetInt(field, value)\n    }\n    for field, value := range a.Floats {\n        db.SetFloat(field, value)\n    }\n\n    return a\n}\n",[1825,2163,2164,2169,2203,2208,2236,2240,2245,2273,2294,2299,2322,2341,2345,2368,2387,2391,2395,2403],{"__ignoreMap":35},[1828,2165,2166],{"class":1830,"line":9},[1828,2167,2168],{"class":1833},"// Library side: doesn't know T, doesn't need T\n",[1828,2170,2171,2174,2177,2179,2182,2185,2187,2189,2191,2193,2195,2197,2199,2201],{"class":1830,"line":20},[1828,2172,2173],{"class":1839},"func",[1828,2175,2176],{"class":1932}," Save",[1828,2178,2028],{"class":1850},[1828,2180,1740],{"class":2181},"sSYET",[1828,2183,2184],{"class":1952}," *",[1828,2186,1735],{"class":1843},[1828,2188,1929],{"class":1850},[1828,2190,12],{"class":1843},[1828,2192,2067],{"class":1850},[1828,2194,2184],{"class":1952},[1828,2196,1735],{"class":1843},[1828,2198,1929],{"class":1850},[1828,2200,12],{"class":1843},[1828,2202,1851],{"class":1850},[1828,2204,2205],{"class":1830,"line":41},[1828,2206,2207],{"class":1833},"    // Spec describes the struct\n",[1828,2209,2210,2213,2215,2218,2220,2222,2224,2226,2228,2231,2233],{"class":1830,"line":1190},[1828,2211,2212],{"class":1913},"    fmt",[1828,2214,1929],{"class":1850},[1828,2216,2217],{"class":1932},"Println",[1828,2219,2028],{"class":1850},[1828,2221,1740],{"class":1913},[1828,2223,1929],{"class":1850},[1828,2225,1207],{"class":1913},[1828,2227,1929],{"class":1850},[1828,2229,2230],{"class":1913},"TypeName",[1828,2232,2067],{"class":1850},[1828,2234,2235],{"class":1833}," // \"User\"\n",[1828,2237,2238],{"class":1830,"line":1195},[1828,2239,1907],{"emptyLinePlaceholder":1906},[1828,2241,2242],{"class":1830,"line":1879},[1828,2243,2244],{"class":1833},"    // Typed maps hold the values\n",[1828,2246,2247,2250,2253,2255,2258,2260,2263,2266,2268,2271],{"class":1830,"line":1888},[1828,2248,2249],{"class":1952},"    for",[1828,2251,2252],{"class":1913}," field",[1828,2254,1917],{"class":1850},[1828,2256,2257],{"class":1913}," value",[1828,2259,1923],{"class":1913},[1828,2261,2262],{"class":1952}," range",[1828,2264,2265],{"class":1913}," a",[1828,2267,1929],{"class":1850},[1828,2269,2270],{"class":1913},"Strings",[1828,2272,1851],{"class":1850},[1828,2274,2275,2278,2280,2283,2285,2288,2290,2292],{"class":1830,"line":1897},[1828,2276,2277],{"class":1913},"        db",[1828,2279,1929],{"class":1850},[1828,2281,2282],{"class":1932},"SetString",[1828,2284,2028],{"class":1850},[1828,2286,2287],{"class":1913},"field",[1828,2289,1917],{"class":1850},[1828,2291,2257],{"class":1913},[1828,2293,2033],{"class":1850},[1828,2295,2296],{"class":1830,"line":1903},[1828,2297,2298],{"class":1850},"    }\n",[1828,2300,2301,2303,2305,2307,2309,2311,2313,2315,2317,2320],{"class":1830,"line":1910},[1828,2302,2249],{"class":1952},[1828,2304,2252],{"class":1913},[1828,2306,1917],{"class":1850},[1828,2308,2257],{"class":1913},[1828,2310,1923],{"class":1913},[1828,2312,2262],{"class":1952},[1828,2314,2265],{"class":1913},[1828,2316,1929],{"class":1850},[1828,2318,2319],{"class":1913},"Ints",[1828,2321,1851],{"class":1850},[1828,2323,2324,2326,2328,2331,2333,2335,2337,2339],{"class":1830,"line":1944},[1828,2325,2277],{"class":1913},[1828,2327,1929],{"class":1850},[1828,2329,2330],{"class":1932},"SetInt",[1828,2332,2028],{"class":1850},[1828,2334,2287],{"class":1913},[1828,2336,1917],{"class":1850},[1828,2338,2257],{"class":1913},[1828,2340,2033],{"class":1850},[1828,2342,2343],{"class":1830,"line":2014},[1828,2344,2298],{"class":1850},[1828,2346,2347,2349,2351,2353,2355,2357,2359,2361,2363,2366],{"class":1830,"line":2036},[1828,2348,2249],{"class":1952},[1828,2350,2252],{"class":1913},[1828,2352,1917],{"class":1850},[1828,2354,2257],{"class":1913},[1828,2356,1923],{"class":1913},[1828,2358,2262],{"class":1952},[1828,2360,2265],{"class":1913},[1828,2362,1929],{"class":1850},[1828,2364,2365],{"class":1913},"Floats",[1828,2367,1851],{"class":1850},[1828,2369,2370,2372,2374,2377,2379,2381,2383,2385],{"class":1830,"line":2041},[1828,2371,2277],{"class":1913},[1828,2373,1929],{"class":1850},[1828,2375,2376],{"class":1932},"SetFloat",[1828,2378,2028],{"class":1850},[1828,2380,2287],{"class":1913},[1828,2382,1917],{"class":1850},[1828,2384,2257],{"class":1913},[1828,2386,2033],{"class":1850},[1828,2388,2389],{"class":1830,"line":2047},[1828,2390,2298],{"class":1850},[1828,2392,2393],{"class":1830,"line":2073},[1828,2394,1907],{"emptyLinePlaceholder":1906},[1828,2396,2397,2400],{"class":1830,"line":2098},[1828,2398,2399],{"class":1952},"    return",[1828,2401,2402],{"class":1913}," a\n",[1828,2404,2405],{"class":1830,"line":2123},[1828,2406,1900],{"class":1850},[1737,2408,2409],{},"Type-safe field access. Zero knowledge of the original struct.",[1811,2411,2413],{"id":2412},"install","Install",[1819,2415,2419],{"className":2416,"code":2417,"language":2418,"meta":35,"style":35},"language-bash shiki shiki-themes","go get github.com/zoobz-io/atom@latest\n","bash",[1825,2420,2421],{"__ignoreMap":35},[1828,2422,2423,2425,2428],{"class":1830,"line":9},[1828,2424,1823],{"class":1932},[1828,2426,2427],{"class":1967}," get",[1828,2429,2430],{"class":1967}," github.com/zoobz-io/atom@latest\n",[1737,2432,2433],{},"Requires Go 1.24+.",[1811,2435,2437],{"id":2436},"quick-start","Quick Start",[1819,2439,2441],{"className":1821,"code":2440,"language":1823,"meta":35,"style":35},"package main\n\nimport (\n    \"fmt\"\n    \"github.com/zoobz-io/atom\"\n)\n\ntype Order struct {\n    ID     string\n    Total  float64\n    Status string\n}\n\nfunc main() {\n    // Register the type once\n    atomizer, err := atom.Use[Order]()\n    if err != nil {\n        panic(err)\n    }\n\n    order := &Order{ID: \"order-123\", Total: 99.99, Status: \"pending\"}\n\n    // Decompose to atom\n    a := atomizer.Atomize(order)\n\n    // Work with typed maps\n    fmt.Printf(\"ID: %s\\n\", a.Strings[\"ID\"])\n    fmt.Printf(\"Total: %.2f\\n\", a.Floats[\"Total\"])\n\n    // Modify fields\n    a.Strings[\"Status\"] = \"confirmed\"\n\n    // Reconstruct\n    restored, _ := atomizer.Deatomize(a)\n    fmt.Printf(\"Status: %s\\n\", restored.Status) // \"confirmed\"\n}\n",[1825,2442,2443,2451,2455,2464,2469,2474,2478,2482,2493,2500,2508,2516,2520,2524,2536,2541,2566,2581,2594,2598,2602,2645,2650,2656,2677,2682,2688,2730,2766,2771,2777,2800,2805,2811,2835,2870],{"__ignoreMap":35},[1828,2444,2445,2448],{"class":1830,"line":9},[1828,2446,2447],{"class":1839},"package",[1828,2449,2450],{"class":1843}," main\n",[1828,2452,2453],{"class":1830,"line":20},[1828,2454,1907],{"emptyLinePlaceholder":1906},[1828,2456,2457,2460],{"class":1830,"line":41},[1828,2458,2459],{"class":1839},"import",[1828,2461,2463],{"class":2462},"soy-K"," (\n",[1828,2465,2466],{"class":1830,"line":1190},[1828,2467,2468],{"class":1967},"    \"fmt\"\n",[1828,2470,2471],{"class":1830,"line":1195},[1828,2472,2473],{"class":1967},"    \"github.com/zoobz-io/atom\"\n",[1828,2475,2476],{"class":1830,"line":1879},[1828,2477,2033],{"class":2462},[1828,2479,2480],{"class":1830,"line":1888},[1828,2481,1907],{"emptyLinePlaceholder":1906},[1828,2483,2484,2486,2489,2491],{"class":1830,"line":1897},[1828,2485,1840],{"class":1839},[1828,2487,2488],{"class":1843}," Order",[1828,2490,1847],{"class":1839},[1828,2492,1851],{"class":1850},[1828,2494,2495,2497],{"class":1830,"line":1903},[1828,2496,1857],{"class":1856},[1828,2498,2499],{"class":1843},"     string\n",[1828,2501,2502,2505],{"class":1830,"line":1910},[1828,2503,2504],{"class":1856},"    Total",[1828,2506,2507],{"class":1843},"  float64\n",[1828,2509,2510,2513],{"class":1830,"line":1944},[1828,2511,2512],{"class":1856},"    Status",[1828,2514,2515],{"class":1843}," string\n",[1828,2517,2518],{"class":1830,"line":2014},[1828,2519,1900],{"class":1850},[1828,2521,2522],{"class":1830,"line":2036},[1828,2523,1907],{"emptyLinePlaceholder":1906},[1828,2525,2526,2528,2531,2534],{"class":1830,"line":2041},[1828,2527,2173],{"class":1839},[1828,2529,2530],{"class":1932}," main",[1828,2532,2533],{"class":1850},"()",[1828,2535,1851],{"class":1850},[1828,2537,2538],{"class":1830,"line":2047},[1828,2539,2540],{"class":1833},"    // Register the type once\n",[1828,2542,2543,2546,2548,2551,2553,2555,2557,2559,2561,2564],{"class":1830,"line":2073},[1828,2544,2545],{"class":1913},"    atomizer",[1828,2547,1917],{"class":1850},[1828,2549,2550],{"class":1913}," err",[1828,2552,1923],{"class":1913},[1828,2554,1926],{"class":1913},[1828,2556,1929],{"class":1850},[1828,2558,1159],{"class":1932},[1828,2560,1935],{"class":1850},[1828,2562,2563],{"class":1843},"Order",[1828,2565,1941],{"class":1850},[1828,2567,2568,2571,2573,2576,2579],{"class":1830,"line":2098},[1828,2569,2570],{"class":1952},"    if",[1828,2572,2550],{"class":1913},[1828,2574,2575],{"class":1952}," !=",[1828,2577,2578],{"class":1839}," nil",[1828,2580,1851],{"class":1850},[1828,2582,2583,2587,2589,2592],{"class":1830,"line":2123},[1828,2584,2586],{"class":2585},"skxcq","        panic",[1828,2588,2028],{"class":1850},[1828,2590,2591],{"class":1913},"err",[1828,2593,2033],{"class":1850},[1828,2595,2596],{"class":1830,"line":2128},[1828,2597,2298],{"class":1850},[1828,2599,2600],{"class":1830,"line":2134},[1828,2601,1907],{"emptyLinePlaceholder":1906},[1828,2603,2605,2608,2610,2612,2614,2616,2618,2620,2623,2625,2628,2630,2633,2635,2638,2640,2643],{"class":1830,"line":2604},21,[1828,2606,2607],{"class":1913},"    order",[1828,2609,1923],{"class":1913},[1828,2611,1953],{"class":1952},[1828,2613,2563],{"class":1843},[1828,2615,1958],{"class":1850},[1828,2617,1961],{"class":1856},[1828,2619,1964],{"class":1850},[1828,2621,2622],{"class":1967}," \"order-123\"",[1828,2624,1917],{"class":1850},[1828,2626,2627],{"class":1856}," Total",[1828,2629,1964],{"class":1850},[1828,2631,2632],{"class":1988}," 99.99",[1828,2634,1917],{"class":1850},[1828,2636,2637],{"class":1856}," Status",[1828,2639,1964],{"class":1850},[1828,2641,2642],{"class":1967}," \"pending\"",[1828,2644,1900],{"class":1850},[1828,2646,2648],{"class":1830,"line":2647},22,[1828,2649,1907],{"emptyLinePlaceholder":1906},[1828,2651,2653],{"class":1830,"line":2652},23,[1828,2654,2655],{"class":1833},"    // Decompose to atom\n",[1828,2657,2659,2662,2664,2666,2668,2670,2672,2675],{"class":1830,"line":2658},24,[1828,2660,2661],{"class":1913},"    a",[1828,2663,1923],{"class":1913},[1828,2665,2021],{"class":1913},[1828,2667,1929],{"class":1850},[1828,2669,252],{"class":1932},[1828,2671,2028],{"class":1850},[1828,2673,2674],{"class":1913},"order",[1828,2676,2033],{"class":1850},[1828,2678,2680],{"class":1830,"line":2679},25,[1828,2681,1907],{"emptyLinePlaceholder":1906},[1828,2683,2685],{"class":1830,"line":2684},26,[1828,2686,2687],{"class":1833},"    // Work with typed maps\n",[1828,2689,2691,2693,2695,2698,2700,2703,2707,2711,2714,2716,2718,2720,2722,2724,2727],{"class":1830,"line":2690},27,[1828,2692,2212],{"class":1913},[1828,2694,1929],{"class":1850},[1828,2696,2697],{"class":1932},"Printf",[1828,2699,2028],{"class":1850},[1828,2701,2702],{"class":1967},"\"ID: ",[1828,2704,2706],{"class":2705},"scyPU","%s",[1828,2708,2710],{"class":2709},"suWN2","\\n",[1828,2712,2713],{"class":1967},"\"",[1828,2715,1917],{"class":1850},[1828,2717,2265],{"class":1913},[1828,2719,1929],{"class":1850},[1828,2721,2270],{"class":1913},[1828,2723,1935],{"class":1850},[1828,2725,2726],{"class":1967},"\"ID\"",[1828,2728,2729],{"class":1850},"])\n",[1828,2731,2733,2735,2737,2739,2741,2744,2747,2749,2751,2753,2755,2757,2759,2761,2764],{"class":1830,"line":2732},28,[1828,2734,2212],{"class":1913},[1828,2736,1929],{"class":1850},[1828,2738,2697],{"class":1932},[1828,2740,2028],{"class":1850},[1828,2742,2743],{"class":1967},"\"Total: ",[1828,2745,2746],{"class":2705},"%.2f",[1828,2748,2710],{"class":2709},[1828,2750,2713],{"class":1967},[1828,2752,1917],{"class":1850},[1828,2754,2265],{"class":1913},[1828,2756,1929],{"class":1850},[1828,2758,2365],{"class":1913},[1828,2760,1935],{"class":1850},[1828,2762,2763],{"class":1967},"\"Total\"",[1828,2765,2729],{"class":1850},[1828,2767,2769],{"class":1830,"line":2768},29,[1828,2770,1907],{"emptyLinePlaceholder":1906},[1828,2772,2774],{"class":1830,"line":2773},30,[1828,2775,2776],{"class":1833},"    // Modify fields\n",[1828,2778,2780,2782,2784,2786,2788,2791,2794,2797],{"class":1830,"line":2779},31,[1828,2781,2661],{"class":1913},[1828,2783,1929],{"class":1850},[1828,2785,2270],{"class":1913},[1828,2787,1935],{"class":1850},[1828,2789,2790],{"class":1967},"\"Status\"",[1828,2792,2793],{"class":1850},"]",[1828,2795,2796],{"class":1913}," =",[1828,2798,2799],{"class":1967}," \"confirmed\"\n",[1828,2801,2803],{"class":1830,"line":2802},32,[1828,2804,1907],{"emptyLinePlaceholder":1906},[1828,2806,2808],{"class":1830,"line":2807},33,[1828,2809,2810],{"class":1833},"    // Reconstruct\n",[1828,2812,2814,2817,2819,2821,2823,2825,2827,2829,2831,2833],{"class":1830,"line":2813},34,[1828,2815,2816],{"class":1913},"    restored",[1828,2818,1917],{"class":1850},[1828,2820,1920],{"class":1913},[1828,2822,1923],{"class":1913},[1828,2824,2021],{"class":1913},[1828,2826,1929],{"class":1850},[1828,2828,257],{"class":1932},[1828,2830,2028],{"class":1850},[1828,2832,1740],{"class":1913},[1828,2834,2033],{"class":1850},[1828,2836,2838,2840,2842,2844,2846,2849,2851,2853,2855,2857,2860,2862,2865,2867],{"class":1830,"line":2837},35,[1828,2839,2212],{"class":1913},[1828,2841,1929],{"class":1850},[1828,2843,2697],{"class":1932},[1828,2845,2028],{"class":1850},[1828,2847,2848],{"class":1967},"\"Status: ",[1828,2850,2706],{"class":2705},[1828,2852,2710],{"class":2709},[1828,2854,2713],{"class":1967},[1828,2856,1917],{"class":1850},[1828,2858,2859],{"class":1913}," restored",[1828,2861,1929],{"class":1850},[1828,2863,2864],{"class":1913},"Status",[1828,2866,2067],{"class":1850},[1828,2868,2869],{"class":1833}," // \"confirmed\"\n",[1828,2871,2873],{"class":1830,"line":2872},36,[1828,2874,1900],{"class":1850},[1811,2876,2878],{"id":2877},"capabilities","Capabilities",[2880,2881,2882,2898],"table",{},[2883,2884,2885],"thead",{},[2886,2887,2888,2892,2895],"tr",{},[2889,2890,2891],"th",{},"Feature",[2889,2893,2894],{},"Description",[2889,2896,2897],{},"Docs",[2899,2900,2901,2916,2937,2954,2967,2979,2997],"tbody",{},[2886,2902,2903,2907,2910],{},[2904,2905,2906],"td",{},"Type Segregation",[2904,2908,2909],{},"Strings, ints, floats, bools, times, bytes in separate typed maps",[2904,2911,2912],{},[1740,2913,2915],{"href":2914},"docs/learn/concepts","Concepts",[2886,2917,2918,2921,2932],{},[2904,2919,2920],{},"Nullable Fields",[2904,2922,2923,2924,2927,2928,2931],{},"Pointer types (",[1825,2925,2926],{},"*string",", ",[1825,2929,2930],{},"*int64",") with explicit nil handling",[2904,2933,2934],{},[1740,2935,83],{"href":2936},"docs/guides/basic-usage",[2886,2938,2939,2941,2950],{},[2904,2940,853],{},[2904,2942,2943,2927,2946,2949],{},[1825,2944,2945],{},"[]string",[1825,2947,2948],{},"[]int64",", etc. preserved as typed slices",[2904,2951,2952],{},[1740,2953,83],{"href":2936},[2886,2955,2956,2959,2962],{},[2904,2957,2958],{},"Nested Composition",[2904,2960,2961],{},"Embed atoms within atoms for complex object graphs",[2904,2963,2964],{},[1740,2965,526],{"href":2966},"docs/guides/nested-structs",[2886,2968,2969,2971,2974],{},[2904,2970,345],{},[2904,2972,2973],{},"Query fields, tables, and type metadata via Spec",[2904,2975,2976],{},[1740,2977,1141],{"href":2978},"docs/reference/api",[2886,2980,2981,2984,2992],{},[2904,2982,2983],{},"Custom Implementations",[2904,2985,2986,2988,2989,2991],{},[1825,2987,1268],{},"/",[1825,2990,1273],{}," interfaces bypass reflection",[2904,2993,2994],{},[1740,2995,617],{"href":2996},"docs/guides/interfaces",[2886,2998,2999,3001,3004],{},[2904,3000,689],{},[2904,3002,3003],{},"Generate implementations for zero-reflection paths",[2904,3005,3006],{},[1740,3007,689],{"href":3008},"docs/cookbook/codegen",[1811,3010,3012],{"id":3011},"why-atom","Why atom?",[3014,3015,3016,3028,3034,3040,3046],"ul",{},[3017,3018,3019,3023,3024,3027],"li",{},[3020,3021,3022],"strong",{},"Type-safe without T"," — Libraries work with typed maps, not ",[1825,3025,3026],{},"any"," or reflection",[3017,3029,3030,3033],{},[3020,3031,3032],{},"Field-level control"," — Read, write, transform individual fields programmatically",[3017,3035,3036,3039],{},[3020,3037,3038],{},"Decoupled"," — Infrastructure code never imports user types",[3017,3041,3042,3045],{},[3020,3043,3044],{},"Zero reflection path"," — Implement interfaces or use codegen for production performance",[3017,3047,3048,3051],{},[3020,3049,3050],{},"Sentinel integration"," — Automatic field discovery and metadata extraction",[1811,3053,3055],{"id":3054},"the-typed-bridge","The Typed Bridge",[1737,3057,3058,3059,1929],{},"Atom enables a pattern: ",[3020,3060,3061],{},"user code owns types, infrastructure owns behaviour",[1737,3063,3064],{},"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).",[1819,3066,3068],{"className":1821,"code":3067,"language":1823,"meta":35,"style":35},"// Your domain package defines types\ntype User struct { ... }\ntype Order struct { ... }\n\n// Storage library accepts atoms — never sees User or Order\nfunc (s *Store) Put(a *atom.Atom) error { ... }\nfunc (s *Store) Get(spec atom.Spec, id string) (*atom.Atom, error) { ... }\n\n// Your code bridges the two\na := userAtomizer.Atomize(user)\nstore.Put(a)\n",[1825,3069,3070,3075,3092,3106,3110,3115,3159,3219,3223,3228,3247],{"__ignoreMap":35},[1828,3071,3072],{"class":1830,"line":9},[1828,3073,3074],{"class":1833},"// Your domain package defines types\n",[1828,3076,3077,3079,3081,3083,3086,3089],{"class":1830,"line":20},[1828,3078,1840],{"class":1839},[1828,3080,1844],{"class":1843},[1828,3082,1847],{"class":1839},[1828,3084,3085],{"class":1850}," {",[1828,3087,3088],{"class":1952}," ...",[1828,3090,3091],{"class":1850}," }\n",[1828,3093,3094,3096,3098,3100,3102,3104],{"class":1830,"line":41},[1828,3095,1840],{"class":1839},[1828,3097,2488],{"class":1843},[1828,3099,1847],{"class":1839},[1828,3101,3085],{"class":1850},[1828,3103,3088],{"class":1952},[1828,3105,3091],{"class":1850},[1828,3107,3108],{"class":1830,"line":1190},[1828,3109,1907],{"emptyLinePlaceholder":1906},[1828,3111,3112],{"class":1830,"line":1195},[1828,3113,3114],{"class":1833},"// Storage library accepts atoms — never sees User or Order\n",[1828,3116,3117,3119,3122,3125,3128,3131,3133,3136,3138,3140,3142,3144,3146,3148,3150,3153,3155,3157],{"class":1830,"line":1879},[1828,3118,2173],{"class":1839},[1828,3120,3121],{"class":1850}," (",[1828,3123,3124],{"class":2181},"s ",[1828,3126,3127],{"class":1952},"*",[1828,3129,3130],{"class":1843},"Store",[1828,3132,2067],{"class":1850},[1828,3134,3135],{"class":1932}," Put",[1828,3137,2028],{"class":1850},[1828,3139,1740],{"class":2181},[1828,3141,2184],{"class":1952},[1828,3143,1735],{"class":1843},[1828,3145,1929],{"class":1850},[1828,3147,12],{"class":1843},[1828,3149,2067],{"class":1850},[1828,3151,3152],{"class":1843}," error",[1828,3154,3085],{"class":1850},[1828,3156,3088],{"class":1952},[1828,3158,3091],{"class":1850},[1828,3160,3161,3163,3165,3167,3169,3171,3173,3176,3178,3181,3183,3185,3187,3189,3192,3195,3197,3199,3201,3203,3205,3207,3209,3211,3213,3215,3217],{"class":1830,"line":1888},[1828,3162,2173],{"class":1839},[1828,3164,3121],{"class":1850},[1828,3166,3124],{"class":2181},[1828,3168,3127],{"class":1952},[1828,3170,3130],{"class":1843},[1828,3172,2067],{"class":1850},[1828,3174,3175],{"class":1932}," Get",[1828,3177,2028],{"class":1850},[1828,3179,3180],{"class":2181},"spec",[1828,3182,1926],{"class":1843},[1828,3184,1929],{"class":1850},[1828,3186,1207],{"class":1843},[1828,3188,1917],{"class":1850},[1828,3190,3191],{"class":2181}," id",[1828,3193,3194],{"class":1843}," string",[1828,3196,2067],{"class":1850},[1828,3198,3121],{"class":1850},[1828,3200,3127],{"class":1952},[1828,3202,1735],{"class":1843},[1828,3204,1929],{"class":1850},[1828,3206,12],{"class":1843},[1828,3208,1917],{"class":1850},[1828,3210,3152],{"class":1843},[1828,3212,2067],{"class":1850},[1828,3214,3085],{"class":1850},[1828,3216,3088],{"class":1952},[1828,3218,3091],{"class":1850},[1828,3220,3221],{"class":1830,"line":1897},[1828,3222,1907],{"emptyLinePlaceholder":1906},[1828,3224,3225],{"class":1830,"line":1903},[1828,3226,3227],{"class":1833},"// Your code bridges the two\n",[1828,3229,3230,3232,3234,3237,3239,3241,3243,3245],{"class":1830,"line":1910},[1828,3231,1740],{"class":1913},[1828,3233,1923],{"class":1913},[1828,3235,3236],{"class":1913}," userAtomizer",[1828,3238,1929],{"class":1850},[1828,3240,252],{"class":1932},[1828,3242,2028],{"class":1850},[1828,3244,1947],{"class":1913},[1828,3246,2033],{"class":1850},[1828,3248,3249,3252,3254,3257,3259,3261],{"class":1830,"line":1944},[1828,3250,3251],{"class":1913},"store",[1828,3253,1929],{"class":1850},[1828,3255,3256],{"class":1932},"Put",[1828,3258,2028],{"class":1850},[1828,3260,1740],{"class":1913},[1828,3262,2033],{"class":1850},[1737,3264,3265],{},"The contract is the Atom structure. The types stay where they belong.",[1811,3267,3269],{"id":3268},"documentation","Documentation",[3271,3272,1651],"h3",{"id":3273},"learn",[3014,3275,3276,3282,3287],{},[3017,3277,3278,3281],{},[1740,3279,69],{"href":3280},"docs/learn/quickstart"," — Get started in 5 minutes",[3017,3283,3284,3286],{},[1740,3285,122],{"href":2914}," — Atoms, tables, specs",[3017,3288,3289,3292],{},[1740,3290,190],{"href":3291},"docs/learn/architecture"," — Internal design",[3271,3294,1663],{"id":3295},"guides",[3014,3297,3298,3303,3309,3314,3319],{},[3017,3299,3300,3302],{},[1740,3301,83],{"href":2936}," — Common patterns",[3017,3304,3305,3308],{},[1740,3306,429],{"href":3307},"docs/guides/custom-types"," — Named types and enums",[3017,3310,3311,3313],{},[1740,3312,526],{"href":2966}," — Composition",[3017,3315,3316,3318],{},[1740,3317,617],{"href":2996}," — Custom serialization",[3017,3320,3321,3324],{},[1740,3322,716],{"href":3323},"docs/guides/testing"," — Test strategies",[3271,3326,1678],{"id":3327},"cookbook",[3014,3329,3330,3335,3341],{},[3017,3331,3332,3334],{},[1740,3333,689],{"href":3008}," — Eliminating reflection",[3017,3336,3337,3340],{},[1740,3338,902],{"href":3339},"docs/cookbook/serialization"," — Encoding atoms",[3017,3342,3343,3347],{},[1740,3344,3346],{"href":3345},"docs/cookbook/migrations","Migrations"," — Schema evolution",[3271,3349,1689],{"id":3350},"reference",[3014,3352,3353,3358,3364],{},[3017,3354,3355,3357],{},[1740,3356,1141],{"href":2978}," — Complete API",[3017,3359,3360,3363],{},[1740,3361,1303],{"href":3362},"docs/reference/tables"," — All table types",[3017,3365,3366,3369],{},[1740,3367,1511],{"href":3368},"docs/reference/testing"," — Test utilities",[1811,3371,3373],{"id":3372},"contributing","Contributing",[1737,3375,3376,3377,3381,3382,3385],{},"See ",[1740,3378,3380],{"href":3379},"CONTRIBUTING","CONTRIBUTING.md"," for guidelines. Run ",[1825,3383,3384],{},"make help"," for available commands.",[1811,3387,1787],{"id":3388},"license",[1737,3390,3391,3392,3394],{},"MIT License — see ",[1740,3393,1784],{"href":1784}," for details.",[3396,3397,3398],"style",{},"html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}",{"title":35,"searchDepth":20,"depth":20,"links":3400},[3401,3402,3403,3404,3405,3406,3407,3413,3414],{"id":1813,"depth":20,"text":1814},{"id":2412,"depth":20,"text":2413},{"id":2436,"depth":20,"text":2437},{"id":2877,"depth":20,"text":2878},{"id":3011,"depth":20,"text":3012},{"id":3054,"depth":20,"text":3055},{"id":3268,"depth":20,"text":3269,"children":3408},[3409,3410,3411,3412],{"id":3273,"depth":41,"text":1651},{"id":3295,"depth":41,"text":1663},{"id":3327,"depth":41,"text":1678},{"id":3350,"depth":41,"text":1689},{"id":3372,"depth":20,"text":3373},{"id":3388,"depth":20,"text":1787},"md","book-open",{},"/readme",{"title":1728,"description":35},"readme","OTWGeloGLApgRGlrJrJeVNhy_rTzEg7VhkuTNvTgtkA",{"id":3423,"title":3424,"body":3425,"description":35,"extension":3415,"icon":3571,"meta":3572,"navigation":1906,"path":3573,"seo":3574,"stem":3575,"__hash__":3576},"resources/security.md","Security",{"type":1730,"value":3426,"toc":3564},[3427,3431,3435,3457,3461,3464,3493,3497,3500,3533,3537,3540,3557,3561],[1733,3428,3430],{"id":3429},"security-policy","Security Policy",[1811,3432,3434],{"id":3433},"supported-versions","Supported Versions",[2880,3436,3437,3447],{},[2883,3438,3439],{},[2886,3440,3441,3444],{},[2889,3442,3443],{},"Version",[2889,3445,3446],{},"Supported",[2899,3448,3449],{},[2886,3450,3451,3454],{},[2904,3452,3453],{},"Latest",[2904,3455,3456],{},"✅",[1811,3458,3460],{"id":3459},"reporting-a-vulnerability","Reporting a Vulnerability",[1737,3462,3463],{},"We take security vulnerabilities seriously. If you discover a security issue, please follow these steps:",[3465,3466,3467,3473,3476],"ol",{},[3017,3468,3469,3472],{},[3020,3470,3471],{},"DO NOT"," create a public GitHub issue",[3017,3474,3475],{},"Email security details to the maintainers",[3017,3477,3478,3479],{},"Include:\n",[3014,3480,3481,3484,3487,3490],{},[3017,3482,3483],{},"Description of the vulnerability",[3017,3485,3486],{},"Steps to reproduce",[3017,3488,3489],{},"Potential impact",[3017,3491,3492],{},"Suggested fix (if available)",[1811,3494,3496],{"id":3495},"security-best-practices","Security Best Practices",[1737,3498,3499],{},"When using Atom:",[3465,3501,3502,3511,3521,3527],{},[3017,3503,3504,3506,3507,3510],{},[3020,3505,684],{},": Always implement the ",[1825,3508,3509],{},"Validator"," interface to validate data before atomization. This prevents malformed data from entering your storage layer.",[3017,3512,3513,3516,3517,3520],{},[3020,3514,3515],{},"ID Security",": The ",[1825,3518,3519],{},"Atoms.ID"," field is used as a key identifier. Ensure IDs don't contain sensitive information and are properly sanitized.",[3017,3522,3523,3526],{},[3020,3524,3525],{},"Encoding",": The encoding utilities are designed for internal use. When exposing data externally, consider additional encryption or encoding as needed.",[3017,3528,3529,3532],{},[3020,3530,3531],{},"Field Exposure",": Be aware that field names and types are discoverable via metadata. Review your struct definitions for information disclosure.",[1811,3534,3536],{"id":3535},"security-features","Security Features",[1737,3538,3539],{},"Atom is designed with security in mind:",[3014,3541,3542,3545,3548,3551,3554],{},[3017,3543,3544],{},"Minimal dependencies (only sentinel)",[3017,3546,3547],{},"No network operations",[3017,3549,3550],{},"No file system operations",[3017,3552,3553],{},"Validation hooks before atomization",[3017,3555,3556],{},"Type-safe generics prevent runtime type errors",[1811,3558,3560],{"id":3559},"acknowledgments","Acknowledgments",[1737,3562,3563],{},"We appreciate responsible disclosure of security vulnerabilities.",{"title":35,"searchDepth":20,"depth":20,"links":3565},[3566,3567,3568,3569,3570],{"id":3433,"depth":20,"text":3434},{"id":3459,"depth":20,"text":3460},{"id":3495,"depth":20,"text":3496},{"id":3535,"depth":20,"text":3536},{"id":3559,"depth":20,"text":3560},"shield",{},"/security",{"title":3424,"description":35},"security","1s64yRTVAddXdEKjtZim6ifO6dszgk4gzpT1MdJH3sw",{"id":3578,"title":3373,"body":3579,"description":3587,"extension":3415,"icon":1825,"meta":3845,"navigation":1906,"path":3846,"seo":3847,"stem":3372,"__hash__":3848},"resources/contributing.md",{"type":1730,"value":3580,"toc":3833},[3581,3585,3588,3592,3630,3634,3638,3649,3653,3709,3713,3733,3737,3754,3758,3772,3776,3779,3823,3827,3830],[1733,3582,3584],{"id":3583},"contributing-to-atom","Contributing to Atom",[1737,3586,3587],{},"Thank you for your interest in contributing to Atom! We welcome contributions from the community.",[1811,3589,3591],{"id":3590},"getting-started","Getting Started",[3465,3593,3594,3597,3603,3609,3612,3618,3624,3627],{},[3017,3595,3596],{},"Fork the repository",[3017,3598,3599,3600],{},"Clone your fork: ",[1825,3601,3602],{},"git clone https://github.com/your-username/atom.git",[3017,3604,3605,3606],{},"Create a new branch: ",[1825,3607,3608],{},"git checkout -b feature/your-feature-name",[3017,3610,3611],{},"Make your changes",[3017,3613,3614,3615],{},"Run tests: ",[1825,3616,3617],{},"make test",[3017,3619,3620,3621],{},"Run linters: ",[1825,3622,3623],{},"make lint",[3017,3625,3626],{},"Commit your changes with a descriptive message",[3017,3628,3629],{},"Push to your fork and submit a pull request",[1811,3631,3633],{"id":3632},"development-setup","Development Setup",[3271,3635,3637],{"id":3636},"prerequisites","Prerequisites",[3014,3639,3640,3643],{},[3017,3641,3642],{},"Go 1.23 or higher",[3017,3644,3645,3646,2067],{},"golangci-lint (install with ",[1825,3647,3648],{},"make install-tools",[3271,3650,3652],{"id":3651},"running-tests","Running Tests",[1819,3654,3656],{"className":2416,"code":3655,"language":2418,"meta":35,"style":35},"make test        # Run all tests\nmake bench       # Run benchmarks\nmake coverage    # Generate coverage report\nmake lint        # Run linters\nmake check       # Run tests and lint\n",[1825,3657,3658,3669,3679,3689,3699],{"__ignoreMap":35},[1828,3659,3660,3663,3666],{"class":1830,"line":9},[1828,3661,3662],{"class":1932},"make",[1828,3664,3665],{"class":1967}," test",[1828,3667,3668],{"class":1833},"        # Run all tests\n",[1828,3670,3671,3673,3676],{"class":1830,"line":20},[1828,3672,3662],{"class":1932},[1828,3674,3675],{"class":1967}," bench",[1828,3677,3678],{"class":1833},"       # Run benchmarks\n",[1828,3680,3681,3683,3686],{"class":1830,"line":41},[1828,3682,3662],{"class":1932},[1828,3684,3685],{"class":1967}," coverage",[1828,3687,3688],{"class":1833},"    # Generate coverage report\n",[1828,3690,3691,3693,3696],{"class":1830,"line":1190},[1828,3692,3662],{"class":1932},[1828,3694,3695],{"class":1967}," lint",[1828,3697,3698],{"class":1833},"        # Run linters\n",[1828,3700,3701,3703,3706],{"class":1830,"line":1195},[1828,3702,3662],{"class":1932},[1828,3704,3705],{"class":1967}," check",[1828,3707,3708],{"class":1833},"       # Run tests and lint\n",[1811,3710,3712],{"id":3711},"code-style","Code Style",[3014,3714,3715,3718,3724,3727,3730],{},[3017,3716,3717],{},"Follow standard Go conventions",[3017,3719,3720,3721],{},"Ensure all code passes ",[1825,3722,3723],{},"golangci-lint",[3017,3725,3726],{},"Write tests for new functionality",[3017,3728,3729],{},"Keep test coverage above 90%",[3017,3731,3732],{},"Document exported functions and types",[1811,3734,3736],{"id":3735},"pull-request-process","Pull Request Process",[3465,3738,3739,3742,3745,3748,3751],{},[3017,3740,3741],{},"Ensure all tests pass",[3017,3743,3744],{},"Update documentation if needed",[3017,3746,3747],{},"Add entries to CHANGELOG.md if applicable",[3017,3749,3750],{},"Ensure your PR description clearly describes the problem and solution",[3017,3752,3753],{},"Link any relevant issues",[1811,3755,3757],{"id":3756},"testing-guidelines","Testing Guidelines",[3014,3759,3760,3763,3766,3769],{},[3017,3761,3762],{},"Each source file should have a corresponding test file",[3017,3764,3765],{},"Write both positive and negative test cases",[3017,3767,3768],{},"Use table-driven tests where appropriate",[3017,3770,3771],{},"Ensure tests are deterministic and don't depend on external services",[1811,3773,3775],{"id":3774},"commit-message-format","Commit Message Format",[1737,3777,3778],{},"Use conventional commits format:",[3014,3780,3781,3787,3793,3799,3805,3811,3817],{},[3017,3782,3783,3786],{},[1825,3784,3785],{},"feat:"," New feature",[3017,3788,3789,3792],{},[1825,3790,3791],{},"fix:"," Bug fix",[3017,3794,3795,3798],{},[1825,3796,3797],{},"docs:"," Documentation changes",[3017,3800,3801,3804],{},[1825,3802,3803],{},"test:"," Test additions or changes",[3017,3806,3807,3810],{},[1825,3808,3809],{},"refactor:"," Code refactoring",[3017,3812,3813,3816],{},[1825,3814,3815],{},"perf:"," Performance improvements",[3017,3818,3819,3822],{},[1825,3820,3821],{},"chore:"," Maintenance tasks",[1811,3824,3826],{"id":3825},"questions","Questions?",[1737,3828,3829],{},"Feel free to open an issue for any questions or concerns.",[3396,3831,3832],{},"html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":35,"searchDepth":20,"depth":20,"links":3834},[3835,3836,3840,3841,3842,3843,3844],{"id":3590,"depth":20,"text":3591},{"id":3632,"depth":20,"text":3633,"children":3837},[3838,3839],{"id":3636,"depth":41,"text":3637},{"id":3651,"depth":41,"text":3652},{"id":3711,"depth":20,"text":3712},{"id":3735,"depth":20,"text":3736},{"id":3756,"depth":20,"text":3757},{"id":3774,"depth":20,"text":3775},{"id":3825,"depth":20,"text":3826},{},"/contributing",{"title":3373,"description":3587},"_LkaroTHXcmbW0p8MeBOczTAHGiWsHi8FVotI4zm-FQ",{"id":3850,"title":902,"author":3851,"body":3852,"description":904,"extension":3415,"meta":7108,"navigation":1906,"path":901,"published":7109,"readtime":7110,"seo":7111,"stem":1685,"tags":7112,"updated":7114,"__hash__":7115},"atom/v1.0.2/4.cookbook/2.serialization.md","zoobzio",{"type":1730,"value":3853,"toc":7082},[3854,3857,3859,3862,3864,3867,3870,3873,4025,4028,4130,4133,4136,4381,4384,4387,4476,4479,4482,4533,4536,4777,4780,4783,4786,5239,5242,5245,5432,5435,5438,5441,5553,5783,5786,5891,5894,5897,6306,6309,6312,6541,6544,6547,6726,6729,6732,6875,6878,7061,7064,7079],[1733,3855,902],{"id":3856},"serialization",[1737,3858,908],{},[1811,3860,6],{"id":3861},"overview",[1737,3863,912],{},[1811,3865,915],{"id":3866},"json-encoding",[3271,3868,919],{"id":3869},"direct-marshaling",[1737,3871,3872],{},"Atoms can be marshaled directly to JSON:",[1819,3874,3876],{"className":1821,"code":3875,"language":1823,"meta":35,"style":35},"import \"encoding/json\"\n\natomizer, _ := atom.Use[User]()\nuser := &User{Name: \"Alice\", Age: 30}\na := atomizer.Atomize(user)\n\ndata, err := json.Marshal(a)\nif err != nil {\n    log.Fatal(err)\n}\n// {\"Strings\":{\"Name\":\"Alice\"},\"Ints\":{\"Age\":30},...}\n",[1825,3877,3878,3885,3889,3911,3940,3958,3962,3987,4000,4016,4020],{"__ignoreMap":35},[1828,3879,3880,3882],{"class":1830,"line":9},[1828,3881,2459],{"class":1839},[1828,3883,3884],{"class":1967}," \"encoding/json\"\n",[1828,3886,3887],{"class":1830,"line":20},[1828,3888,1907],{"emptyLinePlaceholder":1906},[1828,3890,3891,3893,3895,3897,3899,3901,3903,3905,3907,3909],{"class":1830,"line":41},[1828,3892,1914],{"class":1913},[1828,3894,1917],{"class":1850},[1828,3896,1920],{"class":1913},[1828,3898,1923],{"class":1913},[1828,3900,1926],{"class":1913},[1828,3902,1929],{"class":1850},[1828,3904,1159],{"class":1932},[1828,3906,1935],{"class":1850},[1828,3908,1938],{"class":1843},[1828,3910,1941],{"class":1850},[1828,3912,3913,3915,3917,3919,3921,3923,3926,3928,3930,3932,3934,3936,3938],{"class":1830,"line":1190},[1828,3914,1947],{"class":1913},[1828,3916,1923],{"class":1913},[1828,3918,1953],{"class":1952},[1828,3920,1938],{"class":1843},[1828,3922,1958],{"class":1850},[1828,3924,3925],{"class":1856},"Name",[1828,3927,1964],{"class":1850},[1828,3929,1978],{"class":1967},[1828,3931,1917],{"class":1850},[1828,3933,1983],{"class":1856},[1828,3935,1964],{"class":1850},[1828,3937,1989],{"class":1988},[1828,3939,1900],{"class":1850},[1828,3941,3942,3944,3946,3948,3950,3952,3954,3956],{"class":1830,"line":1195},[1828,3943,1740],{"class":1913},[1828,3945,1923],{"class":1913},[1828,3947,2021],{"class":1913},[1828,3949,1929],{"class":1850},[1828,3951,252],{"class":1932},[1828,3953,2028],{"class":1850},[1828,3955,1947],{"class":1913},[1828,3957,2033],{"class":1850},[1828,3959,3960],{"class":1830,"line":1879},[1828,3961,1907],{"emptyLinePlaceholder":1906},[1828,3963,3964,3967,3969,3971,3973,3976,3978,3981,3983,3985],{"class":1830,"line":1888},[1828,3965,3966],{"class":1913},"data",[1828,3968,1917],{"class":1850},[1828,3970,2550],{"class":1913},[1828,3972,1923],{"class":1913},[1828,3974,3975],{"class":1913}," json",[1828,3977,1929],{"class":1850},[1828,3979,3980],{"class":1932},"Marshal",[1828,3982,2028],{"class":1850},[1828,3984,1740],{"class":1913},[1828,3986,2033],{"class":1850},[1828,3988,3989,3992,3994,3996,3998],{"class":1830,"line":1897},[1828,3990,3991],{"class":1952},"if",[1828,3993,2550],{"class":1913},[1828,3995,2575],{"class":1952},[1828,3997,2578],{"class":1839},[1828,3999,1851],{"class":1850},[1828,4001,4002,4005,4007,4010,4012,4014],{"class":1830,"line":1903},[1828,4003,4004],{"class":1913},"    log",[1828,4006,1929],{"class":1850},[1828,4008,4009],{"class":1932},"Fatal",[1828,4011,2028],{"class":1850},[1828,4013,2591],{"class":1913},[1828,4015,2033],{"class":1850},[1828,4017,4018],{"class":1830,"line":1910},[1828,4019,1900],{"class":1850},[1828,4021,4022],{"class":1830,"line":1944},[1828,4023,4024],{"class":1833},"// {\"Strings\":{\"Name\":\"Alice\"},\"Ints\":{\"Age\":30},...}\n",[3271,4026,924],{"id":4027},"unmarshaling",[1819,4029,4031],{"className":1821,"code":4030,"language":1823,"meta":35,"style":35},"var restored atom.Atom\nif err := json.Unmarshal(data, &restored); err != nil {\n    log.Fatal(err)\n}\n\nuser, err := atomizer.Deatomize(&restored)\n",[1825,4032,4033,4047,4083,4097,4101,4105],{"__ignoreMap":35},[1828,4034,4035,4038,4040,4042,4044],{"class":1830,"line":9},[1828,4036,4037],{"class":1839},"var",[1828,4039,2859],{"class":1913},[1828,4041,1926],{"class":1843},[1828,4043,1929],{"class":1850},[1828,4045,4046],{"class":1843},"Atom\n",[1828,4048,4049,4051,4053,4055,4057,4059,4062,4064,4066,4068,4070,4072,4075,4077,4079,4081],{"class":1830,"line":20},[1828,4050,3991],{"class":1952},[1828,4052,2550],{"class":1913},[1828,4054,1923],{"class":1913},[1828,4056,3975],{"class":1913},[1828,4058,1929],{"class":1850},[1828,4060,4061],{"class":1932},"Unmarshal",[1828,4063,2028],{"class":1850},[1828,4065,3966],{"class":1913},[1828,4067,1917],{"class":1850},[1828,4069,1953],{"class":1952},[1828,4071,2137],{"class":1913},[1828,4073,4074],{"class":1850},");",[1828,4076,2550],{"class":1913},[1828,4078,2575],{"class":1952},[1828,4080,2578],{"class":1839},[1828,4082,1851],{"class":1850},[1828,4084,4085,4087,4089,4091,4093,4095],{"class":1830,"line":41},[1828,4086,4004],{"class":1913},[1828,4088,1929],{"class":1850},[1828,4090,4009],{"class":1932},[1828,4092,2028],{"class":1850},[1828,4094,2591],{"class":1913},[1828,4096,2033],{"class":1850},[1828,4098,4099],{"class":1830,"line":1190},[1828,4100,1900],{"class":1850},[1828,4102,4103],{"class":1830,"line":1195},[1828,4104,1907],{"emptyLinePlaceholder":1906},[1828,4106,4107,4109,4111,4113,4115,4117,4119,4121,4123,4126,4128],{"class":1830,"line":1879},[1828,4108,1947],{"class":1913},[1828,4110,1917],{"class":1850},[1828,4112,2550],{"class":1913},[1828,4114,1923],{"class":1913},[1828,4116,2021],{"class":1913},[1828,4118,1929],{"class":1850},[1828,4120,257],{"class":1932},[1828,4122,2028],{"class":1850},[1828,4124,4125],{"class":1952},"&",[1828,4127,2137],{"class":1913},[1828,4129,2033],{"class":1850},[3271,4131,929],{"id":4132},"compact-json",[1737,4134,4135],{},"For smaller payloads, omit empty maps:",[1819,4137,4139],{"className":1821,"code":4138,"language":1823,"meta":35,"style":35},"type CompactAtom struct {\n    Spec         atom.Spec              `json:\"spec,omitempty\"`\n    Strings      map[string]string      `json:\"s,omitempty\"`\n    Ints         map[string]int64       `json:\"i,omitempty\"`\n    Floats       map[string]float64     `json:\"f,omitempty\"`\n    Bools        map[string]bool        `json:\"b,omitempty\"`\n    // ... other fields\n}\n\nfunc ToCompact(a *atom.Atom) CompactAtom {\n    return CompactAtom{\n        Spec:    a.Spec,\n        Strings: a.Strings,\n        Ints:    a.Ints,\n        Floats:  a.Floats,\n        Bools:   a.Bools,\n    }\n}\n",[1825,4140,4141,4152,4167,4187,4207,4227,4247,4252,4256,4260,4285,4294,4310,4325,4340,4356,4373,4377],{"__ignoreMap":35},[1828,4142,4143,4145,4148,4150],{"class":1830,"line":9},[1828,4144,1840],{"class":1839},[1828,4146,4147],{"class":1843}," CompactAtom",[1828,4149,1847],{"class":1839},[1828,4151,1851],{"class":1850},[1828,4153,4154,4157,4160,4162,4164],{"class":1830,"line":20},[1828,4155,4156],{"class":1856},"    Spec",[1828,4158,4159],{"class":1843},"         atom",[1828,4161,1929],{"class":1850},[1828,4163,1207],{"class":1843},[1828,4165,4166],{"class":1967},"              `json:\"spec,omitempty\"`\n",[1828,4168,4169,4172,4175,4177,4180,4182,4184],{"class":1830,"line":41},[1828,4170,4171],{"class":1856},"    Strings",[1828,4173,4174],{"class":1839},"      map",[1828,4176,1935],{"class":1850},[1828,4178,4179],{"class":1843},"string",[1828,4181,2793],{"class":1850},[1828,4183,4179],{"class":1843},[1828,4185,4186],{"class":1967},"      `json:\"s,omitempty\"`\n",[1828,4188,4189,4192,4195,4197,4199,4201,4204],{"class":1830,"line":1190},[1828,4190,4191],{"class":1856},"    Ints",[1828,4193,4194],{"class":1839},"         map",[1828,4196,1935],{"class":1850},[1828,4198,4179],{"class":1843},[1828,4200,2793],{"class":1850},[1828,4202,4203],{"class":1843},"int64",[1828,4205,4206],{"class":1967},"       `json:\"i,omitempty\"`\n",[1828,4208,4209,4212,4215,4217,4219,4221,4224],{"class":1830,"line":1195},[1828,4210,4211],{"class":1856},"    Floats",[1828,4213,4214],{"class":1839},"       map",[1828,4216,1935],{"class":1850},[1828,4218,4179],{"class":1843},[1828,4220,2793],{"class":1850},[1828,4222,4223],{"class":1843},"float64",[1828,4225,4226],{"class":1967},"     `json:\"f,omitempty\"`\n",[1828,4228,4229,4232,4235,4237,4239,4241,4244],{"class":1830,"line":1879},[1828,4230,4231],{"class":1856},"    Bools",[1828,4233,4234],{"class":1839},"        map",[1828,4236,1935],{"class":1850},[1828,4238,4179],{"class":1843},[1828,4240,2793],{"class":1850},[1828,4242,4243],{"class":1843},"bool",[1828,4245,4246],{"class":1967},"        `json:\"b,omitempty\"`\n",[1828,4248,4249],{"class":1830,"line":1888},[1828,4250,4251],{"class":1833},"    // ... other fields\n",[1828,4253,4254],{"class":1830,"line":1897},[1828,4255,1900],{"class":1850},[1828,4257,4258],{"class":1830,"line":1903},[1828,4259,1907],{"emptyLinePlaceholder":1906},[1828,4261,4262,4264,4267,4269,4271,4273,4275,4277,4279,4281,4283],{"class":1830,"line":1910},[1828,4263,2173],{"class":1839},[1828,4265,4266],{"class":1932}," ToCompact",[1828,4268,2028],{"class":1850},[1828,4270,1740],{"class":2181},[1828,4272,2184],{"class":1952},[1828,4274,1735],{"class":1843},[1828,4276,1929],{"class":1850},[1828,4278,12],{"class":1843},[1828,4280,2067],{"class":1850},[1828,4282,4147],{"class":1843},[1828,4284,1851],{"class":1850},[1828,4286,4287,4289,4291],{"class":1830,"line":1944},[1828,4288,2399],{"class":1952},[1828,4290,4147],{"class":1843},[1828,4292,4293],{"class":1850},"{\n",[1828,4295,4296,4299,4301,4303,4305,4307],{"class":1830,"line":2014},[1828,4297,4298],{"class":1856},"        Spec",[1828,4300,1964],{"class":1850},[1828,4302,2661],{"class":1913},[1828,4304,1929],{"class":1850},[1828,4306,1207],{"class":1913},[1828,4308,4309],{"class":1850},",\n",[1828,4311,4312,4315,4317,4319,4321,4323],{"class":1830,"line":2036},[1828,4313,4314],{"class":1856},"        Strings",[1828,4316,1964],{"class":1850},[1828,4318,2265],{"class":1913},[1828,4320,1929],{"class":1850},[1828,4322,2270],{"class":1913},[1828,4324,4309],{"class":1850},[1828,4326,4327,4330,4332,4334,4336,4338],{"class":1830,"line":2041},[1828,4328,4329],{"class":1856},"        Ints",[1828,4331,1964],{"class":1850},[1828,4333,2661],{"class":1913},[1828,4335,1929],{"class":1850},[1828,4337,2319],{"class":1913},[1828,4339,4309],{"class":1850},[1828,4341,4342,4345,4347,4350,4352,4354],{"class":1830,"line":2047},[1828,4343,4344],{"class":1856},"        Floats",[1828,4346,1964],{"class":1850},[1828,4348,4349],{"class":1913},"  a",[1828,4351,1929],{"class":1850},[1828,4353,2365],{"class":1913},[1828,4355,4309],{"class":1850},[1828,4357,4358,4361,4363,4366,4368,4371],{"class":1830,"line":2073},[1828,4359,4360],{"class":1856},"        Bools",[1828,4362,1964],{"class":1850},[1828,4364,4365],{"class":1913},"   a",[1828,4367,1929],{"class":1850},[1828,4369,4370],{"class":1913},"Bools",[1828,4372,4309],{"class":1850},[1828,4374,4375],{"class":1830,"line":2098},[1828,4376,2298],{"class":1850},[1828,4378,4379],{"class":1830,"line":2123},[1828,4380,1900],{"class":1850},[1811,4382,934],{"id":4383},"messagepack",[1737,4385,4386],{},"For binary efficiency:",[1819,4388,4390],{"className":1821,"code":4389,"language":1823,"meta":35,"style":35},"import \"github.com/vmihailenco/msgpack/v5\"\n\n// Encode\ndata, err := msgpack.Marshal(a)\n\n// Decode\nvar restored atom.Atom\nerr = msgpack.Unmarshal(data, &restored)\n",[1825,4391,4392,4399,4403,4408,4431,4435,4440,4452],{"__ignoreMap":35},[1828,4393,4394,4396],{"class":1830,"line":9},[1828,4395,2459],{"class":1839},[1828,4397,4398],{"class":1967}," \"github.com/vmihailenco/msgpack/v5\"\n",[1828,4400,4401],{"class":1830,"line":20},[1828,4402,1907],{"emptyLinePlaceholder":1906},[1828,4404,4405],{"class":1830,"line":41},[1828,4406,4407],{"class":1833},"// Encode\n",[1828,4409,4410,4412,4414,4416,4418,4421,4423,4425,4427,4429],{"class":1830,"line":1190},[1828,4411,3966],{"class":1913},[1828,4413,1917],{"class":1850},[1828,4415,2550],{"class":1913},[1828,4417,1923],{"class":1913},[1828,4419,4420],{"class":1913}," msgpack",[1828,4422,1929],{"class":1850},[1828,4424,3980],{"class":1932},[1828,4426,2028],{"class":1850},[1828,4428,1740],{"class":1913},[1828,4430,2033],{"class":1850},[1828,4432,4433],{"class":1830,"line":1195},[1828,4434,1907],{"emptyLinePlaceholder":1906},[1828,4436,4437],{"class":1830,"line":1879},[1828,4438,4439],{"class":1833},"// Decode\n",[1828,4441,4442,4444,4446,4448,4450],{"class":1830,"line":1888},[1828,4443,4037],{"class":1839},[1828,4445,2859],{"class":1913},[1828,4447,1926],{"class":1843},[1828,4449,1929],{"class":1850},[1828,4451,4046],{"class":1843},[1828,4453,4454,4456,4458,4460,4462,4464,4466,4468,4470,4472,4474],{"class":1830,"line":1897},[1828,4455,2591],{"class":1913},[1828,4457,2796],{"class":1913},[1828,4459,4420],{"class":1913},[1828,4461,1929],{"class":1850},[1828,4463,4061],{"class":1932},[1828,4465,2028],{"class":1850},[1828,4467,3966],{"class":1913},[1828,4469,1917],{"class":1850},[1828,4471,1953],{"class":1952},[1828,4473,2137],{"class":1913},[1828,4475,2033],{"class":1850},[1811,4477,939],{"id":4478},"protocol-buffers",[1737,4480,4481],{},"Define a proto schema:",[1819,4483,4487],{"className":4484,"code":4485,"language":4486,"meta":35,"style":35},"language-protobuf shiki shiki-themes","message Atom {\n    map\u003Cstring, string> strings = 1;\n    map\u003Cstring, int64> ints = 2;\n    map\u003Cstring, double> floats = 3;\n    map\u003Cstring, bool> bools = 4;\n    map\u003Cstring, bytes> bytes = 5;\n    map\u003Cstring, Atom> nested = 6;\n    // ... etc\n}\n","protobuf",[1825,4488,4489,4494,4499,4504,4509,4514,4519,4524,4529],{"__ignoreMap":35},[1828,4490,4491],{"class":1830,"line":9},[1828,4492,4493],{},"message Atom {\n",[1828,4495,4496],{"class":1830,"line":20},[1828,4497,4498],{},"    map\u003Cstring, string> strings = 1;\n",[1828,4500,4501],{"class":1830,"line":41},[1828,4502,4503],{},"    map\u003Cstring, int64> ints = 2;\n",[1828,4505,4506],{"class":1830,"line":1190},[1828,4507,4508],{},"    map\u003Cstring, double> floats = 3;\n",[1828,4510,4511],{"class":1830,"line":1195},[1828,4512,4513],{},"    map\u003Cstring, bool> bools = 4;\n",[1828,4515,4516],{"class":1830,"line":1879},[1828,4517,4518],{},"    map\u003Cstring, bytes> bytes = 5;\n",[1828,4520,4521],{"class":1830,"line":1888},[1828,4522,4523],{},"    map\u003Cstring, Atom> nested = 6;\n",[1828,4525,4526],{"class":1830,"line":1897},[1828,4527,4528],{},"    // ... etc\n",[1828,4530,4531],{"class":1830,"line":1903},[1828,4532,1900],{},[1737,4534,4535],{},"Convert between atom and proto:",[1819,4537,4539],{"className":1821,"code":4538,"language":1823,"meta":35,"style":35},"func ToProto(a *atom.Atom) *pb.Atom {\n    return &pb.Atom{\n        Strings: a.Strings,\n        Ints:    a.Ints,\n        Floats:  a.Floats,\n        Bools:   a.Bools,\n        // ...\n    }\n}\n\nfunc FromProto(p *pb.Atom) *atom.Atom {\n    return &atom.Atom{\n        Strings: p.Strings,\n        Ints:    p.Ints,\n        Floats:  p.Floats,\n        Bools:   p.Bools,\n        // ...\n    }\n}\n",[1825,4540,4541,4573,4587,4601,4615,4629,4643,4648,4652,4656,4660,4691,4705,4720,4735,4750,4765,4769,4773],{"__ignoreMap":35},[1828,4542,4543,4545,4548,4550,4552,4554,4556,4558,4560,4562,4564,4567,4569,4571],{"class":1830,"line":9},[1828,4544,2173],{"class":1839},[1828,4546,4547],{"class":1932}," ToProto",[1828,4549,2028],{"class":1850},[1828,4551,1740],{"class":2181},[1828,4553,2184],{"class":1952},[1828,4555,1735],{"class":1843},[1828,4557,1929],{"class":1850},[1828,4559,12],{"class":1843},[1828,4561,2067],{"class":1850},[1828,4563,2184],{"class":1952},[1828,4565,4566],{"class":1843},"pb",[1828,4568,1929],{"class":1850},[1828,4570,12],{"class":1843},[1828,4572,1851],{"class":1850},[1828,4574,4575,4577,4579,4581,4583,4585],{"class":1830,"line":20},[1828,4576,2399],{"class":1952},[1828,4578,1953],{"class":1952},[1828,4580,4566],{"class":1843},[1828,4582,1929],{"class":1850},[1828,4584,12],{"class":1843},[1828,4586,4293],{"class":1850},[1828,4588,4589,4591,4593,4595,4597,4599],{"class":1830,"line":41},[1828,4590,4314],{"class":1856},[1828,4592,1964],{"class":1850},[1828,4594,2265],{"class":1913},[1828,4596,1929],{"class":1850},[1828,4598,2270],{"class":1913},[1828,4600,4309],{"class":1850},[1828,4602,4603,4605,4607,4609,4611,4613],{"class":1830,"line":1190},[1828,4604,4329],{"class":1856},[1828,4606,1964],{"class":1850},[1828,4608,2661],{"class":1913},[1828,4610,1929],{"class":1850},[1828,4612,2319],{"class":1913},[1828,4614,4309],{"class":1850},[1828,4616,4617,4619,4621,4623,4625,4627],{"class":1830,"line":1195},[1828,4618,4344],{"class":1856},[1828,4620,1964],{"class":1850},[1828,4622,4349],{"class":1913},[1828,4624,1929],{"class":1850},[1828,4626,2365],{"class":1913},[1828,4628,4309],{"class":1850},[1828,4630,4631,4633,4635,4637,4639,4641],{"class":1830,"line":1879},[1828,4632,4360],{"class":1856},[1828,4634,1964],{"class":1850},[1828,4636,4365],{"class":1913},[1828,4638,1929],{"class":1850},[1828,4640,4370],{"class":1913},[1828,4642,4309],{"class":1850},[1828,4644,4645],{"class":1830,"line":1888},[1828,4646,4647],{"class":1833},"        // ...\n",[1828,4649,4650],{"class":1830,"line":1897},[1828,4651,2298],{"class":1850},[1828,4653,4654],{"class":1830,"line":1903},[1828,4655,1900],{"class":1850},[1828,4657,4658],{"class":1830,"line":1910},[1828,4659,1907],{"emptyLinePlaceholder":1906},[1828,4661,4662,4664,4667,4669,4671,4673,4675,4677,4679,4681,4683,4685,4687,4689],{"class":1830,"line":1944},[1828,4663,2173],{"class":1839},[1828,4665,4666],{"class":1932}," FromProto",[1828,4668,2028],{"class":1850},[1828,4670,1737],{"class":2181},[1828,4672,2184],{"class":1952},[1828,4674,4566],{"class":1843},[1828,4676,1929],{"class":1850},[1828,4678,12],{"class":1843},[1828,4680,2067],{"class":1850},[1828,4682,2184],{"class":1952},[1828,4684,1735],{"class":1843},[1828,4686,1929],{"class":1850},[1828,4688,12],{"class":1843},[1828,4690,1851],{"class":1850},[1828,4692,4693,4695,4697,4699,4701,4703],{"class":1830,"line":2014},[1828,4694,2399],{"class":1952},[1828,4696,1953],{"class":1952},[1828,4698,1735],{"class":1843},[1828,4700,1929],{"class":1850},[1828,4702,12],{"class":1843},[1828,4704,4293],{"class":1850},[1828,4706,4707,4709,4711,4714,4716,4718],{"class":1830,"line":2036},[1828,4708,4314],{"class":1856},[1828,4710,1964],{"class":1850},[1828,4712,4713],{"class":1913}," p",[1828,4715,1929],{"class":1850},[1828,4717,2270],{"class":1913},[1828,4719,4309],{"class":1850},[1828,4721,4722,4724,4726,4729,4731,4733],{"class":1830,"line":2041},[1828,4723,4329],{"class":1856},[1828,4725,1964],{"class":1850},[1828,4727,4728],{"class":1913},"    p",[1828,4730,1929],{"class":1850},[1828,4732,2319],{"class":1913},[1828,4734,4309],{"class":1850},[1828,4736,4737,4739,4741,4744,4746,4748],{"class":1830,"line":2047},[1828,4738,4344],{"class":1856},[1828,4740,1964],{"class":1850},[1828,4742,4743],{"class":1913},"  p",[1828,4745,1929],{"class":1850},[1828,4747,2365],{"class":1913},[1828,4749,4309],{"class":1850},[1828,4751,4752,4754,4756,4759,4761,4763],{"class":1830,"line":2073},[1828,4753,4360],{"class":1856},[1828,4755,1964],{"class":1850},[1828,4757,4758],{"class":1913},"   p",[1828,4760,1929],{"class":1850},[1828,4762,4370],{"class":1913},[1828,4764,4309],{"class":1850},[1828,4766,4767],{"class":1830,"line":2098},[1828,4768,4647],{"class":1833},[1828,4770,4771],{"class":1830,"line":2123},[1828,4772,2298],{"class":1850},[1828,4774,4775],{"class":1830,"line":2128},[1828,4776,1900],{"class":1850},[1811,4778,944],{"id":4779},"key-value-storage",[3271,4781,948],{"id":4782},"redis-hashes",[1737,4784,4785],{},"Store each table as a hash:",[1819,4787,4789],{"className":1821,"code":4788,"language":1823,"meta":35,"style":35},"import \"github.com/redis/go-redis/v9\"\n\nfunc StoreAtom(ctx context.Context, rdb *redis.Client, key string, a *atom.Atom) error {\n    pipe := rdb.Pipeline()\n\n    // Store strings\n    if len(a.Strings) > 0 {\n        args := make([]any, 0, len(a.Strings)*2)\n        for k, v := range a.Strings {\n            args = append(args, k, v)\n        }\n        pipe.HSet(ctx, key+\":strings\", args...)\n    }\n\n    // Store ints\n    if len(a.Ints) > 0 {\n        args := make([]any, 0, len(a.Ints)*2)\n        for k, v := range a.Ints {\n            args = append(args, k, v)\n        }\n        pipe.HSet(ctx, key+\":ints\", args...)\n    }\n\n    // ... other tables\n\n    _, err := pipe.Exec(ctx)\n    return err\n}\n",[1825,4790,4791,4798,4802,4862,4879,4883,4888,4913,4953,4978,5003,5008,5040,5044,5048,5053,5075,5111,5133,5155,5159,5186,5190,5194,5199,5203,5228,5235],{"__ignoreMap":35},[1828,4792,4793,4795],{"class":1830,"line":9},[1828,4794,2459],{"class":1839},[1828,4796,4797],{"class":1967}," \"github.com/redis/go-redis/v9\"\n",[1828,4799,4800],{"class":1830,"line":20},[1828,4801,1907],{"emptyLinePlaceholder":1906},[1828,4803,4804,4806,4809,4811,4814,4817,4819,4822,4824,4827,4829,4832,4834,4837,4839,4842,4844,4846,4848,4850,4852,4854,4856,4858,4860],{"class":1830,"line":41},[1828,4805,2173],{"class":1839},[1828,4807,4808],{"class":1932}," StoreAtom",[1828,4810,2028],{"class":1850},[1828,4812,4813],{"class":2181},"ctx",[1828,4815,4816],{"class":1843}," context",[1828,4818,1929],{"class":1850},[1828,4820,4821],{"class":1843},"Context",[1828,4823,1917],{"class":1850},[1828,4825,4826],{"class":2181}," rdb",[1828,4828,2184],{"class":1952},[1828,4830,4831],{"class":1843},"redis",[1828,4833,1929],{"class":1850},[1828,4835,4836],{"class":1843},"Client",[1828,4838,1917],{"class":1850},[1828,4840,4841],{"class":2181}," key",[1828,4843,3194],{"class":1843},[1828,4845,1917],{"class":1850},[1828,4847,2265],{"class":2181},[1828,4849,2184],{"class":1952},[1828,4851,1735],{"class":1843},[1828,4853,1929],{"class":1850},[1828,4855,12],{"class":1843},[1828,4857,2067],{"class":1850},[1828,4859,3152],{"class":1843},[1828,4861,1851],{"class":1850},[1828,4863,4864,4867,4869,4871,4873,4876],{"class":1830,"line":1190},[1828,4865,4866],{"class":1913},"    pipe",[1828,4868,1923],{"class":1913},[1828,4870,4826],{"class":1913},[1828,4872,1929],{"class":1850},[1828,4874,4875],{"class":1932},"Pipeline",[1828,4877,4878],{"class":1850},"()\n",[1828,4880,4881],{"class":1830,"line":1195},[1828,4882,1907],{"emptyLinePlaceholder":1906},[1828,4884,4885],{"class":1830,"line":1879},[1828,4886,4887],{"class":1833},"    // Store strings\n",[1828,4889,4890,4892,4895,4897,4899,4901,4903,4905,4908,4911],{"class":1830,"line":1888},[1828,4891,2570],{"class":1952},[1828,4893,4894],{"class":2585}," len",[1828,4896,2028],{"class":1850},[1828,4898,1740],{"class":1913},[1828,4900,1929],{"class":1850},[1828,4902,2270],{"class":1913},[1828,4904,2067],{"class":1850},[1828,4906,4907],{"class":1952}," >",[1828,4909,4910],{"class":1988}," 0",[1828,4912,1851],{"class":1850},[1828,4914,4915,4918,4920,4923,4926,4928,4930,4932,4934,4936,4938,4940,4942,4944,4946,4948,4951],{"class":1830,"line":1897},[1828,4916,4917],{"class":1913},"        args",[1828,4919,1923],{"class":1913},[1828,4921,4922],{"class":2585}," make",[1828,4924,4925],{"class":1850},"([]",[1828,4927,3026],{"class":1843},[1828,4929,1917],{"class":1850},[1828,4931,4910],{"class":1988},[1828,4933,1917],{"class":1850},[1828,4935,4894],{"class":2585},[1828,4937,2028],{"class":1850},[1828,4939,1740],{"class":1913},[1828,4941,1929],{"class":1850},[1828,4943,2270],{"class":1913},[1828,4945,2067],{"class":1850},[1828,4947,3127],{"class":1913},[1828,4949,4950],{"class":1988},"2",[1828,4952,2033],{"class":1850},[1828,4954,4955,4958,4961,4963,4966,4968,4970,4972,4974,4976],{"class":1830,"line":1903},[1828,4956,4957],{"class":1952},"        for",[1828,4959,4960],{"class":1913}," k",[1828,4962,1917],{"class":1850},[1828,4964,4965],{"class":1913}," v",[1828,4967,1923],{"class":1913},[1828,4969,2262],{"class":1952},[1828,4971,2265],{"class":1913},[1828,4973,1929],{"class":1850},[1828,4975,2270],{"class":1913},[1828,4977,1851],{"class":1850},[1828,4979,4980,4983,4985,4988,4990,4993,4995,4997,4999,5001],{"class":1830,"line":1910},[1828,4981,4982],{"class":1913},"            args",[1828,4984,2796],{"class":1913},[1828,4986,4987],{"class":2585}," append",[1828,4989,2028],{"class":1850},[1828,4991,4992],{"class":1913},"args",[1828,4994,1917],{"class":1850},[1828,4996,4960],{"class":1913},[1828,4998,1917],{"class":1850},[1828,5000,4965],{"class":1913},[1828,5002,2033],{"class":1850},[1828,5004,5005],{"class":1830,"line":1944},[1828,5006,5007],{"class":1850},"        }\n",[1828,5009,5010,5013,5015,5018,5020,5022,5024,5027,5030,5032,5035,5038],{"class":1830,"line":2014},[1828,5011,5012],{"class":1913},"        pipe",[1828,5014,1929],{"class":1850},[1828,5016,5017],{"class":1932},"HSet",[1828,5019,2028],{"class":1850},[1828,5021,4813],{"class":1913},[1828,5023,1917],{"class":1850},[1828,5025,5026],{"class":1913}," key+",[1828,5028,5029],{"class":1967},"\":strings\"",[1828,5031,1917],{"class":1850},[1828,5033,5034],{"class":1913}," args",[1828,5036,5037],{"class":1952},"...",[1828,5039,2033],{"class":1850},[1828,5041,5042],{"class":1830,"line":2036},[1828,5043,2298],{"class":1850},[1828,5045,5046],{"class":1830,"line":2041},[1828,5047,1907],{"emptyLinePlaceholder":1906},[1828,5049,5050],{"class":1830,"line":2047},[1828,5051,5052],{"class":1833},"    // Store ints\n",[1828,5054,5055,5057,5059,5061,5063,5065,5067,5069,5071,5073],{"class":1830,"line":2073},[1828,5056,2570],{"class":1952},[1828,5058,4894],{"class":2585},[1828,5060,2028],{"class":1850},[1828,5062,1740],{"class":1913},[1828,5064,1929],{"class":1850},[1828,5066,2319],{"class":1913},[1828,5068,2067],{"class":1850},[1828,5070,4907],{"class":1952},[1828,5072,4910],{"class":1988},[1828,5074,1851],{"class":1850},[1828,5076,5077,5079,5081,5083,5085,5087,5089,5091,5093,5095,5097,5099,5101,5103,5105,5107,5109],{"class":1830,"line":2098},[1828,5078,4917],{"class":1913},[1828,5080,1923],{"class":1913},[1828,5082,4922],{"class":2585},[1828,5084,4925],{"class":1850},[1828,5086,3026],{"class":1843},[1828,5088,1917],{"class":1850},[1828,5090,4910],{"class":1988},[1828,5092,1917],{"class":1850},[1828,5094,4894],{"class":2585},[1828,5096,2028],{"class":1850},[1828,5098,1740],{"class":1913},[1828,5100,1929],{"class":1850},[1828,5102,2319],{"class":1913},[1828,5104,2067],{"class":1850},[1828,5106,3127],{"class":1913},[1828,5108,4950],{"class":1988},[1828,5110,2033],{"class":1850},[1828,5112,5113,5115,5117,5119,5121,5123,5125,5127,5129,5131],{"class":1830,"line":2123},[1828,5114,4957],{"class":1952},[1828,5116,4960],{"class":1913},[1828,5118,1917],{"class":1850},[1828,5120,4965],{"class":1913},[1828,5122,1923],{"class":1913},[1828,5124,2262],{"class":1952},[1828,5126,2265],{"class":1913},[1828,5128,1929],{"class":1850},[1828,5130,2319],{"class":1913},[1828,5132,1851],{"class":1850},[1828,5134,5135,5137,5139,5141,5143,5145,5147,5149,5151,5153],{"class":1830,"line":2128},[1828,5136,4982],{"class":1913},[1828,5138,2796],{"class":1913},[1828,5140,4987],{"class":2585},[1828,5142,2028],{"class":1850},[1828,5144,4992],{"class":1913},[1828,5146,1917],{"class":1850},[1828,5148,4960],{"class":1913},[1828,5150,1917],{"class":1850},[1828,5152,4965],{"class":1913},[1828,5154,2033],{"class":1850},[1828,5156,5157],{"class":1830,"line":2134},[1828,5158,5007],{"class":1850},[1828,5160,5161,5163,5165,5167,5169,5171,5173,5175,5178,5180,5182,5184],{"class":1830,"line":2604},[1828,5162,5012],{"class":1913},[1828,5164,1929],{"class":1850},[1828,5166,5017],{"class":1932},[1828,5168,2028],{"class":1850},[1828,5170,4813],{"class":1913},[1828,5172,1917],{"class":1850},[1828,5174,5026],{"class":1913},[1828,5176,5177],{"class":1967},"\":ints\"",[1828,5179,1917],{"class":1850},[1828,5181,5034],{"class":1913},[1828,5183,5037],{"class":1952},[1828,5185,2033],{"class":1850},[1828,5187,5188],{"class":1830,"line":2647},[1828,5189,2298],{"class":1850},[1828,5191,5192],{"class":1830,"line":2652},[1828,5193,1907],{"emptyLinePlaceholder":1906},[1828,5195,5196],{"class":1830,"line":2658},[1828,5197,5198],{"class":1833},"    // ... other tables\n",[1828,5200,5201],{"class":1830,"line":2679},[1828,5202,1907],{"emptyLinePlaceholder":1906},[1828,5204,5205,5208,5210,5212,5214,5217,5219,5222,5224,5226],{"class":1830,"line":2684},[1828,5206,5207],{"class":1913},"    _",[1828,5209,1917],{"class":1850},[1828,5211,2550],{"class":1913},[1828,5213,1923],{"class":1913},[1828,5215,5216],{"class":1913}," pipe",[1828,5218,1929],{"class":1850},[1828,5220,5221],{"class":1932},"Exec",[1828,5223,2028],{"class":1850},[1828,5225,4813],{"class":1913},[1828,5227,2033],{"class":1850},[1828,5229,5230,5232],{"class":1830,"line":2690},[1828,5231,2399],{"class":1952},[1828,5233,5234],{"class":1913}," err\n",[1828,5236,5237],{"class":1830,"line":2732},[1828,5238,1900],{"class":1850},[3271,5240,953],{"id":5241},"partial-reads",[1737,5243,5244],{},"Read only specific fields:",[1819,5246,5248],{"className":1821,"code":5247,"language":1823,"meta":35,"style":35},"func LoadStringField(ctx context.Context, rdb *redis.Client, key, field string) (string, error) {\n    return rdb.HGet(ctx, key+\":strings\", field).Result()\n}\n\nfunc LoadIntField(ctx context.Context, rdb *redis.Client, key, field string) (int64, error) {\n    return rdb.HGet(ctx, key+\":ints\", field).Int64()\n}\n",[1825,5249,5250,5303,5336,5340,5344,5397,5428],{"__ignoreMap":35},[1828,5251,5252,5254,5257,5259,5261,5263,5265,5267,5269,5271,5273,5275,5277,5279,5281,5283,5285,5287,5289,5291,5293,5295,5297,5299,5301],{"class":1830,"line":9},[1828,5253,2173],{"class":1839},[1828,5255,5256],{"class":1932}," LoadStringField",[1828,5258,2028],{"class":1850},[1828,5260,4813],{"class":2181},[1828,5262,4816],{"class":1843},[1828,5264,1929],{"class":1850},[1828,5266,4821],{"class":1843},[1828,5268,1917],{"class":1850},[1828,5270,4826],{"class":2181},[1828,5272,2184],{"class":1952},[1828,5274,4831],{"class":1843},[1828,5276,1929],{"class":1850},[1828,5278,4836],{"class":1843},[1828,5280,1917],{"class":1850},[1828,5282,4841],{"class":2181},[1828,5284,1917],{"class":1850},[1828,5286,2252],{"class":2181},[1828,5288,3194],{"class":1843},[1828,5290,2067],{"class":1850},[1828,5292,3121],{"class":1850},[1828,5294,4179],{"class":1843},[1828,5296,1917],{"class":1850},[1828,5298,3152],{"class":1843},[1828,5300,2067],{"class":1850},[1828,5302,1851],{"class":1850},[1828,5304,5305,5307,5309,5311,5314,5316,5318,5320,5322,5324,5326,5328,5331,5334],{"class":1830,"line":20},[1828,5306,2399],{"class":1952},[1828,5308,4826],{"class":1913},[1828,5310,1929],{"class":1850},[1828,5312,5313],{"class":1932},"HGet",[1828,5315,2028],{"class":1850},[1828,5317,4813],{"class":1913},[1828,5319,1917],{"class":1850},[1828,5321,5026],{"class":1913},[1828,5323,5029],{"class":1967},[1828,5325,1917],{"class":1850},[1828,5327,2252],{"class":1913},[1828,5329,5330],{"class":1850},").",[1828,5332,5333],{"class":1932},"Result",[1828,5335,4878],{"class":1850},[1828,5337,5338],{"class":1830,"line":41},[1828,5339,1900],{"class":1850},[1828,5341,5342],{"class":1830,"line":1190},[1828,5343,1907],{"emptyLinePlaceholder":1906},[1828,5345,5346,5348,5351,5353,5355,5357,5359,5361,5363,5365,5367,5369,5371,5373,5375,5377,5379,5381,5383,5385,5387,5389,5391,5393,5395],{"class":1830,"line":1195},[1828,5347,2173],{"class":1839},[1828,5349,5350],{"class":1932}," LoadIntField",[1828,5352,2028],{"class":1850},[1828,5354,4813],{"class":2181},[1828,5356,4816],{"class":1843},[1828,5358,1929],{"class":1850},[1828,5360,4821],{"class":1843},[1828,5362,1917],{"class":1850},[1828,5364,4826],{"class":2181},[1828,5366,2184],{"class":1952},[1828,5368,4831],{"class":1843},[1828,5370,1929],{"class":1850},[1828,5372,4836],{"class":1843},[1828,5374,1917],{"class":1850},[1828,5376,4841],{"class":2181},[1828,5378,1917],{"class":1850},[1828,5380,2252],{"class":2181},[1828,5382,3194],{"class":1843},[1828,5384,2067],{"class":1850},[1828,5386,3121],{"class":1850},[1828,5388,4203],{"class":1843},[1828,5390,1917],{"class":1850},[1828,5392,3152],{"class":1843},[1828,5394,2067],{"class":1850},[1828,5396,1851],{"class":1850},[1828,5398,5399,5401,5403,5405,5407,5409,5411,5413,5415,5417,5419,5421,5423,5426],{"class":1830,"line":1879},[1828,5400,2399],{"class":1952},[1828,5402,4826],{"class":1913},[1828,5404,1929],{"class":1850},[1828,5406,5313],{"class":1932},[1828,5408,2028],{"class":1850},[1828,5410,4813],{"class":1913},[1828,5412,1917],{"class":1850},[1828,5414,5026],{"class":1913},[1828,5416,5177],{"class":1967},[1828,5418,1917],{"class":1850},[1828,5420,2252],{"class":1913},[1828,5422,5330],{"class":1850},[1828,5424,5425],{"class":1932},"Int64",[1828,5427,4878],{"class":1850},[1828,5429,5430],{"class":1830,"line":1888},[1828,5431,1900],{"class":1850},[1811,5433,958],{"id":5434},"column-oriented-storage",[3271,5436,962],{"id":5437},"per-type-tables",[1737,5439,5440],{},"Store each atom table in a separate database table:",[1819,5442,5446],{"className":5443,"code":5444,"language":5445,"meta":35,"style":35},"language-sql shiki shiki-themes","CREATE TABLE atom_strings (\n    entity_id TEXT,\n    field_name TEXT,\n    value TEXT,\n    PRIMARY KEY (entity_id, field_name)\n);\n\nCREATE TABLE atom_ints (\n    entity_id TEXT,\n    field_name TEXT,\n    value BIGINT,\n    PRIMARY KEY (entity_id, field_name)\n);\n","sql",[1825,5447,5448,5461,5471,5480,5490,5498,5503,5507,5518,5526,5534,5543,5549],{"__ignoreMap":35},[1828,5449,5450,5453,5456,5459],{"class":1830,"line":9},[1828,5451,5452],{"class":1839},"CREATE",[1828,5454,5455],{"class":1839}," TABLE",[1828,5457,5458],{"class":1932}," atom_strings",[1828,5460,2463],{"class":2462},[1828,5462,5463,5466,5469],{"class":1830,"line":20},[1828,5464,5465],{"class":2462},"    entity_id ",[1828,5467,5468],{"class":1843},"TEXT",[1828,5470,4309],{"class":2462},[1828,5472,5473,5476,5478],{"class":1830,"line":41},[1828,5474,5475],{"class":2462},"    field_name ",[1828,5477,5468],{"class":1843},[1828,5479,4309],{"class":2462},[1828,5481,5482,5485,5488],{"class":1830,"line":1190},[1828,5483,5484],{"class":1839},"    value",[1828,5486,5487],{"class":1843}," TEXT",[1828,5489,4309],{"class":2462},[1828,5491,5492,5495],{"class":1830,"line":1195},[1828,5493,5494],{"class":1843},"    PRIMARY KEY",[1828,5496,5497],{"class":2462}," (entity_id, field_name)\n",[1828,5499,5500],{"class":1830,"line":1879},[1828,5501,5502],{"class":2462},");\n",[1828,5504,5505],{"class":1830,"line":1888},[1828,5506,1907],{"emptyLinePlaceholder":1906},[1828,5508,5509,5511,5513,5516],{"class":1830,"line":1897},[1828,5510,5452],{"class":1839},[1828,5512,5455],{"class":1839},[1828,5514,5515],{"class":1932}," atom_ints",[1828,5517,2463],{"class":2462},[1828,5519,5520,5522,5524],{"class":1830,"line":1903},[1828,5521,5465],{"class":2462},[1828,5523,5468],{"class":1843},[1828,5525,4309],{"class":2462},[1828,5527,5528,5530,5532],{"class":1830,"line":1910},[1828,5529,5475],{"class":2462},[1828,5531,5468],{"class":1843},[1828,5533,4309],{"class":2462},[1828,5535,5536,5538,5541],{"class":1830,"line":1944},[1828,5537,5484],{"class":1839},[1828,5539,5540],{"class":1843}," BIGINT",[1828,5542,4309],{"class":2462},[1828,5544,5545,5547],{"class":1830,"line":2014},[1828,5546,5494],{"class":1843},[1828,5548,5497],{"class":2462},[1828,5550,5551],{"class":1830,"line":2036},[1828,5552,5502],{"class":2462},[1819,5554,5556],{"className":1821,"code":5555,"language":1823,"meta":35,"style":35},"func StoreAtom(db *sql.DB, entityID string, a *atom.Atom) error {\n    tx, _ := db.Begin()\n\n    for field, value := range a.Strings {\n        tx.Exec(`INSERT INTO atom_strings VALUES (?, ?, ?)\n                 ON CONFLICT DO UPDATE SET value = ?`,\n            entityID, field, value, value)\n    }\n\n    for field, value := range a.Ints {\n        tx.Exec(`INSERT INTO atom_ints VALUES (?, ?, ?)\n                 ON CONFLICT DO UPDATE SET value = ?`,\n            entityID, field, value, value)\n    }\n\n    return tx.Commit()\n}\n",[1825,5557,5558,5603,5624,5628,5650,5664,5671,5690,5694,5698,5720,5733,5739,5757,5761,5765,5779],{"__ignoreMap":35},[1828,5559,5560,5562,5564,5566,5569,5571,5573,5575,5578,5580,5583,5585,5587,5589,5591,5593,5595,5597,5599,5601],{"class":1830,"line":9},[1828,5561,2173],{"class":1839},[1828,5563,4808],{"class":1932},[1828,5565,2028],{"class":1850},[1828,5567,5568],{"class":2181},"db",[1828,5570,2184],{"class":1952},[1828,5572,5445],{"class":1843},[1828,5574,1929],{"class":1850},[1828,5576,5577],{"class":1843},"DB",[1828,5579,1917],{"class":1850},[1828,5581,5582],{"class":2181}," entityID",[1828,5584,3194],{"class":1843},[1828,5586,1917],{"class":1850},[1828,5588,2265],{"class":2181},[1828,5590,2184],{"class":1952},[1828,5592,1735],{"class":1843},[1828,5594,1929],{"class":1850},[1828,5596,12],{"class":1843},[1828,5598,2067],{"class":1850},[1828,5600,3152],{"class":1843},[1828,5602,1851],{"class":1850},[1828,5604,5605,5608,5610,5612,5614,5617,5619,5622],{"class":1830,"line":20},[1828,5606,5607],{"class":1913},"    tx",[1828,5609,1917],{"class":1850},[1828,5611,1920],{"class":1913},[1828,5613,1923],{"class":1913},[1828,5615,5616],{"class":1913}," db",[1828,5618,1929],{"class":1850},[1828,5620,5621],{"class":1932},"Begin",[1828,5623,4878],{"class":1850},[1828,5625,5626],{"class":1830,"line":41},[1828,5627,1907],{"emptyLinePlaceholder":1906},[1828,5629,5630,5632,5634,5636,5638,5640,5642,5644,5646,5648],{"class":1830,"line":1190},[1828,5631,2249],{"class":1952},[1828,5633,2252],{"class":1913},[1828,5635,1917],{"class":1850},[1828,5637,2257],{"class":1913},[1828,5639,1923],{"class":1913},[1828,5641,2262],{"class":1952},[1828,5643,2265],{"class":1913},[1828,5645,1929],{"class":1850},[1828,5647,2270],{"class":1913},[1828,5649,1851],{"class":1850},[1828,5651,5652,5655,5657,5659,5661],{"class":1830,"line":1195},[1828,5653,5654],{"class":1913},"        tx",[1828,5656,1929],{"class":1850},[1828,5658,5221],{"class":1932},[1828,5660,2028],{"class":1850},[1828,5662,5663],{"class":1967},"`INSERT INTO atom_strings VALUES (?, ?, ?)\n",[1828,5665,5666,5669],{"class":1830,"line":1879},[1828,5667,5668],{"class":1967},"                 ON CONFLICT DO UPDATE SET value = ?`",[1828,5670,4309],{"class":1850},[1828,5672,5673,5676,5678,5680,5682,5684,5686,5688],{"class":1830,"line":1888},[1828,5674,5675],{"class":1913},"            entityID",[1828,5677,1917],{"class":1850},[1828,5679,2252],{"class":1913},[1828,5681,1917],{"class":1850},[1828,5683,2257],{"class":1913},[1828,5685,1917],{"class":1850},[1828,5687,2257],{"class":1913},[1828,5689,2033],{"class":1850},[1828,5691,5692],{"class":1830,"line":1897},[1828,5693,2298],{"class":1850},[1828,5695,5696],{"class":1830,"line":1903},[1828,5697,1907],{"emptyLinePlaceholder":1906},[1828,5699,5700,5702,5704,5706,5708,5710,5712,5714,5716,5718],{"class":1830,"line":1910},[1828,5701,2249],{"class":1952},[1828,5703,2252],{"class":1913},[1828,5705,1917],{"class":1850},[1828,5707,2257],{"class":1913},[1828,5709,1923],{"class":1913},[1828,5711,2262],{"class":1952},[1828,5713,2265],{"class":1913},[1828,5715,1929],{"class":1850},[1828,5717,2319],{"class":1913},[1828,5719,1851],{"class":1850},[1828,5721,5722,5724,5726,5728,5730],{"class":1830,"line":1944},[1828,5723,5654],{"class":1913},[1828,5725,1929],{"class":1850},[1828,5727,5221],{"class":1932},[1828,5729,2028],{"class":1850},[1828,5731,5732],{"class":1967},"`INSERT INTO atom_ints VALUES (?, ?, ?)\n",[1828,5734,5735,5737],{"class":1830,"line":2014},[1828,5736,5668],{"class":1967},[1828,5738,4309],{"class":1850},[1828,5740,5741,5743,5745,5747,5749,5751,5753,5755],{"class":1830,"line":2036},[1828,5742,5675],{"class":1913},[1828,5744,1917],{"class":1850},[1828,5746,2252],{"class":1913},[1828,5748,1917],{"class":1850},[1828,5750,2257],{"class":1913},[1828,5752,1917],{"class":1850},[1828,5754,2257],{"class":1913},[1828,5756,2033],{"class":1850},[1828,5758,5759],{"class":1830,"line":2041},[1828,5760,2298],{"class":1850},[1828,5762,5763],{"class":1830,"line":2047},[1828,5764,1907],{"emptyLinePlaceholder":1906},[1828,5766,5767,5769,5772,5774,5777],{"class":1830,"line":2073},[1828,5768,2399],{"class":1952},[1828,5770,5771],{"class":1913}," tx",[1828,5773,1929],{"class":1850},[1828,5775,5776],{"class":1932},"Commit",[1828,5778,4878],{"class":1850},[1828,5780,5781],{"class":1830,"line":2098},[1828,5782,1900],{"class":1850},[3271,5784,967],{"id":5785},"querying-by-field",[1819,5787,5789],{"className":5443,"code":5788,"language":5445,"meta":35,"style":35},"-- Find users over 30\nSELECT entity_id FROM atom_ints\nWHERE field_name = 'Age' AND value > 30;\n\n-- Find users by name\nSELECT entity_id FROM atom_strings\nWHERE field_name = 'Name' AND value = 'Alice';\n",[1825,5790,5791,5796,5810,5842,5846,5851,5862],{"__ignoreMap":35},[1828,5792,5793],{"class":1830,"line":9},[1828,5794,5795],{"class":1833},"-- Find users over 30\n",[1828,5797,5798,5801,5804,5807],{"class":1830,"line":20},[1828,5799,5800],{"class":1839},"SELECT",[1828,5802,5803],{"class":2462}," entity_id ",[1828,5805,5806],{"class":1839},"FROM",[1828,5808,5809],{"class":2462}," atom_ints\n",[1828,5811,5812,5815,5818,5821,5824,5827,5830,5833,5835,5837,5839],{"class":1830,"line":41},[1828,5813,5814],{"class":1839},"WHERE",[1828,5816,5817],{"class":2462}," field_name ",[1828,5819,5820],{"class":1952},"=",[1828,5822,5823],{"class":1850}," '",[1828,5825,5826],{"class":1967},"Age",[1828,5828,5829],{"class":1850},"'",[1828,5831,5832],{"class":1839}," AND",[1828,5834,2257],{"class":1839},[1828,5836,4907],{"class":1952},[1828,5838,1989],{"class":1988},[1828,5840,5841],{"class":2462},";\n",[1828,5843,5844],{"class":1830,"line":1190},[1828,5845,1907],{"emptyLinePlaceholder":1906},[1828,5847,5848],{"class":1830,"line":1195},[1828,5849,5850],{"class":1833},"-- Find users by name\n",[1828,5852,5853,5855,5857,5859],{"class":1830,"line":1879},[1828,5854,5800],{"class":1839},[1828,5856,5803],{"class":2462},[1828,5858,5806],{"class":1839},[1828,5860,5861],{"class":2462}," atom_strings\n",[1828,5863,5864,5866,5868,5870,5872,5874,5876,5878,5880,5882,5884,5887,5889],{"class":1830,"line":1888},[1828,5865,5814],{"class":1839},[1828,5867,5817],{"class":2462},[1828,5869,5820],{"class":1952},[1828,5871,5823],{"class":1850},[1828,5873,3925],{"class":1967},[1828,5875,5829],{"class":1850},[1828,5877,5832],{"class":1839},[1828,5879,2257],{"class":1839},[1828,5881,2796],{"class":1952},[1828,5883,5823],{"class":1850},[1828,5885,5886],{"class":1967},"Alice",[1828,5888,5829],{"class":1850},[1828,5890,5841],{"class":2462},[1811,5892,972],{"id":5893},"binary-format",[1737,5895,5896],{},"Custom binary encoding for maximum efficiency:",[1819,5898,5900],{"className":1821,"code":5899,"language":1823,"meta":35,"style":35},"import \"encoding/binary\"\n\nfunc EncodeAtom(a *atom.Atom) []byte {\n    buf := new(bytes.Buffer)\n\n    // Write string count and entries\n    binary.Write(buf, binary.LittleEndian, uint32(len(a.Strings)))\n    for k, v := range a.Strings {\n        writeString(buf, k)\n        writeString(buf, v)\n    }\n\n    // Write int count and entries\n    binary.Write(buf, binary.LittleEndian, uint32(len(a.Ints)))\n    for k, v := range a.Ints {\n        writeString(buf, k)\n        binary.Write(buf, binary.LittleEndian, v)\n    }\n\n    // ... other tables\n\n    return buf.Bytes()\n}\n\nfunc writeString(buf *bytes.Buffer, s string) {\n    binary.Write(buf, binary.LittleEndian, uint32(len(s)))\n    buf.WriteString(s)\n}\n",[1825,5901,5902,5909,5913,5942,5964,5968,5973,6019,6041,6056,6070,6074,6078,6083,6121,6143,6157,6184,6188,6192,6196,6200,6214,6218,6222,6252,6287,6302],{"__ignoreMap":35},[1828,5903,5904,5906],{"class":1830,"line":9},[1828,5905,2459],{"class":1839},[1828,5907,5908],{"class":1967}," \"encoding/binary\"\n",[1828,5910,5911],{"class":1830,"line":20},[1828,5912,1907],{"emptyLinePlaceholder":1906},[1828,5914,5915,5917,5920,5922,5924,5926,5928,5930,5932,5934,5937,5940],{"class":1830,"line":41},[1828,5916,2173],{"class":1839},[1828,5918,5919],{"class":1932}," EncodeAtom",[1828,5921,2028],{"class":1850},[1828,5923,1740],{"class":2181},[1828,5925,2184],{"class":1952},[1828,5927,1735],{"class":1843},[1828,5929,1929],{"class":1850},[1828,5931,12],{"class":1843},[1828,5933,2067],{"class":1850},[1828,5935,5936],{"class":1850}," []",[1828,5938,5939],{"class":1843},"byte",[1828,5941,1851],{"class":1850},[1828,5943,5944,5947,5949,5952,5954,5957,5959,5962],{"class":1830,"line":1190},[1828,5945,5946],{"class":1913},"    buf",[1828,5948,1923],{"class":1913},[1828,5950,5951],{"class":2585}," new",[1828,5953,2028],{"class":1850},[1828,5955,5956],{"class":1843},"bytes",[1828,5958,1929],{"class":1850},[1828,5960,5961],{"class":1843},"Buffer",[1828,5963,2033],{"class":1850},[1828,5965,5966],{"class":1830,"line":1195},[1828,5967,1907],{"emptyLinePlaceholder":1906},[1828,5969,5970],{"class":1830,"line":1879},[1828,5971,5972],{"class":1833},"    // Write string count and entries\n",[1828,5974,5975,5978,5980,5983,5985,5988,5990,5993,5995,5998,6000,6003,6005,6008,6010,6012,6014,6016],{"class":1830,"line":1888},[1828,5976,5977],{"class":1913},"    binary",[1828,5979,1929],{"class":1850},[1828,5981,5982],{"class":1932},"Write",[1828,5984,2028],{"class":1850},[1828,5986,5987],{"class":1913},"buf",[1828,5989,1917],{"class":1850},[1828,5991,5992],{"class":1913}," binary",[1828,5994,1929],{"class":1850},[1828,5996,5997],{"class":1913},"LittleEndian",[1828,5999,1917],{"class":1850},[1828,6001,6002],{"class":1843}," uint32",[1828,6004,2028],{"class":1850},[1828,6006,6007],{"class":2585},"len",[1828,6009,2028],{"class":1850},[1828,6011,1740],{"class":1913},[1828,6013,1929],{"class":1850},[1828,6015,2270],{"class":1913},[1828,6017,6018],{"class":1850},")))\n",[1828,6020,6021,6023,6025,6027,6029,6031,6033,6035,6037,6039],{"class":1830,"line":1897},[1828,6022,2249],{"class":1952},[1828,6024,4960],{"class":1913},[1828,6026,1917],{"class":1850},[1828,6028,4965],{"class":1913},[1828,6030,1923],{"class":1913},[1828,6032,2262],{"class":1952},[1828,6034,2265],{"class":1913},[1828,6036,1929],{"class":1850},[1828,6038,2270],{"class":1913},[1828,6040,1851],{"class":1850},[1828,6042,6043,6046,6048,6050,6052,6054],{"class":1830,"line":1903},[1828,6044,6045],{"class":1932},"        writeString",[1828,6047,2028],{"class":1850},[1828,6049,5987],{"class":1913},[1828,6051,1917],{"class":1850},[1828,6053,4960],{"class":1913},[1828,6055,2033],{"class":1850},[1828,6057,6058,6060,6062,6064,6066,6068],{"class":1830,"line":1910},[1828,6059,6045],{"class":1932},[1828,6061,2028],{"class":1850},[1828,6063,5987],{"class":1913},[1828,6065,1917],{"class":1850},[1828,6067,4965],{"class":1913},[1828,6069,2033],{"class":1850},[1828,6071,6072],{"class":1830,"line":1944},[1828,6073,2298],{"class":1850},[1828,6075,6076],{"class":1830,"line":2014},[1828,6077,1907],{"emptyLinePlaceholder":1906},[1828,6079,6080],{"class":1830,"line":2036},[1828,6081,6082],{"class":1833},"    // Write int count and entries\n",[1828,6084,6085,6087,6089,6091,6093,6095,6097,6099,6101,6103,6105,6107,6109,6111,6113,6115,6117,6119],{"class":1830,"line":2041},[1828,6086,5977],{"class":1913},[1828,6088,1929],{"class":1850},[1828,6090,5982],{"class":1932},[1828,6092,2028],{"class":1850},[1828,6094,5987],{"class":1913},[1828,6096,1917],{"class":1850},[1828,6098,5992],{"class":1913},[1828,6100,1929],{"class":1850},[1828,6102,5997],{"class":1913},[1828,6104,1917],{"class":1850},[1828,6106,6002],{"class":1843},[1828,6108,2028],{"class":1850},[1828,6110,6007],{"class":2585},[1828,6112,2028],{"class":1850},[1828,6114,1740],{"class":1913},[1828,6116,1929],{"class":1850},[1828,6118,2319],{"class":1913},[1828,6120,6018],{"class":1850},[1828,6122,6123,6125,6127,6129,6131,6133,6135,6137,6139,6141],{"class":1830,"line":2047},[1828,6124,2249],{"class":1952},[1828,6126,4960],{"class":1913},[1828,6128,1917],{"class":1850},[1828,6130,4965],{"class":1913},[1828,6132,1923],{"class":1913},[1828,6134,2262],{"class":1952},[1828,6136,2265],{"class":1913},[1828,6138,1929],{"class":1850},[1828,6140,2319],{"class":1913},[1828,6142,1851],{"class":1850},[1828,6144,6145,6147,6149,6151,6153,6155],{"class":1830,"line":2073},[1828,6146,6045],{"class":1932},[1828,6148,2028],{"class":1850},[1828,6150,5987],{"class":1913},[1828,6152,1917],{"class":1850},[1828,6154,4960],{"class":1913},[1828,6156,2033],{"class":1850},[1828,6158,6159,6162,6164,6166,6168,6170,6172,6174,6176,6178,6180,6182],{"class":1830,"line":2098},[1828,6160,6161],{"class":1913},"        binary",[1828,6163,1929],{"class":1850},[1828,6165,5982],{"class":1932},[1828,6167,2028],{"class":1850},[1828,6169,5987],{"class":1913},[1828,6171,1917],{"class":1850},[1828,6173,5992],{"class":1913},[1828,6175,1929],{"class":1850},[1828,6177,5997],{"class":1913},[1828,6179,1917],{"class":1850},[1828,6181,4965],{"class":1913},[1828,6183,2033],{"class":1850},[1828,6185,6186],{"class":1830,"line":2123},[1828,6187,2298],{"class":1850},[1828,6189,6190],{"class":1830,"line":2128},[1828,6191,1907],{"emptyLinePlaceholder":1906},[1828,6193,6194],{"class":1830,"line":2134},[1828,6195,5198],{"class":1833},[1828,6197,6198],{"class":1830,"line":2604},[1828,6199,1907],{"emptyLinePlaceholder":1906},[1828,6201,6202,6204,6207,6209,6212],{"class":1830,"line":2647},[1828,6203,2399],{"class":1952},[1828,6205,6206],{"class":1913}," buf",[1828,6208,1929],{"class":1850},[1828,6210,6211],{"class":1932},"Bytes",[1828,6213,4878],{"class":1850},[1828,6215,6216],{"class":1830,"line":2652},[1828,6217,1900],{"class":1850},[1828,6219,6220],{"class":1830,"line":2658},[1828,6221,1907],{"emptyLinePlaceholder":1906},[1828,6223,6224,6226,6229,6231,6233,6235,6237,6239,6241,6243,6246,6248,6250],{"class":1830,"line":2679},[1828,6225,2173],{"class":1839},[1828,6227,6228],{"class":1932}," writeString",[1828,6230,2028],{"class":1850},[1828,6232,5987],{"class":2181},[1828,6234,2184],{"class":1952},[1828,6236,5956],{"class":1843},[1828,6238,1929],{"class":1850},[1828,6240,5961],{"class":1843},[1828,6242,1917],{"class":1850},[1828,6244,6245],{"class":2181}," s",[1828,6247,3194],{"class":1843},[1828,6249,2067],{"class":1850},[1828,6251,1851],{"class":1850},[1828,6253,6254,6256,6258,6260,6262,6264,6266,6268,6270,6272,6274,6276,6278,6280,6282,6285],{"class":1830,"line":2684},[1828,6255,5977],{"class":1913},[1828,6257,1929],{"class":1850},[1828,6259,5982],{"class":1932},[1828,6261,2028],{"class":1850},[1828,6263,5987],{"class":1913},[1828,6265,1917],{"class":1850},[1828,6267,5992],{"class":1913},[1828,6269,1929],{"class":1850},[1828,6271,5997],{"class":1913},[1828,6273,1917],{"class":1850},[1828,6275,6002],{"class":1843},[1828,6277,2028],{"class":1850},[1828,6279,6007],{"class":2585},[1828,6281,2028],{"class":1850},[1828,6283,6284],{"class":1913},"s",[1828,6286,6018],{"class":1850},[1828,6288,6289,6291,6293,6296,6298,6300],{"class":1830,"line":2690},[1828,6290,5946],{"class":1913},[1828,6292,1929],{"class":1850},[1828,6294,6295],{"class":1932},"WriteString",[1828,6297,2028],{"class":1850},[1828,6299,6284],{"class":1913},[1828,6301,2033],{"class":1850},[1828,6303,6304],{"class":1830,"line":2732},[1828,6305,1900],{"class":1850},[1811,6307,977],{"id":6308},"streaming",[1737,6310,6311],{},"For large datasets, stream atoms:",[1819,6313,6315],{"className":1821,"code":6314,"language":1823,"meta":35,"style":35},"type AtomStream struct {\n    encoder *json.Encoder\n}\n\nfunc (s *AtomStream) Write(a *atom.Atom) error {\n    return s.encoder.Encode(a)\n}\n\n// Usage\nfile, _ := os.Create(\"atoms.jsonl\")\nstream := &AtomStream{encoder: json.NewEncoder(file)}\n\nfor _, user := range users {\n    a := atomizer.Atomize(user)\n    stream.Write(a)\n}\n",[1825,6316,6317,6328,6343,6347,6351,6387,6409,6413,6417,6422,6448,6479,6483,6504,6522,6537],{"__ignoreMap":35},[1828,6318,6319,6321,6324,6326],{"class":1830,"line":9},[1828,6320,1840],{"class":1839},[1828,6322,6323],{"class":1843}," AtomStream",[1828,6325,1847],{"class":1839},[1828,6327,1851],{"class":1850},[1828,6329,6330,6333,6335,6338,6340],{"class":1830,"line":20},[1828,6331,6332],{"class":1856},"    encoder",[1828,6334,2184],{"class":1952},[1828,6336,6337],{"class":1843},"json",[1828,6339,1929],{"class":1850},[1828,6341,6342],{"class":1843},"Encoder\n",[1828,6344,6345],{"class":1830,"line":41},[1828,6346,1900],{"class":1850},[1828,6348,6349],{"class":1830,"line":1190},[1828,6350,1907],{"emptyLinePlaceholder":1906},[1828,6352,6353,6355,6357,6359,6361,6364,6366,6369,6371,6373,6375,6377,6379,6381,6383,6385],{"class":1830,"line":1195},[1828,6354,2173],{"class":1839},[1828,6356,3121],{"class":1850},[1828,6358,3124],{"class":2181},[1828,6360,3127],{"class":1952},[1828,6362,6363],{"class":1843},"AtomStream",[1828,6365,2067],{"class":1850},[1828,6367,6368],{"class":1932}," Write",[1828,6370,2028],{"class":1850},[1828,6372,1740],{"class":2181},[1828,6374,2184],{"class":1952},[1828,6376,1735],{"class":1843},[1828,6378,1929],{"class":1850},[1828,6380,12],{"class":1843},[1828,6382,2067],{"class":1850},[1828,6384,3152],{"class":1843},[1828,6386,1851],{"class":1850},[1828,6388,6389,6391,6393,6395,6398,6400,6403,6405,6407],{"class":1830,"line":1879},[1828,6390,2399],{"class":1952},[1828,6392,6245],{"class":1913},[1828,6394,1929],{"class":1850},[1828,6396,6397],{"class":1913},"encoder",[1828,6399,1929],{"class":1850},[1828,6401,6402],{"class":1932},"Encode",[1828,6404,2028],{"class":1850},[1828,6406,1740],{"class":1913},[1828,6408,2033],{"class":1850},[1828,6410,6411],{"class":1830,"line":1888},[1828,6412,1900],{"class":1850},[1828,6414,6415],{"class":1830,"line":1897},[1828,6416,1907],{"emptyLinePlaceholder":1906},[1828,6418,6419],{"class":1830,"line":1903},[1828,6420,6421],{"class":1833},"// Usage\n",[1828,6423,6424,6427,6429,6431,6433,6436,6438,6441,6443,6446],{"class":1830,"line":1910},[1828,6425,6426],{"class":1913},"file",[1828,6428,1917],{"class":1850},[1828,6430,1920],{"class":1913},[1828,6432,1923],{"class":1913},[1828,6434,6435],{"class":1913}," os",[1828,6437,1929],{"class":1850},[1828,6439,6440],{"class":1932},"Create",[1828,6442,2028],{"class":1850},[1828,6444,6445],{"class":1967},"\"atoms.jsonl\"",[1828,6447,2033],{"class":1850},[1828,6449,6450,6453,6455,6457,6459,6461,6463,6465,6467,6469,6472,6474,6476],{"class":1830,"line":1944},[1828,6451,6452],{"class":1913},"stream",[1828,6454,1923],{"class":1913},[1828,6456,1953],{"class":1952},[1828,6458,6363],{"class":1843},[1828,6460,1958],{"class":1850},[1828,6462,6397],{"class":1856},[1828,6464,1964],{"class":1850},[1828,6466,3975],{"class":1913},[1828,6468,1929],{"class":1850},[1828,6470,6471],{"class":1932},"NewEncoder",[1828,6473,2028],{"class":1850},[1828,6475,6426],{"class":1913},[1828,6477,6478],{"class":1850},")}\n",[1828,6480,6481],{"class":1830,"line":2014},[1828,6482,1907],{"emptyLinePlaceholder":1906},[1828,6484,6485,6488,6490,6492,6495,6497,6499,6502],{"class":1830,"line":2036},[1828,6486,6487],{"class":1952},"for",[1828,6489,1920],{"class":1913},[1828,6491,1917],{"class":1850},[1828,6493,6494],{"class":1913}," user",[1828,6496,1923],{"class":1913},[1828,6498,2262],{"class":1952},[1828,6500,6501],{"class":1913}," users",[1828,6503,1851],{"class":1850},[1828,6505,6506,6508,6510,6512,6514,6516,6518,6520],{"class":1830,"line":2041},[1828,6507,2661],{"class":1913},[1828,6509,1923],{"class":1913},[1828,6511,2021],{"class":1913},[1828,6513,1929],{"class":1850},[1828,6515,252],{"class":1932},[1828,6517,2028],{"class":1850},[1828,6519,1947],{"class":1913},[1828,6521,2033],{"class":1850},[1828,6523,6524,6527,6529,6531,6533,6535],{"class":1830,"line":2047},[1828,6525,6526],{"class":1913},"    stream",[1828,6528,1929],{"class":1850},[1828,6530,5982],{"class":1932},[1828,6532,2028],{"class":1850},[1828,6534,1740],{"class":1913},[1828,6536,2033],{"class":1850},[1828,6538,6539],{"class":1830,"line":2073},[1828,6540,1900],{"class":1850},[1811,6542,982],{"id":6543},"compression",[1737,6545,6546],{},"Combine with compression:",[1819,6548,6550],{"className":1821,"code":6549,"language":1823,"meta":35,"style":35},"import \"compress/gzip\"\n\nfunc CompressAtom(a *atom.Atom) ([]byte, error) {\n    var buf bytes.Buffer\n    gz := gzip.NewWriter(&buf)\n\n    if err := json.NewEncoder(gz).Encode(a); err != nil {\n        return nil, err\n    }\n    gz.Close()\n\n    return buf.Bytes(), nil\n}\n",[1825,6551,6552,6559,6563,6597,6612,6635,6639,6676,6687,6691,6702,6706,6722],{"__ignoreMap":35},[1828,6553,6554,6556],{"class":1830,"line":9},[1828,6555,2459],{"class":1839},[1828,6557,6558],{"class":1967}," \"compress/gzip\"\n",[1828,6560,6561],{"class":1830,"line":20},[1828,6562,1907],{"emptyLinePlaceholder":1906},[1828,6564,6565,6567,6570,6572,6574,6576,6578,6580,6582,6584,6587,6589,6591,6593,6595],{"class":1830,"line":41},[1828,6566,2173],{"class":1839},[1828,6568,6569],{"class":1932}," CompressAtom",[1828,6571,2028],{"class":1850},[1828,6573,1740],{"class":2181},[1828,6575,2184],{"class":1952},[1828,6577,1735],{"class":1843},[1828,6579,1929],{"class":1850},[1828,6581,12],{"class":1843},[1828,6583,2067],{"class":1850},[1828,6585,6586],{"class":1850}," ([]",[1828,6588,5939],{"class":1843},[1828,6590,1917],{"class":1850},[1828,6592,3152],{"class":1843},[1828,6594,2067],{"class":1850},[1828,6596,1851],{"class":1850},[1828,6598,6599,6602,6604,6607,6609],{"class":1830,"line":1190},[1828,6600,6601],{"class":1839},"    var",[1828,6603,6206],{"class":1913},[1828,6605,6606],{"class":1843}," bytes",[1828,6608,1929],{"class":1850},[1828,6610,6611],{"class":1843},"Buffer\n",[1828,6613,6614,6617,6619,6622,6624,6627,6629,6631,6633],{"class":1830,"line":1195},[1828,6615,6616],{"class":1913},"    gz",[1828,6618,1923],{"class":1913},[1828,6620,6621],{"class":1913}," gzip",[1828,6623,1929],{"class":1850},[1828,6625,6626],{"class":1932},"NewWriter",[1828,6628,2028],{"class":1850},[1828,6630,4125],{"class":1952},[1828,6632,5987],{"class":1913},[1828,6634,2033],{"class":1850},[1828,6636,6637],{"class":1830,"line":1879},[1828,6638,1907],{"emptyLinePlaceholder":1906},[1828,6640,6641,6643,6645,6647,6649,6651,6653,6655,6658,6660,6662,6664,6666,6668,6670,6672,6674],{"class":1830,"line":1888},[1828,6642,2570],{"class":1952},[1828,6644,2550],{"class":1913},[1828,6646,1923],{"class":1913},[1828,6648,3975],{"class":1913},[1828,6650,1929],{"class":1850},[1828,6652,6471],{"class":1932},[1828,6654,2028],{"class":1850},[1828,6656,6657],{"class":1913},"gz",[1828,6659,5330],{"class":1850},[1828,6661,6402],{"class":1932},[1828,6663,2028],{"class":1850},[1828,6665,1740],{"class":1913},[1828,6667,4074],{"class":1850},[1828,6669,2550],{"class":1913},[1828,6671,2575],{"class":1952},[1828,6673,2578],{"class":1839},[1828,6675,1851],{"class":1850},[1828,6677,6678,6681,6683,6685],{"class":1830,"line":1897},[1828,6679,6680],{"class":1952},"        return",[1828,6682,2578],{"class":1839},[1828,6684,1917],{"class":1850},[1828,6686,5234],{"class":1913},[1828,6688,6689],{"class":1830,"line":1903},[1828,6690,2298],{"class":1850},[1828,6692,6693,6695,6697,6700],{"class":1830,"line":1910},[1828,6694,6616],{"class":1913},[1828,6696,1929],{"class":1850},[1828,6698,6699],{"class":1932},"Close",[1828,6701,4878],{"class":1850},[1828,6703,6704],{"class":1830,"line":1944},[1828,6705,1907],{"emptyLinePlaceholder":1906},[1828,6707,6708,6710,6712,6714,6716,6719],{"class":1830,"line":2014},[1828,6709,2399],{"class":1952},[1828,6711,6206],{"class":1913},[1828,6713,1929],{"class":1850},[1828,6715,6211],{"class":1932},[1828,6717,6718],{"class":1850},"(),",[1828,6720,6721],{"class":1839}," nil\n",[1828,6723,6724],{"class":1830,"line":2036},[1828,6725,1900],{"class":1850},[1811,6727,406],{"id":6728},"best-practices",[3271,6730,990],{"id":6731},"version-your-format",[1819,6733,6735],{"className":1821,"code":6734,"language":1823,"meta":35,"style":35},"type VersionedAtom struct {\n    Version int        `json:\"v\"`\n    Atom    atom.Atom  `json:\"a\"`\n}\n\nfunc Encode(a *atom.Atom) []byte {\n    va := VersionedAtom{Version: 1, Atom: *a}\n    data, _ := json.Marshal(va)\n    return data\n}\n",[1825,6736,6737,6748,6759,6774,6778,6782,6809,6840,6864,6871],{"__ignoreMap":35},[1828,6738,6739,6741,6744,6746],{"class":1830,"line":9},[1828,6740,1840],{"class":1839},[1828,6742,6743],{"class":1843}," VersionedAtom",[1828,6745,1847],{"class":1839},[1828,6747,1851],{"class":1850},[1828,6749,6750,6753,6756],{"class":1830,"line":20},[1828,6751,6752],{"class":1856},"    Version",[1828,6754,6755],{"class":1843}," int",[1828,6757,6758],{"class":1967},"        `json:\"v\"`\n",[1828,6760,6761,6764,6767,6769,6771],{"class":1830,"line":41},[1828,6762,6763],{"class":1856},"    Atom",[1828,6765,6766],{"class":1843},"    atom",[1828,6768,1929],{"class":1850},[1828,6770,12],{"class":1843},[1828,6772,6773],{"class":1967},"  `json:\"a\"`\n",[1828,6775,6776],{"class":1830,"line":1190},[1828,6777,1900],{"class":1850},[1828,6779,6780],{"class":1830,"line":1195},[1828,6781,1907],{"emptyLinePlaceholder":1906},[1828,6783,6784,6786,6789,6791,6793,6795,6797,6799,6801,6803,6805,6807],{"class":1830,"line":1879},[1828,6785,2173],{"class":1839},[1828,6787,6788],{"class":1932}," Encode",[1828,6790,2028],{"class":1850},[1828,6792,1740],{"class":2181},[1828,6794,2184],{"class":1952},[1828,6796,1735],{"class":1843},[1828,6798,1929],{"class":1850},[1828,6800,12],{"class":1843},[1828,6802,2067],{"class":1850},[1828,6804,5936],{"class":1850},[1828,6806,5939],{"class":1843},[1828,6808,1851],{"class":1850},[1828,6810,6811,6814,6816,6818,6820,6822,6824,6827,6829,6832,6834,6836,6838],{"class":1830,"line":1888},[1828,6812,6813],{"class":1913},"    va",[1828,6815,1923],{"class":1913},[1828,6817,6743],{"class":1843},[1828,6819,1958],{"class":1850},[1828,6821,3443],{"class":1856},[1828,6823,1964],{"class":1850},[1828,6825,6826],{"class":1988}," 1",[1828,6828,1917],{"class":1850},[1828,6830,6831],{"class":1856}," Atom",[1828,6833,1964],{"class":1850},[1828,6835,2184],{"class":1952},[1828,6837,1740],{"class":1913},[1828,6839,1900],{"class":1850},[1828,6841,6842,6845,6847,6849,6851,6853,6855,6857,6859,6862],{"class":1830,"line":1897},[1828,6843,6844],{"class":1913},"    data",[1828,6846,1917],{"class":1850},[1828,6848,1920],{"class":1913},[1828,6850,1923],{"class":1913},[1828,6852,3975],{"class":1913},[1828,6854,1929],{"class":1850},[1828,6856,3980],{"class":1932},[1828,6858,2028],{"class":1850},[1828,6860,6861],{"class":1913},"va",[1828,6863,2033],{"class":1850},[1828,6865,6866,6868],{"class":1830,"line":1903},[1828,6867,2399],{"class":1952},[1828,6869,6870],{"class":1913}," data\n",[1828,6872,6873],{"class":1830,"line":1910},[1828,6874,1900],{"class":1850},[3271,6876,995],{"id":6877},"handle-missing-fields",[1819,6879,6881],{"className":1821,"code":6880,"language":1823,"meta":35,"style":35},"func LoadAtom(data []byte) (*atom.Atom, error) {\n    var a atom.Atom\n    if err := json.Unmarshal(data, &a); err != nil {\n        return nil, err\n    }\n\n    // Initialize nil maps\n    if a.Strings == nil {\n        a.Strings = make(map[string]string)\n    }\n    // ...\n\n    return &a, nil\n}\n",[1825,6882,6883,6918,6930,6964,6974,6978,6982,6987,7004,7032,7036,7041,7045,7057],{"__ignoreMap":35},[1828,6884,6885,6887,6890,6892,6894,6896,6898,6900,6902,6904,6906,6908,6910,6912,6914,6916],{"class":1830,"line":9},[1828,6886,2173],{"class":1839},[1828,6888,6889],{"class":1932}," LoadAtom",[1828,6891,2028],{"class":1850},[1828,6893,3966],{"class":2181},[1828,6895,5936],{"class":1850},[1828,6897,5939],{"class":1843},[1828,6899,2067],{"class":1850},[1828,6901,3121],{"class":1850},[1828,6903,3127],{"class":1952},[1828,6905,1735],{"class":1843},[1828,6907,1929],{"class":1850},[1828,6909,12],{"class":1843},[1828,6911,1917],{"class":1850},[1828,6913,3152],{"class":1843},[1828,6915,2067],{"class":1850},[1828,6917,1851],{"class":1850},[1828,6919,6920,6922,6924,6926,6928],{"class":1830,"line":20},[1828,6921,6601],{"class":1839},[1828,6923,2265],{"class":1913},[1828,6925,1926],{"class":1843},[1828,6927,1929],{"class":1850},[1828,6929,4046],{"class":1843},[1828,6931,6932,6934,6936,6938,6940,6942,6944,6946,6948,6950,6952,6954,6956,6958,6960,6962],{"class":1830,"line":41},[1828,6933,2570],{"class":1952},[1828,6935,2550],{"class":1913},[1828,6937,1923],{"class":1913},[1828,6939,3975],{"class":1913},[1828,6941,1929],{"class":1850},[1828,6943,4061],{"class":1932},[1828,6945,2028],{"class":1850},[1828,6947,3966],{"class":1913},[1828,6949,1917],{"class":1850},[1828,6951,1953],{"class":1952},[1828,6953,1740],{"class":1913},[1828,6955,4074],{"class":1850},[1828,6957,2550],{"class":1913},[1828,6959,2575],{"class":1952},[1828,6961,2578],{"class":1839},[1828,6963,1851],{"class":1850},[1828,6965,6966,6968,6970,6972],{"class":1830,"line":1190},[1828,6967,6680],{"class":1952},[1828,6969,2578],{"class":1839},[1828,6971,1917],{"class":1850},[1828,6973,5234],{"class":1913},[1828,6975,6976],{"class":1830,"line":1195},[1828,6977,2298],{"class":1850},[1828,6979,6980],{"class":1830,"line":1879},[1828,6981,1907],{"emptyLinePlaceholder":1906},[1828,6983,6984],{"class":1830,"line":1888},[1828,6985,6986],{"class":1833},"    // Initialize nil maps\n",[1828,6988,6989,6991,6993,6995,6997,7000,7002],{"class":1830,"line":1897},[1828,6990,2570],{"class":1952},[1828,6992,2265],{"class":1913},[1828,6994,1929],{"class":1850},[1828,6996,2270],{"class":1913},[1828,6998,6999],{"class":1952}," ==",[1828,7001,2578],{"class":1839},[1828,7003,1851],{"class":1850},[1828,7005,7006,7009,7011,7013,7015,7017,7019,7022,7024,7026,7028,7030],{"class":1830,"line":1903},[1828,7007,7008],{"class":1913},"        a",[1828,7010,1929],{"class":1850},[1828,7012,2270],{"class":1913},[1828,7014,2796],{"class":1913},[1828,7016,4922],{"class":2585},[1828,7018,2028],{"class":1850},[1828,7020,7021],{"class":1839},"map",[1828,7023,1935],{"class":1850},[1828,7025,4179],{"class":1843},[1828,7027,2793],{"class":1850},[1828,7029,4179],{"class":1843},[1828,7031,2033],{"class":1850},[1828,7033,7034],{"class":1830,"line":1910},[1828,7035,2298],{"class":1850},[1828,7037,7038],{"class":1830,"line":1944},[1828,7039,7040],{"class":1833},"    // ...\n",[1828,7042,7043],{"class":1830,"line":2014},[1828,7044,1907],{"emptyLinePlaceholder":1906},[1828,7046,7047,7049,7051,7053,7055],{"class":1830,"line":2036},[1828,7048,2399],{"class":1952},[1828,7050,1953],{"class":1952},[1828,7052,1740],{"class":1913},[1828,7054,1917],{"class":1850},[1828,7056,6721],{"class":1839},[1828,7058,7059],{"class":1830,"line":2041},[1828,7060,1900],{"class":1850},[1811,7062,64],{"id":7063},"next-steps",[3014,7065,7066,7073],{},[3017,7067,7068,7072],{},[1740,7069,7071],{"href":7070},"migrations","Migrations Cookbook"," - Schema evolution",[3017,7074,7075,7078],{},[1740,7076,1141],{"href":7077},"../reference/api"," - Complete API",[3396,7080,7081],{},"html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}",{"title":35,"searchDepth":20,"depth":20,"links":7083},[7084,7085,7090,7091,7092,7096,7100,7101,7102,7103,7107],{"id":3861,"depth":20,"text":6},{"id":3866,"depth":20,"text":915,"children":7086},[7087,7088,7089],{"id":3869,"depth":41,"text":919},{"id":4027,"depth":41,"text":924},{"id":4132,"depth":41,"text":929},{"id":4383,"depth":20,"text":934},{"id":4478,"depth":20,"text":939},{"id":4779,"depth":20,"text":944,"children":7093},[7094,7095],{"id":4782,"depth":41,"text":948},{"id":5241,"depth":41,"text":953},{"id":5434,"depth":20,"text":958,"children":7097},[7098,7099],{"id":5437,"depth":41,"text":962},{"id":5785,"depth":41,"text":967},{"id":5893,"depth":20,"text":972},{"id":6308,"depth":20,"text":977},{"id":6543,"depth":20,"text":982},{"id":6728,"depth":20,"text":406,"children":7104},[7105,7106],{"id":6731,"depth":41,"text":990},{"id":6877,"depth":41,"text":995},{"id":7063,"depth":20,"text":64},{},"2025-01-01T00:00:00.000Z",null,{"title":902,"description":904},[3327,3856,7113,6337],"encoding","2025-01-06T00:00:00.000Z","eU5q3R6NOBWoKb8q--37NABCzvZYtor5gORRRhJR-uo",[7117,7118],{"title":689,"path":816,"stem":1683,"description":818,"children":-1},{"title":1004,"path":1003,"stem":1687,"description":1006,"children":-1},1776268116448]