# Toon — Implementation Architecture

Internal design of `AcToonSerializer`. Read this when modifying the serializer. End users should stay in `TOON_OPTIONS.md` and `TOON_ATTRIBUTES.md`.

## Partial class breakdown

`AcToonSerializer` is a `static partial class` split across topical files for cohesion. Each file contributes methods to the same class — no separate types, no inheritance.

| File | Responsibility |
|---|---|
| `AcToonSerializer.cs` | Public API (`Serialize`, `SerializeTypeMetadata`, `SerializeMetadata`), primitive fast-path, reference scanning, per-type metadata cache. |
| `AcToonSerializer.ToonSerializationContext.cs` | Per-serialization mutable state: buffer, indent level, reference maps. |
| `AcToonSerializer.ToonSerializeTypeMetadata.cs` | Per-type cached reflection data: properties, display names, navigation. |
| `AcToonSerializer.MetaWriter.cs` | `@meta` + `@types` orchestration, type collection, enum backing-field detection. |
| `AcToonSerializer.TypeDefinitions.cs` | Per-type schema emission — property metadata, navigation, enum definitions. |
| `AcToonSerializer.DataSection.cs` | `@data` emission, value dispatcher, object/collection/dict/primitive writers, multi-line strings. |
| `AcToonSerializer.Descriptions.cs` | Smart-inference pattern catalog, fallback chain resolvers. |
| `AcToonSerializer.Attributes.cs` | Attribute processing entry points. |
| `AcToonSerializer.AttributeExtraction.cs` | DataAnnotations → constraint string extraction. |
| `AcToonSerializer.Placeholders.cs` | `[#Name]` placeholder resolution. |
| `AcToonSerializer.TopologicalSort.cs` | Dependency-first type ordering. |
| `AcToonSerializer.Navigation.cs` | Navigation property metadata (conventions + attributes). |
| `AcToonSerializer.ForeignKeys.cs` | FK property detection, inverse property matching. |
| `AcToonSerializer.Validation.cs` | Output validation / sanity checks. |

Supporting non-partial files:

| File | Role |
|---|---|
| `AcToonSerializerOptions.cs` | Options POCO + preset factories + mode enum. |
| `AcToonContextBase.cs` | Shared base between serialization contexts. |
| `ToonDescriptionAttribute.cs` | Attribute definition + `ToonRelationType` enum. |
| `ToonTypeRelation.cs` | Type relation string constants. |

## Context pattern

```
AcSerializerContextBase<TMetadata, TOptions>       (Serializers/ root)
  └─ AcToonContextBase                              (Toons/ shared base)
       └─ ToonSerializationContext                  (sealed, per-call)
```

`ToonSerializationContext` responsibilities:
- Owns the output `StringBuilder` and current indent level.
- Tracks references by **reference identity** (`Dictionary`/`HashSet` + `ReferenceEqualityComparer`), not the shared `IdentityMap`; `OnlyId` behaves as `All` — see `TOON_ISSUES.md#accore-toon-b-r8k3`.
- Pooled via `ToonSerializationContextPool.Get(options)` (calls `Reset`) / `Return(context)` (calls `Clear`) — reused across serializations.

`ToonSerializeTypeMetadata` (per-type, cached globally in `AcToonSerializer.MetadataCache`):
- Property accessors (`ToonPropertyAccessor`), display names, navigation metadata.
- `CustomDescription` — the `ToonDescriptionAttribute` instance if present.
- Flags: `IsCollection`, `IsDictionary`, `ElementType`.

Shared infrastructure (metadata cache, `IdentityMap`, `ReferenceTracker`): see `../../Serializers/README.md`.

## Serialization flow

```
Serialize(value, options)
  ├─ null check → "null"
  ├─ TrySerializePrimitiveDirect (fast-path, no context allocated)
  ├─ Get context from pool
  ├─ ScanReferences  (if ReferenceHandling != None)   ← marks multi-referenced objects
  ├─ switch Mode
  │   ├─ MetaOnly → WriteMetaSectionOnly
  │   ├─ DataOnly → WriteDataSectionOnly
  │   └─ Full    → WriteMetaSection + blank line + WriteDataSection
  └─ Return context to pool
```

## Type collection (`CollectTypes`)

Recursive traversal starting from the root type:

- Enums: added, not further traversed.
- Primitives and strings: skipped.
- Already-visited types: skipped (`HashSet` dedup).
- Dictionaries: traverse key type + value type.
- Collections (non-string `IEnumerable`): traverse element type.
- Objects: traverse each declared property's type.

Additional pass: `DetectAndCollectEnumBackingFields` scans for the `Status` + `StatusId` pattern (see `TOON_INFERENCE.md#enum-backing-field-detection`).

After collection, `TopologicalSortTypes` orders the set so dependencies come before dependents. Filter applied before sorting:
- Drops primitives, strings.
- Drops `IEnumerable`-implementing non-string types (framework collection machinery).
- Drops generic type definitions.
- Drops well-known collection prefixes (`List`, `ICollection`, `IEnumerable`, `Dictionary`).

The sorted order is used both for the `@meta.types` list and for the `@types` body emission.

## Reference scanning

Pre-pass when `ReferenceHandling != None`. Walks every reachable object exactly once via `TrackForScanning`. An object visited a second time is flagged as "multi-referenced" and assigned an ID.

⚠️ Reference identity only (same for `OnlyId`/`All`); same-`Id` distinct instances are not deduplicated — `TOON_ISSUES.md#accore-toon-b-r8k3`.

During `@data` emission:
- First visit of a multi-referenced object → `@N TypeName { ... }`.
- Subsequent visits → `@ref:N`.

Circular cycles are safe — the second visit aborts with the `@ref:` form. When reference handling is disabled, shared subgraphs serialize redundantly and true cycles truncate at `MaxDepth` to `null`. Note: the base `AcSerializerOptions.MaxDepthBehavior` (`Truncate`/`Throw`/`Disable`) is **not currently dispatched** in Toon — depth-limit handling is always silent-null truncation regardless of the setting (see `TOON_ISSUES.md#accore-toon-i-s4q9`, `TOON_TODO.md#accore-toon-t-w8b5`).

## Enum output

Enum values serialize as their **numeric underlying-type value** (not the member name) for token efficiency. The name ↔ value map lives in the `@types` `values:` sub-list; LLMs must cross-reference.

When `UseEnhancedMetadata = false`, only the bare name/value list is emitted — no description, purpose, underlying-type, or default-value fields.

## Buffer management

The context's `StringBuilder` is shared with write helpers:

- `Write(string)` / `Write(char)` — append, no newline.
- `WriteLine(string)` / `WriteLine()` — append with `\r\n`.
- `WriteIndent()` — emit `CurrentIndentLevel × IndentString`.
- `WriteIndentedLine(string)` — indent + content + newline.
- `WriteProperty(name, value)` — `@meta` helper for `name = value` lines (honors `UseIndentation` for `=` spacing).

`GetResult()` returns the final string and resets the context for pool reuse.

## Key design decisions

- **`static partial class`** — no instance state; per-call state lives only in `ToonSerializationContext`.
- **Sealed context classes** — enables JIT direct calling on hot paths (no virtual dispatch).
- **Context pooling** — repeat serializations allocate no context.
- **Cached per-type metadata** — reflection runs once per type, then hot cache.
- **Parallel code paths for `UseIndentation`** — compact mode avoids space emission on `=` and skips blank lines at write time (not a post-process strip).
- **Fast-path for primitive values** — `Serialize(value)` with a primitive returns without pool access or reflection.
- **No deserialization** — Toon is intentionally one-way. See `TOON_TODO.md#accore-toon-t-f3x1`.
