Writing a Plugin
This guide walks you through creating a custom typewriter plugin.
1. Create a Crate
cargo new --lib typewriter-plugin-mylang
2. Cargo.toml
[package]
name = "typewriter-plugin-mylang"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
typewriter-plugin = "0.1.0"
typewriter-core = "0.5.2"
The cdylib crate type is required for dynamic loading.
3. Implement TypeMapper
#![allow(unused)]
fn main() {
use typewriter_plugin::prelude::*;
struct MyLangMapper;
impl TypeMapper for MyLangMapper {
fn map_primitive(&self, ty: &PrimitiveType) -> String {
match ty {
PrimitiveType::String => "string".into(),
PrimitiveType::Bool => "boolean".into(),
PrimitiveType::U32 => "uint32".into(),
// ... implement all primitives
_ => "any".into(),
}
}
fn map_option(&self, inner: &TypeKind) -> String {
format!("{}?", self.map_type(inner))
}
fn map_vec(&self, inner: &TypeKind) -> String {
format!("List<{}>", self.map_type(inner))
}
fn map_hashmap(&self, key: &TypeKind, value: &TypeKind) -> String {
format!("Map<{}, {}>", self.map_type(key), self.map_type(value))
}
fn map_tuple(&self, elements: &[TypeKind]) -> String {
let inner: Vec<String> = elements.iter().map(|e| self.map_type(e)).collect();
format!("({})", inner.join(", "))
}
fn map_named(&self, name: &str) -> String { name.into() }
fn emit_struct(&self, def: &StructDef) -> String {
// Your struct rendering logic
todo!()
}
fn emit_enum(&self, def: &EnumDef) -> String {
// Your enum rendering logic
todo!()
}
fn file_header(&self, type_name: &str) -> String {
format!("// Generated by typewriter. Source: {}\n\n", type_name)
}
fn file_extension(&self) -> &str { "mylang" }
fn file_naming(&self, type_name: &str) -> String {
to_file_style(type_name, FileStyle::SnakeCase)
}
}
}
4. Implement EmitterPlugin
#![allow(unused)]
fn main() {
struct MyLangPlugin;
impl MyLangPlugin {
fn new() -> Self { Self }
}
impl EmitterPlugin for MyLangPlugin {
fn language_id(&self) -> &str { "mylang" }
fn language_name(&self) -> &str { "My Language" }
fn version(&self) -> &str { "0.1.0" }
fn default_output_dir(&self) -> &str { "./generated/mylang" }
fn file_extension(&self) -> &str { "mylang" }
fn mapper(&self, _config: &PluginConfig) -> Box<dyn TypeMapper> {
Box::new(MyLangMapper)
}
}
declare_plugin!(MyLangPlugin);
}
5. Build and Install
cargo build --release
cp target/release/libtypewriter_plugin_mylang.so ~/.typewriter/plugins/
6. Validate
typebridge plugin validate ./target/release/libtypewriter_plugin_mylang.so
7. Use
#![allow(unused)]
fn main() {
#[derive(TypeWriter)]
#[sync_to(mylang)]
pub struct User { ... }
}
typebridge generate --all