ADR-0002: extract shared types into typub-core subcrate
Status: accepted | Date: 2026-02-13
References: RFC-0002, RFC-0005
Context
build.rs and the main typub crate both need capability types (MathRendering, AssetStrategy, etc.) but cannot share them because build.rs runs at build time and cannot depend on the crate being compiled.
Problem Statement
build.rs deserializes TOML capability fields as String and manually maps them to Rust enum expression strings (e.g., math_rendering_expr("svg") → "MathRendering::Svg"). This is fragile — typos in TOML surface as panics in match arms rather than serde errors, and there is no compile-time guarantee that build-time and runtime enum definitions stay in sync. Theme IDs are also bare String/&str with no type distinction from other strings.
Constraints
- RFC-0002 defines the pipeline stages including capability resolution
- RFC-0005 defines the configuration hierarchy including theme resolution
build.rsgenerates static&'staticdata — the shared types must be lightweight enough for both build-time deserialization and runtime use- Existing
use crate::adapters::{MathRendering, ...}import paths should remain valid via re-exports
Options Considered
- Extract enums to a
typub-coresubcrate (chosen) - Keep string-based build.rs with better validation
- Use proc-macros to generate enums from TOML
Decision
We will extract capability enums and ThemeId newtype into a crates/typub-core/ subcrate because:
- Single source of truth: Enum variants are defined once and shared by both
build.rs(build-dependency) and the main crate (normal dependency). - Serde validation at build time: TOML strings are deserialized into typed enums via serde, catching typos immediately with clear error messages instead of panics in match arms.
- Type safety for theme IDs:
ThemeId(String)newtype prevents accidental confusion between theme IDs and other strings.
Implementation Notes
- Each enum implements
code_expr(&self) -> &'static strfor build-time code generation CapabilitySupportandDraftSupportneed custom serde deserializers due to flattened TOML representations- The main crate re-exports all
typub-coretypes from their original module paths to avoid import churn ThemeIdimplementsDeref<Target = str>for ergonomic use at call sites
Consequences
Positive
- Typos in
adapters.tomlproduce clear serde deserialization errors at build time - Adding a new enum variant only requires editing
typub-core;build.rsstring-mapping functions are eliminated ThemeIdprevents passing arbitrary strings where theme IDs are expected- Workspace structure enables future subcrate extraction if needed
Negative
- Additional crate adds marginal compilation overhead (mitigation:
typub-coreis tiny with minimal dependencies — onlyserde) - Workspace conversion changes
Cargo.lockpath behavior (mitigation: Cargo handles this transparently)
Neutral
- Existing import paths (
use crate::adapters::MathRendering) continue to work via re-exports — no downstream churn