A powerful, template-driven code generation tool for Protocol Buffers
Project description
๐ง Wizard Codegen
A powerful, template-driven code generation tool for Protocol Buffers
Wizard Codegen transforms your .proto definitions into typed code for multiple programming languages using
customizable Jinja2 templates. Whether you're building React components, Swift structs, Kotlin data classes, or Go
handlers โ this tool has you covered.
๐ Table of Contents
- Features
- Quick Start
- Installation
- CLI Usage
- Configuration
- Template Authoring
- Custom Hooks
- Architecture
- Developer Guide
- Examples
- Troubleshooting
- License
โจ Features
| Feature | Description |
|---|---|
| Multi-Language Support | Generate code for TypeScript, Swift, Kotlin, Go, and more |
| Jinja2 Templates | Full power of Jinja2 templating with custom filters |
| Flexible Filtering | Target specific messages, enums, or services with where clauses |
| Runtime Name Filter | Generate only specific items from CLI: wizard-codegen generate SampleProtoPage |
| Annotation Filtering | Filter messages by custom proto annotations/options (e.g., has_option: "*.page") |
| Git Proto Sources | Fetch proto definitions directly from Git repositories |
| Multiple Write Modes | overwrite, append, or write-once file generation |
| Custom Hooks | Extend with your own Jinja filters and helpers |
| Name Transformations | Auto-convert between snake_case, kebab-case, PascalCase, camelCase, MACROCASE, MACRO_SNAKE_CASE, microcase |
| Dry Run Mode | Preview changes without writing files |
| Verbose Output | Debug with detailed proto and context inspection |
| Nested Type Support | Full support for nested messages and enums |
| Options Extraction | Access message, field, and enum options in templates |
๐ Quick Start
1. Install Prerequisites
Protocol Buffer Compiler (protoc):
# macOS
brew install protobuf
# Ubuntu/Debian
sudo apt install -y protobuf-compiler
# Verify installation
protoc --version
uv (Python package manager):
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or via Homebrew
brew install uv
# Verify installation
uv --version
2. Install Wizard Codegen
uv tool install wizard-codegen
3. Create Configuration
Create wizard/codegen.yaml in your project:
proto:
root: "path/to/your/protos" # Local path to proto files
cache_dir: ".cache/protos"
files:
- "**/*.proto"
targets:
typescript:
templates: "wizard/templates/ts"
out: "src/generated/ts"
render:
- template: "form.j2"
for_each: "message"
where:
all:
- name: "*Form"
output: "components/{{ item.name.kebab_case }}/index.tsx"
4. Generate Code
# Generate code
wizard-codegen generate
# Preview changes without writing files
wizard-codegen --dry-run generate
# Verbose output for debugging
wizard-codegen --verbose generate
๐ฆ Installation
Prerequisites
| Requirement | Description | Installation |
|---|---|---|
| Python 3.10+ | Runtime | python.org |
| uv | Fast Python package manager | See Quick Start or docs.astral.sh/uv |
| protoc | Protocol Buffer Compiler | See Quick Start or grpc.io |
| Git | For fetching proto sources | Usually pre-installed |
Install via uv (Recommended)
uv tool install wizard-codegen
Upgrade
uv tool upgrade wizard-codegen
Install from Source (for development)
git clone <internal-repo-url>
cd wizard-codegen
make deps # Install dependencies
make install # Install CLI tool
Upgrade from Source
make upgrade
Dependencies
Core dependencies (managed in pyproject.toml):
| Package | Version | Purpose |
|---|---|---|
| typer | 0.20.1 | CLI framework |
| pydantic | 2.12.5 | Configuration validation |
| jinja2 | 3.1.6 | Template engine |
| rich | 14.2.0 | Terminal output |
| protobuf | 6.33.2 | Proto descriptor handling |
| pyyaml | 6.0.3 | YAML configuration |
๐ฅ๏ธ CLI Usage
The CLI is built with Typer and provides rich, colorful output.
Commands
generate โ Generate code from protos
# Basic generation (all items)
wizard-codegen generate
# Generate only specific items by name
wizard-codegen generate SampleProtoPage
# Generate multiple specific items
wizard-codegen generate SampleProtoPage UserFormPage
# Use glob patterns to match item names
wizard-codegen generate "*Page"
# With custom config file
wizard-codegen --config path/to/codegen.yaml generate
# Dry run (preview without writing)
wizard-codegen --dry-run generate
# Verbose mode (detailed output)
wizard-codegen --verbose generate
# Combine flags and name filter
wizard-codegen --verbose --dry-run generate SampleProtoPage
The optional positional NAMES arguments filter which items are generated at runtime. This works alongside any where clauses in your config โ the config filters are applied first, then the name filter narrows the results further. Glob patterns (*Page, Sample*) and regex patterns are supported. When no names are provided, all matching items are generated as usual.
list-protos โ List discovered proto files
wizard-codegen list-protos
Output:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Available proto schemas โ
โโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Name โ Path โ
โโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ user โ user/user.proto โ
โ user_form โ user/user_form.proto โ
โ order โ order/order.proto โ
โโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
validate โ Validate templates and configuration
wizard-codegen validate
Validates:
- Configuration file syntax
- Proto file discovery
- Template syntax and variable resolution
- Descriptor set generation
Global Options
| Option | Short | Description |
|---|---|---|
--version |
-V |
Show version and exit |
--config |
-c |
Path to codegen YAML configuration (default: wizard/codegen.yaml) |
--verbose |
-v |
Enable detailed logging and context printing |
--dry-run |
Preview actions without writing files | |
--local |
-l |
Use local proto.root instead of git source |
--help |
Show help message |
โ๏ธ Configuration
Configuration is defined in YAML format. The default location is wizard/codegen.yaml.
Complete Example
proto:
# Git source configuration (used by default)
source:
git: "git@github.com:YourOrg/proto-definitions.git"
ref: "latest-tag" # Default: auto-resolves to latest semver tag
# fds: "build/descriptor.pb" # Optional: use pre-built descriptor
include_info: true # Include source info in descriptors
# Local path (used with --local flag for development)
root: "../local-protos"
# Cache directory for git clones
cache_dir: ".cache/proto-common"
# Include paths (relative to proto root)
includes:
- "{proto_root}/shared"
- "{proto_root}/service/wizard"
# File patterns to discover
files:
- "pages/**/*.proto"
- "models/*.proto"
targets:
typescript:
templates: "wizard/templates/ts"
out: "src/generated/ts"
render:
# Generate form components for *Form messages
- template: "form.j2"
for_each: "message"
mode: "write-once"
where:
all:
- name: "*Form"
not:
- file: "*/shared/design_system/*"
output: "components/{{ item.name.kebab_case }}-form/index.tsx"
# Generate context providers for *Context messages
- template: "context.j2"
for_each: "message"
where:
any:
- name: "*Context"
not:
- file: "*/shared/design_system/*"
output: "contexts/{{ item.name.kebab_case }}/index.tsx"
swift:
templates: "wizard/templates/swift"
out: "ios/Generated"
render:
- template: "struct.j2"
for_each: "message"
output: "{{ item.name.pascal_case }}.swift"
kotlin:
templates: "wizard/templates/kotlin"
out: "android/generated"
render:
- template: "data_class.j2"
for_each: "message"
output: "{{ item.name.pascal_case }}.kt"
hooks:
root: "wizard"
module: "hook_sample" # Optional: custom filters module
Proto Configuration
| Key | Type | Description |
|---|---|---|
root |
string |
Local path to proto files (used with --local flag) |
source.git |
string |
Git repository URL for proto source |
source.ref |
string |
Git ref: tag, branch, commit SHA, or latest-tag (default: latest-tag) |
source.fds |
string |
Path to pre-built FileDescriptorSet (optional) |
source.include_info |
bool |
Include source info in protoc output (default: true) |
cache_dir |
string |
Directory for caching git clones |
includes |
list[string] |
Include paths for proto imports |
files |
list[string] |
Glob patterns for proto file discovery |
Special Ref: latest-tag
Use ref: "latest-tag" to automatically checkout the latest semver tag from the repository:
proto:
source:
git: "git@github.com:YourOrg/proto-definitions.git"
ref: "latest-tag" # Automatically resolves to latest semver tag (e.g., v2.1.0)
This feature:
- Lists all tags in the repository
- Filters for valid semver tags (e.g.,
v1.2.3,1.0.0,v2.0.0-beta) - Prefers stable releases over prereleases (e.g.,
v1.0.0>v1.0.0-beta) - Checks out the highest version according to semver ordering
Proto Source Resolution
You can configure both a local path (proto.root) and a git source (proto.source) simultaneously. This allows
developers to easily switch between tagged releases and local development:
Default behavior (no flags):
proto.source.gitโ clone/fetch from git repository- Falls back to
proto.rootโ if no git source is configured - Error โ if neither is available
With --local flag:
proto.rootโ use local filesystem path- Error โ if
proto.rootis not configured or doesn't exist
This design enables a common workflow:
- CI/Production: Uses git source with
latest-tag(the default ref) - Local Development: Use
--localflag to test against local proto changes
# Use git source (default - fetches latest tag)
wizard-codegen generate
# Use local protos for development
wizard-codegen --local generate
Targets Configuration
Each target represents a language/output configuration.
| Key | Type | Description |
|---|---|---|
templates |
string |
Path to Jinja2 template directory |
out |
string |
Output directory for generated files |
render |
list |
List of render rules |
Render Rules
| Key | Type | Description |
|---|---|---|
template |
string |
Template filename (relative to templates dir) |
output |
string |
Output path pattern (Jinja2 template string) |
for_each |
enum |
Iteration mode: file, message, enum, service |
mode |
enum |
Write mode: overwrite, append, write-once |
where |
object |
Filter predicates (optional) |
Write Modes
| Mode | Description |
|---|---|
overwrite |
Always replace existing files (default) |
append |
Add content to end of existing files |
write-once |
Only create if file doesn't exist |
Where Clauses (Filtering)
Filter which items to generate for:
where:
all: # AND conditions (all must match)
- name: "*Form"
- package: "wizard.*"
any: # OR conditions (at least one must match)
- name: "*Context"
- name: "*Provider"
not: # Exclude matching items
- file: "*/design_system/*"
- name: "*Internal"
Predicate Fields
| Field | Description | Example |
|---|---|---|
name |
Message/enum/service name | "*Form", "User*", "Order" |
package |
Proto package name | "wizard.*", "com.example.*" |
file |
Proto file path | "*/shared/*", "user/*.proto" |
full_name |
Fully qualified name | ".wizard.UserForm" |
option.equals |
Match proto options | { key: "deprecated", value: true } |
has_option |
Check if option/annotation is present | "*.page", "annotations.wizard.v1.page" |
Patterns support:
- Glob patterns:
*,?,[abc] - Regex patterns: Any valid Python regex
Hooks Configuration
| Key | Type | Description |
|---|---|---|
root |
string |
Root directory for hooks module (default: "wizard") |
module |
string |
Python module name with register() function |
๐ Template Authoring
Templates use Jinja2 syntax with custom extensions.
Template Context
When a template is rendered, it receives a rich context:
{
"proto_root": "/path/to/protos",
# Current item (when using for_each)
"item": {
"name": Name("UserForm"), # Name object with case transformations
"full_name": ".wizard.UserForm",
"file": "user/user_form.proto",
"package": "wizard",
"fields": [...], # List of field objects
"nested_messages": [...], # Nested message definitions
"nested_enums": [...], # Nested enum definitions
},
# All files (for cross-referencing)
"files": [...],
# Indexes for quick lookup
"message": {".package.MessageName": {...}, ...},
"enum": {".package.EnumName": {...}, ...},
"service": {".package.ServiceName": {...}, ...},
"types": {".package.TypeName": {...}, ...},
}
File Object
{
"name": Name("user_form"),
"full_name": "user_form.proto",
"package": "wizard",
"package_path": "wizard",
"imports": ["common/timestamp.proto"],
"messages": [...],
"enums": [...],
"services": [...],
"options": {
# Extracted file options (standard and extensions)
# e.g., {"java_package": "com.example", "go_package": "example/pb"}
},
}
Message Object
{
"name": Name("UserForm"),
"full_name": ".wizard.UserForm",
"file": "user/user_form.proto",
"package": "wizard",
"fields": [
{
"name": Name("user_name"),
"number": 1,
"label": 1, # 1=optional, 2=required, 3=repeated
"type": 9, # Proto type number (9=string)
"type_name": "", # For message/enum refs: ".package.Type"
"json_name": "userName",
"field": < FieldDescriptor >,
"options": {}, # Extracted field options
},
...
],
"nested_messages": [...],
"nested_enums": [...],
"options": {
# Extracted message options (standard and extensions)
# Example: {"annotations.wizard.v1.page": True}
},
}
Enum Object
{
"name": Name("Status"),
"full_name": ".wizard.Status",
"file": "common/status.proto",
"package": "wizard",
"enum_values": [
{"name": "STATUS_UNKNOWN", "number": 0},
{"name": "STATUS_ACTIVE", "number": 1},
...
],
"options": {}, # Extracted enum options
}
Service Object
{
"name": Name("UserService"),
"full_name": ".wizard.UserService",
"file": "user/user_service.proto",
"package": "wizard",
"methods": [
{
"name": Name("GetUser"),
"input_type": ".wizard.GetUserRequest",
"output_type": ".wizard.User",
"client_streaming": False,
"server_streaming": False,
"options": {}, # Extracted method options
},
...
],
"options": {}, # Extracted service options
}
Name Transformations
Every name in the context is wrapped in a Name object that provides automatic case transformations:
{{ item.name.raw }} โ UserFormRequest
{{ item.name.snake_case }} โ user_form_request
{{ item.name.kebab_case }} โ user-form-request
{{ item.name.pascal_case }} โ UserFormRequest
{{ item.name.camel_case }} โ userFormRequest
{{ item.name.macro_case }} โ USERFORMREQUEST
{{ item.name.micro_case }} โ userformrequest
{{ item.name.macro_snake_case }} โ USER_FORM_REQUEST
Use in output paths:
output: "{{ item.name.kebab_case }}/index.tsx"
# Generates: user-form-request/index.tsx
Use in templates:
export interface {{ item.name.pascal_case }} {
{% for field in item.fields %}
{{ field.name.camel_case }}: {{ field | ts_type }};
{% endfor %}
}
Built-in Filters
The following filters are available in all templates:
Common Filters
| Filter | Description | Example |
|---|---|---|
replace |
String replacement | {{ name | replace("_", "-") }} |
TypeScript Filters (via hooks)
| Filter | Description | Example |
|---|---|---|
ts_type |
Proto to TS type | {{ field | ts_type }} โ string, number, User |
ts_type_optional |
TS type with undefined | {{ field | ts_type_optional }} โ string | undefined |
is_ts_optional |
Check if optional | {% if field | is_ts_optional %}?{% endif %} |
is_repeated |
Check if array | {% if field | is_repeated %}[]{% endif %} |
Swift Filters (via hooks)
| Filter | Description | Example |
|---|---|---|
swift_type |
Proto to Swift type | {{ field | swift_type }} โ String, Int32, User |
swift_default |
Swift default value | {{ field | swift_default }} โ "", 0, [] |
Kotlin Filters (via hooks)
| Filter | Description | Example |
|---|---|---|
kotlin_type |
Proto to Kotlin type | {{ field | kotlin_type }} โ String, Int, User |
kotlin_default |
Kotlin default value | {{ field | kotlin_default }} โ "", 0, emptyList() |
Go Filters (via hooks)
| Filter | Description | Example |
|---|---|---|
go_type |
Proto to Go type | {{ field | go_type }} โ string, int32, *User |
go_zero |
Go zero value | {{ field | go_zero }} โ "", 0, nil |
go_json_tag |
Go JSON struct tag | {{ field | go_json_tag }} โ `json:"userName,omitempty"` |
Template Example
// {{ item.name.pascal_case }}.swift
// Generated from: {{ item.file }}
// Package: {{ item.package }}
import Foundation
/// {{ item.name.pascal_case }} - Auto-generated from protobuf.
public struct {{ item.name.pascal_case }}: Codable, Equatable, Sendable {
{% for field in item.fields %}
/// {{ field.name.raw }} - Proto type: {{ field.type }}
public var {{ field.name.camel_case }}: {{ field | swift_type }}
{% endfor %}
public init(
{% for field in item.fields %}
{{ field.name.camel_case }}: {{ field | swift_type }} = {{ field | swift_default }}{% if not loop.last %},{% endif %}
{% endfor %}
) {
{% for field in item.fields %}
self.{{ field.name.camel_case }} = {{ field.name.camel_case }}
{% endfor %}
}
}
๐ Custom Hooks
Extend wizard-codegen with custom Jinja2 filters and helpers.
Creating a Hooks Module
- Create a Python file in your hooks root (default:
wizard/):
# wizard/my_hooks.py
from jinja2 import Environment
from core import CodegenConfig
def custom_filter(value):
"""Your custom filter logic."""
return str(value).upper()
def format_field_doc(field):
"""Generate documentation for a field."""
return f"@param {field['name'].camel_case} - {field.get('json_name', '')}"
def register(env: Environment, *, target: str, config: CodegenConfig) -> None:
"""
Register custom filters and globals.
Args:
env: Jinja2 environment to extend
target: Current target name (e.g., "typescript", "swift")
config: Full codegen configuration
"""
# Register filters
env.filters["uppercase"] = custom_filter
env.filters["field_doc"] = format_field_doc
# Register globals (optional)
env.globals["TARGET"] = target
# Target-specific filters
if target == "typescript":
env.filters["ts_custom"] = lambda x: f"TS_{x}"
- Reference it in your configuration:
hooks:
root: "wizard"
module: "my_hooks" # Loads wizard/my_hooks.py
- Use in templates:
{{ item.name.raw | uppercase }}
{{ field | field_doc }}
Current target: {{ TARGET }}
Type Mapping Example
Here's a complete example of type mapping hooks:
# wizard/type_mappings.py
from jinja2 import Environment
# Proto type constants
TYPE_STRING = 9
TYPE_BOOL = 8
TYPE_INT32 = 5
TYPE_INT64 = 3
TS_TYPES = {
TYPE_STRING: "string",
TYPE_BOOL: "boolean",
TYPE_INT32: "number",
TYPE_INT64: "bigint",
}
SWIFT_TYPES = {
TYPE_STRING: "String",
TYPE_BOOL: "Bool",
TYPE_INT32: "Int32",
TYPE_INT64: "Int64",
}
def ts_type(field):
if field.get("type_name"):
return field["type_name"].split(".")[-1]
return TS_TYPES.get(field.get("type"), "unknown")
def swift_type(field):
if field.get("type_name"):
return field["type_name"].split(".")[-1]
return SWIFT_TYPES.get(field.get("type"), "Any")
def register(env: Environment, *, target: str, config) -> None:
if target == "typescript":
env.filters["lang_type"] = ts_type
elif target == "swift":
env.filters["lang_type"] = swift_type
๐๏ธ Architecture
wizard-codegen/
โโโ cli/ # CLI package
โ โโโ __init__.py # Re-exports for backwards compatibility
โ โโโ main.py # CLI entry point (Typer app)
โโโ core/ # Core business logic
โ โโโ config.py # Configuration models (Pydantic)
โ โโโ context_builder.py # Proto โ Jinja context transformation
โ โโโ filter.py # Where clause filtering
โ โโโ renderer.py # Jinja2 template rendering
โ โโโ writer.py # File writing with modes
โโโ proto/ # Protocol Buffer handling
โ โโโ discover.py # Proto file discovery
โ โโโ fds_loader.py # Descriptor set loading
โ โโโ proto_source.py # Git checkout handling
โ โโโ protoc_runner.py # protoc execution
โโโ hooks/ # Plugin system
โ โโโ hooks.py # Hook loading and protocol
โโโ utils/ # Utilities
โ โโโ name.py # Name transformations
โโโ wizard/ # Example configuration
โโโ codegen.yaml # Sample config
โโโ hook_sample.py # Sample hooks
โโโ templates/ # Sample templates
Pipeline Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CLI (cli/main.py) โ
โ parse args, load config โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Proto Resolution (proto/) โ
โ discover files โ resolve git source โ build descriptor set โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Context Building (core/context_builder.py) โ
โ FileDescriptorSet โ Jinja-friendly dictionaries โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Rendering (core/renderer.py) โ
โ for each target โ for each rule โ filter items โ render template โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Writing (core/writer.py) โ
โ apply write mode โ hash comparison โ write files โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ฉโ๐ป Developer Guide
Project Structure
wizard-codegen/
โโโ cli/ # CLI package (main entry point)
โโโ core/ # Core modules
โโโ proto/ # Proto handling
โโโ hooks/ # Plugin system
โโโ utils/ # Utilities
โโโ wizard/ # Example config and templates
โโโ tests/ # Test suite
โ โโโ fixtures/ # Test fixtures
โ โ โโโ protos/ # Sample proto files
โ โ โโโ templates/ # Test templates
โ โ โโโ hooks/ # Test hooks
โ โ โโโ expected_outputs/ # Golden files
โ โโโ test_*.py # Unit tests
โ โโโ test_e2e.py # End-to-end tests
โโโ Makefile # Build automation
โโโ pyproject.toml # Project config & dependencies
โโโ uv.lock # Locked dependencies
Running Tests
# Run all tests
make test
# Run with coverage
make coverage
# Run specific test file
make test PYTEST_ARGS=tests/test_context_builder.py
# Run specific test
make test PYTEST_ARGS="tests/test_e2e.py::TestFullPipelineGeneration -v"
Code Coverage
Test Categories
| Category | Description | Files |
|---|---|---|
| Unit Tests | Test individual functions | test_config.py, test_filter.py, test_name.py |
| Integration Tests | Test module interactions | test_context_builder.py, test_renderer.py |
| E2E Tests | Full pipeline tests | test_e2e.py |
| Fixture Tests | Golden file comparisons | test_fixtures.py |
Adding New Target Languages
- Create templates in
wizard/templates/<language>/:
{# wizard/templates/rust/struct.j2 #}
// {{ item.name.pascal_case }}.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct {{ item.name.pascal_case }} {
{% for field in item.fields %}
pub {{ field.name.snake_case }}: {{ field | rust_type }},
{% endfor %}
}
- Add type mapping hooks (optional):
# wizard/rust_hooks.py
RUST_TYPES = {
9: "String",
8: "bool",
5: "i32",
3: "i64",
}
def rust_type(field):
if field.get("type_name"):
return field["type_name"].split(".")[-1]
return RUST_TYPES.get(field.get("type"), "Unknown")
def register(env, *, target, config):
if target == "rust":
env.filters["rust_type"] = rust_type
- Add target configuration:
targets:
rust:
templates: "wizard/templates/rust"
out: "src/generated"
render:
- template: "struct.j2"
for_each: "message"
output: "{{ item.name.snake_case }}.rs"
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Write tests for new functionality
- Run the test suite:
make test - Submit a pull request
Code Style
- Python 3.10+ type hints
- Black formatting (88 char line length)
- Docstrings for public functions
- Comprehensive tests for new features
๐ Examples
TypeScript React Form Component
{# templates/ts/form.j2 #}
import React, { useState } from 'react';
export interface {{ item.name.pascal_case }}Data {
{% for field in item.fields %}
{{ field.name.camel_case }}{% if field | is_ts_optional %}?{% endif %}: {{ field | ts_type }};
{% endfor %}
}
export const {{ item.name.pascal_case }}: React.FC = () => {
{% for field in item.fields %}
const [{{ field.name.camel_case }}, set{{ field.name.pascal_case }}] = useState<{{ field | ts_type }}>();
{% endfor %}
return (
<form>
{% for field in item.fields %}
<input
name="{{ field.name.snake_case }}"
value={ {{ field.name.camel_case }} ?? ''}
onChange={(e) => set{{ field.name.pascal_case }}(e.target.value)}
/>
{% endfor %}
</form>
);
};
Kotlin Data Class
{# templates/kotlin/data_class.j2 #}
package {{ item.package }}
import kotlinx.serialization.Serializable
@Serializable
data class {{ item.name.pascal_case }}(
{% for field in item.fields %}
val {{ field.name.camel_case }}: {{ field | kotlin_type }} = {{ field | kotlin_default }}{% if not loop.last %},{% endif %}
{% endfor %}
)
Go Struct with JSON Tags
{# templates/go/struct.j2 #}
package {{ item.package | replace(".", "_") }}
// {{ item.name.pascal_case }} - Generated from {{ item.file }}
type {{ item.name.pascal_case }} struct {
{% for field in item.fields %}
{{ field.name.pascal_case }} {{ field | go_type }} {{ field | go_json_tag }}
{% endfor %}
}
๐ง Troubleshooting
Common Issues
"protoc is not available in PATH"
# macOS
brew install protobuf
# Ubuntu/Debian
apt install -y protobuf-compiler
# Verify installation
protoc --version
"Config error: proto.root not found"
Either specify a local proto.root or configure proto.source.git:
proto:
root: "../my-protos" # Local path
# OR
source:
git: "git@github.com:Org/protos.git"
ref: "main"
"Failed to import hooks module"
Ensure your hooks module:
- Is in the correct directory (
hooks.rootconfig) - Has a
register(env, *, target, config)function - Has no import errors
# Test manually
uv run python -c "import wizard.my_hooks"
"Template variable undefined"
- Use
--verboseto inspect the context - Check your
for_eachsetting matches the expected context - Verify field names in the proto file
Made with ๐ง magic
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wizard_codegen-0.2.2.tar.gz.
File metadata
- Download URL: wizard_codegen-0.2.2.tar.gz
- Upload date:
- Size: 39.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.22
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0acefbf473945bd2b18746185d058a7fb51ddecfa8085e88a018b2f207039099
|
|
| MD5 |
51dfec429db45ede763254451d307c3b
|
|
| BLAKE2b-256 |
ebe5ba59122fc478ef444026037531162485014f17bde976fa5e10e770d9e76b
|
File details
Details for the file wizard_codegen-0.2.2-py3-none-any.whl.
File metadata
- Download URL: wizard_codegen-0.2.2-py3-none-any.whl
- Upload date:
- Size: 34.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.22
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
28120136c1b2ee36ad58bd49da42597de079ba670f5059240b653c18a7dbb632
|
|
| MD5 |
1a3974101793e3bfd87bf252fb2666a7
|
|
| BLAKE2b-256 |
b1a049c699786a9f419e697c3b8b6e277752cf3e6231b05c032178c8e2c55aa1
|