plexus-macros
Procedural macros for Plexus RPC activations - the source of truth for the entire client generation pipeline.
Overview: Macros → Client Libraries
These macros are the entry point for the complete Plexus code generation pipeline:
#[hub_methods] Rust Macros (this crate)
↓ schemars::schema_for!()
JSON Schema (PluginSchema + MethodSchema)
↓ Synapse (Haskell) fetches via WebSocket
Synapse IR (deduplicated, compiler-ready)
↓ hub-codegen (Rust) generates code
TypeScript/Rust Client Libraries
↓ synapse-cc (Haskell) integrates
Production-Ready Clients (type-safe, documented)
Critical: Everything downstream (TypeScript clients, Python clients, OpenAPI docs) depends on what these macros expose in the PluginSchema. The macro is the source of truth for:
- Type structure (enums, structs, fields)
- Method signatures (params, returns, streaming)
- Documentation (descriptions, examples)
- Validation rules (constraints, formats)
- API metadata (HTTP methods, tags, deprecation)
See PIPELINE_OVERVIEW.md for the complete flow.
What is Plexus RPC?
Plexus RPC is a protocol for building services with runtime schema introspection. Unlike traditional RPC systems that require separate schema definitions, Plexus RPC extracts schemas directly from your code. This ensures zero drift between your implementation and schema.
Key principle: Your Rust function signature is the schema - no separate IDL files needed.
Current State (hub_methods v1)
Quick Start
use *;
This generates:
BashMethodenum for schema extractionActivationtrait implementation- RPC dispatch logic
- Schema introspection endpoint
- Constants:
NAMESPACE,VERSION,PLUGIN_ID
Current Macro Attributes
#[hub_methods] - Impl Block Level
Required:
namespace = "..."- The activation namespace
Optional:
version = "..."- Semantic version (default: CARGO_PKG_VERSION)description = "..."- Short description (max 15 words)long_description = "..."- Detailed documentationcrate_path = "..."- Import path (default: "crate")hub- This activation has child activationsresolve_handle- Generate handle resolution methodplugin_id = "..."- Explicit UUID (otherwise auto-generated)
#[hub_method] - Method Level
Optional:
name = "..."- Override method namestreaming- Method yields multiple events over timehttp_method = "GET|POST|PUT|DELETE|PATCH"- HTTP verb for REST APIparams(name = "description", ...)- Parameter descriptions (awkward!)returns(Variant1, Variant2, ...)- Filter enum variantsbidirectional- Method uses bidirectional channel
Problems with Current Macros
See MACRO_ANALYSIS.md for detailed analysis. Key issues:
- Too much implicit behavior - Magic parameter skipping (ctx), auto-injected schema method
- Poor naming - "hub" is overloaded, generated names are hidden
- Complex implementation - 1,825 lines with deeply nested parsing
- Weak metadata - Missing doc comments, examples, validation constraints
- Awkward syntax -
params(name = "desc")is verbose and error-prone
Result: Clients get minimal documentation and no validation!
Proposed Macro v2 Syntax
See TARGET_DX.md for complete specification. Key improvements:
Explicit, Clear, Type-Safe
Benefits:
- Doc comments → descriptions (like clap)
- Inline parameter docs (no duplication)
- Validation rules declared (like validator)
- Examples for documentation and tests
- Everything explicit (no magic)
- Strongly-typed enums everywhere
Generated TypeScript:
/**
* Execute a bash command and stream output
*
* This runs commands in a bash shell and streams stdout/stderr
* as it becomes available.
*
* @example
* ```typescript
* for await (const event of client.bash.execute("ls -la", 30)) {
* if (isBashEventStdout(event)) {
* console.log(event.data);
* }
* }
* ```
*
* @param command - Shell command to execute (example: "ls -la")
* @param timeout - Maximum execution time in seconds (default: 30)
* @returns Stream of bash events
* @tags shell, execution
*/
execute(command: string, timeout?: number): AsyncGenerator<BashEvent>;
See DX_INSPIRATION.md for patterns borrowed from clap, serde, axum, utoipa, and validator.
What Must Flow Through the Pipeline
For client libraries to be fully type-safe and well-documented, the macro must extract:
Type Structure (✅ Currently Working)
- ✅ Enum discriminators (which field is the tag)
- ✅ Variant names and fields
- ✅ Format hints (uuid, int32, uint64, date-time)
- ✅ Required vs optional fields
- ✅ Type references (cross-namespace imports)
- ✅ Streaming semantics (AsyncGenerator vs Promise)
- ✅ HTTP method for REST endpoints
Documentation (❌ Currently Missing)
- ❌ Method descriptions (from doc comments)
- ❌ Long descriptions (multiple paragraphs)
- ❌ Parameter descriptions (inline docs)
- ❌ Examples (JSON/code blocks)
- ❌ Deprecation warnings
- ❌ Tags for grouping
Validation (❌ Currently Missing)
- ❌ Email/URL/UUID format validation
- ❌ String length constraints
- ❌ Numeric range constraints
- ❌ Regex pattern matching
- ❌ Enum value constraints
- ❌ Default values
Metadata (❌ Currently Missing)
- ❌ Response documentation (success/error cases)
- ❌ Security requirements (auth level)
- ❌ Performance hints (caching, rate limits)
- ❌ Sensitive field markers (don't log)
These must be added to PluginSchema/MethodSchema for downstream tools to generate rich clients.
Schema Introspection
Methods generated by #[hub_methods] are automatically discoverable via Plexus RPC's schema introspection:
# Using the synapse CLI
# Or via raw JSON-RPC
{
}
This returns the complete PluginSchema with all methods, extracted from your Rust types.
Integration with Plexus Ecosystem
This crate is part of the Plexus RPC ecosystem:
Core Infrastructure
- plexus-core - Core
Activationtrait, streaming protocol - plexus-macros - This crate - procedural macros
- plexus-transport - WebSocket, HTTP REST, MCP transports
Code Generation Pipeline
- synapse (Haskell) - Schema fetcher, IR generator
- hub-codegen (Rust) - Stateless code generator (TypeScript, Rust)
- synapse-cc (Haskell) - Orchestrator (merge, deps, build)
Clients & Tools
- synapse CLI - Interactive CLI for Plexus backends
- Generated clients - TypeScript, Python, Rust (from hub-codegen)
See PIPELINE_OVERVIEW.md for how these pieces fit together.
Documentation Files
- README.md - This file (overview + usage)
- MACRO_ANALYSIS.md - Detailed analysis of current macro problems
- TARGET_DX.md - Complete specification for macro v2
- DX_INSPIRATION.md - Patterns from clap, serde, axum, etc.
- TYPE_EXTRACTION.md - How types flow through schemars
- PIPELINE_OVERVIEW.md - Complete Rust → TypeScript flow
Examples
See the substrate reference server for complete examples:
bash/- Shell command execution with streamingarbor/- Conversation tree storagecone/- LLM orchestration with bidirectional channels
Current Status
v1 Macros (stable): Working but limited metadata extraction v2 Macros (planned): Explicit syntax, rich metadata, full documentation
The v2 macros will be developed as siblings in the same crate, allowing gradual migration:
// Both work simultaneously
// v1 (deprecated)
// v2 (recommended)
Migration tooling (codemod) will be provided for automated conversion.
Contributing
When adding features to the macros, ensure they:
- Preserve type fidelity through the entire pipeline
- Extract all relevant metadata into PluginSchema
- Generate clear error messages with examples
- Maintain backward compatibility where possible
- Update downstream tools (synapse IR, hub-codegen)
The macro is the source of truth - everything flows from here!
License
MIT