Skip to main content

Discovery System

The discovery system is responsible for finding Swift types that should have JSON Schemas generated. It uses two complementary strategies that are coordinated by DiscoveryCoordinator.

DiscoveryCoordinator

File: Sources/Discovery/DiscoveryCoordinator.swift

DiscoveryCoordinator is the entry point for the discovery phase. It:

  1. Decides which Swift files to scan (all files, or --macro-only filtered)
  2. Runs MacroDiscovery and ProtocolDiscovery in sequence
  3. Merges and deduplicates results from both strategies
  4. Returns a [ModelNode] list to the next pipeline stage
class DiscoveryCoordinator {
func discover(in sourcePath: String, macroOnly: Bool) throws -> [ModelNode]
}

--macro-only Filtering

When --macro-only is passed, DiscoveryCoordinator uses FileScanner to pre-filter source files. Only files containing the literal string @ChimeraSchema are included in the scan set. This is a plain text search — no parsing required — and can dramatically reduce scan time for large codebases.


MacroDiscovery

File: Sources/Discovery/MacroDiscovery.swift

MacroDiscovery uses SwiftSyntax to parse source files and find @ChimeraSchema annotations. It delegates to MacroDiscoveryVisitor to walk the AST.

MacroDiscoveryVisitor

File: Sources/Parser/Syntax/MacroDiscoveryVisitor.swift

A SyntaxVisitor subclass that visits every StructDeclSyntax and ClassDeclSyntax. For each type declaration:

  1. Checks attributes for @ChimeraSchema(key:) and @ChimeraMetaData(description:)
  2. If found, creates a partial ModelNode stub with the type name, key, description, and source location
  3. Does not resolve properties at this stage (that happens in parsing)

What MacroDiscovery Finds

  • Any struct or class annotated with @ChimeraSchema
  • Associated @ChimeraMetaData annotation on the same type
  • The source file path for later parsing

ProtocolDiscovery

File: Sources/Discovery/ProtocolDiscovery.swift

ProtocolDiscovery finds types implementing protocols annotated with @PolymorphicMapping. Because protocol conformances can be split across extensions in different files, it uses IndexStoreDB (via InheritanceRepository) rather than pure text scanning.

How It Works

  1. Calls InheritanceRepository to look up all types conforming to each polymorphic protocol
  2. For each conforming type, checks whether it also has @ChimeraSchema (via MacroRepository)
  3. Builds the PolymorphicInfo structure for the protocol, mapping discriminator values to concrete ModelNode stubs

KnotDiscovery

File: Sources/Discovery/KnotDiscovery.swift

An alternative discovery strategy for a custom annotation format (useful if your project uses a non-macro approach). Operates similarly to MacroDiscovery but looks for a different annotation pattern.


Repository Layer

The discovery system is backed by two repositories:

SourceFileRepository

File: Sources/Repository/FileSystem/SourceFileRepository.swift

Provides the list of Swift source files to scan. Accepts a root sourcePath and returns all .swift files recursively. Handles symlinks and ignores hidden directories.

InheritanceRepository

File: Sources/Repository/IndexStore/InheritanceRepository.swift

Wraps IndexStoreDB to look up type inheritance relationships:

class InheritanceRepository {
func conformingTypes(to protocolName: String) throws -> [String]
func superclassChain(for typeName: String) throws -> [String]
}

IndexStoreDB is a compile-time artifact generated by the Swift compiler. It stores symbol relationships (inheritance, method calls, etc.) in a binary database at .build/debug/index/.

MacroRepository

File: Sources/Repository/MacroRepository.swift

A cache of @ChimeraSchema annotations indexed by type name. Populated during MacroDiscovery and queried by ProtocolDiscovery to check whether a protocol conformer is also a @ChimeraSchema.


SymbolRepository

File: Sources/Repository/IndexStore/SymbolRepository.swift

Used to resolve symbol locations for types encountered as property types. When a property is typed as SomeModel, SymbolRepository maps "SomeModel" to its source file path so it can be parsed.


Discovery Output

After discovery, the pipeline has a flat [ModelNode] array where each node has:

  • typeName — Swift type name
  • key@ChimeraSchema(key:) value
  • description — From @ChimeraMetaData, or nil
  • sourceFile — Absolute path to the Swift file
  • propertiesEmpty at this stage (filled by the Parser stage)
  • polymorphicInfo — Filled if the type is used in a @PolymorphicMapping

The Parser stage takes this list and fills in properties for each node.