canonrs-core 0.1.0

CanonRS core types, traits and primitives
docs.rs failed to build canonrs-core-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

canonrs-core

Single Source of Truth for the CanonRS design system. All metadata, constraints, and API contracts are generated from *_ui.rs headers.


Architecture

*_ui.rs  (@canon-* headers + Leptos props)
        ↓
    build.rs  (orchestrates all generators)
        ↓
src/generated/
  ├── catalog.rs            — CatalogEntry with kind, parts, regions, accept rules
  ├── component_meta.rs     — ComponentMeta with intent, capabilities, required parts
  ├── block_meta.rs         — BlockMeta for all blocks and layouts
  ├── block_definitions.rs  — BlockDefinition with regions, props, presets, AcceptRule
  ├── layout_definitions.rs — LayoutDefinition with slots and descriptions
  ├── component_definitions.rs — ComponentDefinition unifying meta + catalog
  ├── llm_components.md     — LLM context for UI components
  ├── llm_blocks.md         — LLM context for blocks
  └── llm_layouts.md        — LLM context for layouts

SSOT Pipeline

The single source of truth flows in one direction only:

NEVER edit generated files manually
ALWAYS edit *_ui.rs headers or Leptos props

Component headers (*_ui.rs)

Every UI component declares its contract via @canon-* doc comments:

//! @canon-id: button
//! @canon-label: Button
//! @canon-family: interactive
//! @canon-category: Action
//! @canon-intent: Trigger an action or event
//! @canon-description: Action button with variant and size
//! @canon-composable: false
//! @canon-capabilities: Disabled
//! @canon-required-parts:
//! @canon-optional-parts:
//! @canon-tags: button, action, submit, click, cta

Block/Layout headers

Blocks and layouts extend the schema with structural metadata:

//! @canon-id: card
//! @canon-type: block
//! @canon-category: layout
//! @canon-variant: structure
//! @canon-container: true
//! @canon-regions: header, content, footer
//! @canon-label: Card
//! @canon-description: Container with header/content/footer regions
//! @canon-tags: card, container, box, content
//! @canon-slot-accepts: header=Any,content=Any,footer=Action

Generated Artifacts

catalog.rs

Unified registry of all components, blocks, and layouts with:

  • CatalogKindComponent | Block | Layout
  • CatalogAcceptRule — declarative accept rules per entry
  • CatalogRegionRule — per-region accept rules derived from @canon-slot-accepts
CatalogEntry {
    id: "block.card",
    kind: CatalogKind::Block,
    category: CatalogCategory::Layout,
    parts: &[],
    regions: &["header", "content", "footer"],
    accepts: &[CatalogAcceptRule::AnyComponent, CatalogAcceptRule::AnyBlock],
    region_rules: &[
        CatalogRegionRule { region: "header",  accepts: &[CatalogAcceptRule::AnyComponent] },
        CatalogRegionRule { region: "content", accepts: &[CatalogAcceptRule::AnyComponent, CatalogAcceptRule::AnyBlock] },
        CatalogRegionRule { region: "footer",  accepts: &[CatalogAcceptRule::ComponentCategory(CatalogCategory::Action)] },
    ],
}

component_meta.rs

Runtime statics for AI/RAG/Decision Engine:

pub static ACCORDION_META: ComponentMeta = ComponentMeta {
    id: "accordion",
    name: "Accordion",
    family: ComponentFamily::Layout,
    intent: "Expand and collapse content sections",
    capabilities: &[Capability::OpenClose, Capability::Multiple],
    composable: true,
    required_parts: &["AccordionItem", "AccordionTrigger", "AccordionContent"],
    optional_parts: &[],
};

block_definitions.rs

Full structural definitions for blocks with semantic AcceptRule per region:

BlockDefinition {
    id: "card",
    regions: &[
        BlockRegion { id: "header",  accepts: &[AcceptRule::Any], layout: RegionLayout::Vertical },
        BlockRegion { id: "content", accepts: &[AcceptRule::Any], layout: RegionLayout::Vertical },
        BlockRegion { id: "footer",  accepts: &[AcceptRule::Category(BlockCategory::Form)], layout: RegionLayout::Horizontal },
    ],
    ...
}

api.rs (per component)

Generated in canonrs-server/src/ui/*/api.rs:

pub const BUTTON_API: ComponentApi = ComponentApi {
    id: "button",
    props: &[
        PropDef { name: "children", kind: PropType::Children, required: true, default: None },
        PropDef { name: "variant",  kind: PropType::Enum(&["primary","solid","secondary",...]), required: false, default: Some("primary") },
        PropDef { name: "size",     kind: PropType::Bool, required: false, default: Some("md") },
        PropDef { name: "disabled", kind: PropType::Bool, required: false, default: Some("false") },
    ],
};

Constraint Engine

src/infra/constraint_engine.rs — validates composition at runtime:

// Can a block be inserted into a region?
ConstraintEngine::can_insert("card", "footer", "button", 0)
// → ValidationResult::Valid

// Does a component have all required parts?
ConstraintEngine::validate_parts("accordion", &["AccordionItem", "AccordionTrigger"])
// → ValidationResult::Invalid([MissingParts { missing: ["AccordionContent"] }])

// What can go into a region?
ConstraintEngine::valid_children_for("card", "content")
// → ["button", "input", "badge", ...]

// Can a catalog entry nest inside another?
ConstraintEngine::catalog_can_nest_in_region(&dashboard, "header", &button)
// → true (header accepts Navigation category)

Running tests

cargo test constraint_engine

Build Generators

Located in build/generators/:

Generator Output Source
gen_catalog.rs catalog.rs @canon-* headers
gen_meta.rs component_meta.rs, block_meta.rs @canon-* headers
gen_definitions.rs block_definitions.rs, layout_definitions.rs @canon-* headers + props
gen_api/ */api.rs Leptos #[component] props
gen_component_definitions.rs component_definitions.rs @canon-* headers
gen_llm.rs llm_*.md All of the above

Adding a New Component

  1. Create canonrs-server/src/ui/<name>/<name>_ui.rs with @canon-* headers
  2. Implement the Leptos #[component]
  3. Run cargo build — all generated files update automatically

Adding a New Block

  1. Create canonrs-server/src/blocks/<name>/<name>_block.rs with @canon-* headers
  2. Add @canon-slot-accepts for region rules
  3. Run cargo build

LLM Context

Three markdown files are generated for LLM consumption:

  • llm_components.md — intent, capabilities, relationships, usage context
  • llm_blocks.md — regions, props, presets, region rules, typical children
  • llm_layouts.md — slots, slot rules, use when context

These files are the recommended input for AI page generation tasks.


Crate Structure

canonrs-core/
├── build/
│   ├── types.rs         — shared build types
│   ├── utils.rs         — helpers (pascal_to_kebab, to_const_name)
│   ├── parsers.rs       — parse @canon-* headers, props, presets
│   └── generators/      — all code generators
├── src/
│   ├── infra/
│   │   ├── constraint_engine.rs  — runtime validation engine
│   │   ├── state_engine.rs       — aria-* / data-rs-state
│   │   └── dom_contract.rs       — required parts validation
│   ├── primitives/      — 82 pure HTML primitives
│   ├── generated/       — auto-generated (do not edit)
│   ├── catalog_types.rs — CatalogEntry, CatalogAcceptRule, PropType
│   ├── block_types.rs   — BlockDefinition, AcceptRule, BlockRegion
│   ├── meta_types.rs    — ComponentMeta, Capability, ComponentFamily
│   └── lib.rs
└── build.rs             — orchestrates all generators