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
  • properties — Empty 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.