use super::*;
use crate::v2::Result;
pub struct WitCodegen;
impl WitCodegen {
pub fn generate_rust(interface: &WitInterface) -> Result<String> {
let mut output = String::new();
output.push_str("// Auto-generated by Run 2.0 - DO NOT EDIT\n");
output.push_str("// WIT Interface: ");
output.push_str(&interface.name);
output.push_str("\n\n");
output.push_str("#![allow(unused)]\n\n");
for (name, ty) in &interface.types {
output.push_str(&Self::generate_rust_type_def(name, ty));
output.push_str("\n\n");
}
output.push_str("pub trait ");
output.push_str(&to_pascal_case(&interface.name));
output.push_str(" {\n");
for func in interface.functions.values() {
output.push_str(" ");
output.push_str(&Self::generate_rust_function_sig(func));
output.push_str(";\n");
}
output.push_str("}\n");
Ok(output)
}
fn generate_rust_type_def(name: &str, ty: &WitType) -> String {
match ty {
WitType::Record { fields } => {
let mut output = format!(
"#[derive(Debug, Clone)]\npub struct {} {{\n",
to_pascal_case(name)
);
for field in fields {
output.push_str(&format!(
" pub {}: {},\n",
to_snake_case(&field.name),
types::wit_type_to_rust(&field.ty)
));
}
output.push_str("}");
output
}
WitType::Variant { cases } => {
let mut output = format!(
"#[derive(Debug, Clone)]\npub enum {} {{\n",
to_pascal_case(name)
);
for case in cases {
match &case.ty {
Some(ty) => {
output.push_str(&format!(
" {}({}),\n",
to_pascal_case(&case.name),
types::wit_type_to_rust(ty)
));
}
None => {
output.push_str(&format!(" {},\n", to_pascal_case(&case.name)));
}
}
}
output.push_str("}");
output
}
WitType::Enum { cases } => {
let mut output = format!(
"#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum {} {{\n",
to_pascal_case(name)
);
for case in cases {
output.push_str(&format!(" {},\n", to_pascal_case(case)));
}
output.push_str("}");
output
}
WitType::Flags { flags } => {
let mut output = String::new();
output.push_str("bitflags::bitflags! {\n");
output.push_str(&format!(
" pub struct {}: u32 {{\n",
to_pascal_case(name)
));
for (i, flag) in flags.iter().enumerate() {
output.push_str(&format!(
" const {} = 1 << {};\n",
to_screaming_snake_case(flag),
i
));
}
output.push_str(" }\n");
output.push_str("}");
output
}
WitType::Resource { name: res_name } => {
format!(
"#[derive(Debug)]\npub struct {} {{\n handle: u32,\n}}",
to_pascal_case(res_name)
)
}
_ => format!(
"pub type {} = {};",
to_pascal_case(name),
types::wit_type_to_rust(ty)
),
}
}
fn generate_rust_function_sig(func: &WitFunction) -> String {
let mut sig = format!("fn {}", to_snake_case(&func.name));
sig.push_str("(&self");
for param in &func.params {
sig.push_str(&format!(
", {}: {}",
to_snake_case(¶m.name),
types::wit_type_to_rust(¶m.ty)
));
}
sig.push(')');
match &func.results {
WitResults::None => {}
WitResults::Anon(ty) => {
sig.push_str(" -> ");
sig.push_str(&types::wit_type_to_rust(ty));
}
WitResults::Named(params) => {
if params.len() == 1 {
sig.push_str(" -> ");
sig.push_str(&types::wit_type_to_rust(¶ms[0].ty));
} else {
sig.push_str(" -> (");
let types: Vec<_> = params
.iter()
.map(|p| types::wit_type_to_rust(&p.ty))
.collect();
sig.push_str(&types.join(", "));
sig.push(')');
}
}
}
sig
}
pub fn generate_python(interface: &WitInterface) -> Result<String> {
let mut output = String::new();
output.push_str("# Auto-generated by Run 2.0 - DO NOT EDIT\n");
output.push_str(&format!("# WIT Interface: {}\n\n", interface.name));
output.push_str("from typing import Protocol, Optional, List, Tuple\n");
output.push_str("from dataclasses import dataclass\n");
output.push_str("from enum import Enum, Flag, auto\n\n");
for (name, ty) in &interface.types {
output.push_str(&Self::generate_python_type_def(name, ty));
output.push_str("\n\n");
}
output.push_str(&format!(
"class {}(Protocol):\n",
to_pascal_case(&interface.name)
));
if interface.functions.is_empty() {
output.push_str(" pass\n");
} else {
for func in interface.functions.values() {
output.push_str(" ");
output.push_str(&Self::generate_python_function_sig(func));
output.push_str("\n ...\n\n");
}
}
Ok(output)
}
fn generate_python_type_def(name: &str, ty: &WitType) -> String {
match ty {
WitType::Record { fields } => {
let mut output = format!("@dataclass\nclass {}:\n", to_pascal_case(name));
if fields.is_empty() {
output.push_str(" pass");
} else {
for field in fields {
output.push_str(&format!(
" {}: {}\n",
to_snake_case(&field.name),
Self::wit_type_to_python(&field.ty)
));
}
}
output
}
WitType::Enum { cases } => {
let mut output = format!("class {}(Enum):\n", to_pascal_case(name));
for case in cases {
output.push_str(&format!(" {} = auto()\n", to_screaming_snake_case(case)));
}
output
}
WitType::Flags { flags } => {
let mut output = format!("class {}(Flag):\n", to_pascal_case(name));
for flag in flags {
output.push_str(&format!(" {} = auto()\n", to_screaming_snake_case(flag)));
}
output
}
_ => format!(
"{} = {}",
to_pascal_case(name),
Self::wit_type_to_python(ty)
),
}
}
fn generate_python_function_sig(func: &WitFunction) -> String {
let mut sig = format!("def {}(self", to_snake_case(&func.name));
for param in &func.params {
sig.push_str(&format!(
", {}: {}",
to_snake_case(¶m.name),
Self::wit_type_to_python(¶m.ty)
));
}
sig.push(')');
match &func.results {
WitResults::None => sig.push_str(" -> None:"),
WitResults::Anon(ty) => {
sig.push_str(&format!(" -> {}:", Self::wit_type_to_python(ty)));
}
WitResults::Named(params) => {
if params.len() == 1 {
sig.push_str(&format!(" -> {}:", Self::wit_type_to_python(¶ms[0].ty)));
} else {
let types: Vec<_> = params
.iter()
.map(|p| Self::wit_type_to_python(&p.ty))
.collect();
sig.push_str(&format!(" -> Tuple[{}]:", types.join(", ")));
}
}
}
sig
}
fn wit_type_to_python(ty: &WitType) -> String {
match ty {
WitType::Bool => "bool".to_string(),
WitType::U8
| WitType::U16
| WitType::U32
| WitType::U64
| WitType::S8
| WitType::S16
| WitType::S32
| WitType::S64 => "int".to_string(),
WitType::F32 | WitType::F64 => "float".to_string(),
WitType::Char | WitType::String => "str".to_string(),
WitType::List(inner) => format!("List[{}]", Self::wit_type_to_python(inner)),
WitType::Option(inner) => format!("Optional[{}]", Self::wit_type_to_python(inner)),
WitType::Result { ok, err: _ } => ok
.as_ref()
.map(|t| Self::wit_type_to_python(t))
.unwrap_or_else(|| "None".to_string()),
WitType::Tuple(types) => {
let inner: Vec<_> = types.iter().map(Self::wit_type_to_python).collect();
format!("Tuple[{}]", inner.join(", "))
}
WitType::Named(name) => to_pascal_case(name),
_ => "Any".to_string(),
}
}
pub fn generate_typescript(interface: &WitInterface) -> Result<String> {
let mut output = String::new();
output.push_str("// Auto-generated by Run 2.0 - DO NOT EDIT\n");
output.push_str(&format!("// WIT Interface: {}\n\n", interface.name));
for (name, ty) in &interface.types {
output.push_str(&Self::generate_typescript_type_def(name, ty));
output.push_str("\n\n");
}
output.push_str(&format!(
"export interface {} {{\n",
to_pascal_case(&interface.name)
));
for func in interface.functions.values() {
output.push_str(" ");
output.push_str(&Self::generate_typescript_function_sig(func));
output.push_str(";\n");
}
output.push_str("}\n");
Ok(output)
}
fn generate_typescript_type_def(name: &str, ty: &WitType) -> String {
match ty {
WitType::Record { fields } => {
let mut output = format!("export interface {} {{\n", to_pascal_case(name));
for field in fields {
output.push_str(&format!(
" {}: {};\n",
to_camel_case(&field.name),
Self::wit_type_to_typescript(&field.ty)
));
}
output.push_str("}");
output
}
WitType::Enum { cases } => {
let mut output = format!("export enum {} {{\n", to_pascal_case(name));
for case in cases {
output.push_str(&format!(" {},\n", to_pascal_case(case)));
}
output.push_str("}");
output
}
WitType::Variant { cases } => {
let variants: Vec<_> = cases
.iter()
.map(|c| match &c.ty {
Some(ty) => format!(
"{{ tag: '{}'; value: {} }}",
to_camel_case(&c.name),
Self::wit_type_to_typescript(ty)
),
None => format!("{{ tag: '{}' }}", to_camel_case(&c.name)),
})
.collect();
format!(
"export type {} = {};",
to_pascal_case(name),
variants.join(" | ")
)
}
_ => format!(
"export type {} = {};",
to_pascal_case(name),
Self::wit_type_to_typescript(ty)
),
}
}
fn generate_typescript_function_sig(func: &WitFunction) -> String {
let mut sig = to_camel_case(&func.name);
sig.push('(');
let params: Vec<_> = func
.params
.iter()
.map(|p| {
format!(
"{}: {}",
to_camel_case(&p.name),
Self::wit_type_to_typescript(&p.ty)
)
})
.collect();
sig.push_str(¶ms.join(", "));
sig.push_str("): ");
match &func.results {
WitResults::None => sig.push_str("void"),
WitResults::Anon(ty) => sig.push_str(&Self::wit_type_to_typescript(ty)),
WitResults::Named(params) => {
if params.len() == 1 {
sig.push_str(&Self::wit_type_to_typescript(¶ms[0].ty));
} else {
let types: Vec<_> = params
.iter()
.map(|p| Self::wit_type_to_typescript(&p.ty))
.collect();
sig.push_str(&format!("[{}]", types.join(", ")));
}
}
}
sig
}
fn wit_type_to_typescript(ty: &WitType) -> String {
match ty {
WitType::Bool => "boolean".to_string(),
WitType::U8
| WitType::U16
| WitType::U32
| WitType::S8
| WitType::S16
| WitType::S32
| WitType::F32
| WitType::F64 => "number".to_string(),
WitType::U64 | WitType::S64 => "bigint".to_string(),
WitType::Char | WitType::String => "string".to_string(),
WitType::List(inner) => format!("{}[]", Self::wit_type_to_typescript(inner)),
WitType::Option(inner) => format!("{} | null", Self::wit_type_to_typescript(inner)),
WitType::Result { ok, err } => {
let ok_type = ok
.as_ref()
.map(|t| Self::wit_type_to_typescript(t))
.unwrap_or_else(|| "void".to_string());
let err_type = err
.as_ref()
.map(|t| Self::wit_type_to_typescript(t))
.unwrap_or_else(|| "Error".to_string());
format!("Result<{}, {}>", ok_type, err_type)
}
WitType::Tuple(types) => {
let inner: Vec<_> = types.iter().map(Self::wit_type_to_typescript).collect();
format!("[{}]", inner.join(", "))
}
WitType::Named(name) => to_pascal_case(name),
_ => "unknown".to_string(),
}
}
pub fn generate_go(interface: &WitInterface) -> Result<String> {
let mut output = String::new();
output.push_str("// Auto-generated by Run 2.0 - DO NOT EDIT\n");
output.push_str(&format!("// WIT Interface: {}\n\n", interface.name));
output.push_str("package wit\n\n");
for (name, ty) in &interface.types {
output.push_str(&Self::generate_go_type_def(name, ty));
output.push_str("\n\n");
}
output.push_str(&format!(
"type {} interface {{\n",
to_pascal_case(&interface.name)
));
for func in interface.functions.values() {
output.push_str(" ");
output.push_str(&Self::generate_go_function_sig(func));
output.push_str("\n");
}
output.push_str("}\n");
Ok(output)
}
fn generate_go_type_def(name: &str, ty: &WitType) -> String {
match ty {
WitType::Record { fields } => {
let mut output = format!("type {} struct {{\n", to_pascal_case(name));
for field in fields {
output.push_str(&format!(
" {} {}\n",
to_pascal_case(&field.name),
Self::wit_type_to_go(&field.ty)
));
}
output.push_str("}");
output
}
WitType::Enum { cases } => {
let mut output = format!("type {} int\n\nconst (\n", to_pascal_case(name));
for (i, case) in cases.iter().enumerate() {
output.push_str(&format!(
" {}{} {} = {}\n",
to_pascal_case(name),
to_pascal_case(case),
to_pascal_case(name),
i
));
}
output.push_str(")");
output
}
_ => format!("type {} {}", to_pascal_case(name), Self::wit_type_to_go(ty)),
}
}
fn generate_go_function_sig(func: &WitFunction) -> String {
let mut sig = format!("{}(", to_pascal_case(&func.name));
let params: Vec<_> = func
.params
.iter()
.map(|p| format!("{} {}", to_camel_case(&p.name), Self::wit_type_to_go(&p.ty)))
.collect();
sig.push_str(¶ms.join(", "));
sig.push(')');
match &func.results {
WitResults::None => {}
WitResults::Anon(ty) => {
sig.push_str(&format!(" {}", Self::wit_type_to_go(ty)));
}
WitResults::Named(params) => {
if params.len() == 1 {
sig.push_str(&format!(" {}", Self::wit_type_to_go(¶ms[0].ty)));
} else {
let types: Vec<_> =
params.iter().map(|p| Self::wit_type_to_go(&p.ty)).collect();
sig.push_str(&format!(" ({})", types.join(", ")));
}
}
}
sig
}
fn wit_type_to_go(ty: &WitType) -> String {
match ty {
WitType::Bool => "bool".to_string(),
WitType::U8 => "uint8".to_string(),
WitType::U16 => "uint16".to_string(),
WitType::U32 => "uint32".to_string(),
WitType::U64 => "uint64".to_string(),
WitType::S8 => "int8".to_string(),
WitType::S16 => "int16".to_string(),
WitType::S32 => "int32".to_string(),
WitType::S64 => "int64".to_string(),
WitType::F32 => "float32".to_string(),
WitType::F64 => "float64".to_string(),
WitType::Char => "rune".to_string(),
WitType::String => "string".to_string(),
WitType::List(inner) => format!("[]{}", Self::wit_type_to_go(inner)),
WitType::Option(inner) => format!("*{}", Self::wit_type_to_go(inner)),
WitType::Tuple(types) => {
let inner: Vec<_> = types.iter().map(Self::wit_type_to_go).collect();
format!("struct{{ {} }}", inner.join(", "))
}
WitType::Named(name) => to_pascal_case(name),
_ => "any".to_string(),
}
}
pub fn generate_zig(interface: &WitInterface) -> Result<String> {
let mut output = String::new();
output.push_str("// Auto-generated by Run 2.0 - DO NOT EDIT\n");
output.push_str(&format!("// WIT Interface: {}\n\n", interface.name));
output.push_str("const std = @import(\"std\");\n\n");
for (name, ty) in &interface.types {
output.push_str(&Self::generate_zig_type_def(name, ty));
output.push_str("\n\n");
}
output.push_str(&format!(
"pub const {} = struct {{\n",
to_pascal_case(&interface.name)
));
for func in interface.functions.values() {
output.push_str(" pub fn ");
output.push_str(&Self::generate_zig_function_sig(func));
output.push_str(" {}\n");
}
output.push_str("};\n");
Ok(output)
}
fn generate_zig_type_def(name: &str, ty: &WitType) -> String {
match ty {
WitType::Record { fields } => {
let mut output = format!("pub const {} = struct {{\n", to_pascal_case(name));
for field in fields {
output.push_str(&format!(
" {}: {},\n",
to_snake_case(&field.name),
Self::wit_type_to_zig(&field.ty)
));
}
output.push_str("};");
output
}
WitType::Enum { cases } => {
let mut output = format!("pub const {} = enum {{\n", to_pascal_case(name));
for case in cases {
output.push_str(&format!(" {},\n", to_snake_case(case)));
}
output.push_str("};");
output
}
_ => format!(
"pub const {} = {};",
to_pascal_case(name),
Self::wit_type_to_zig(ty)
),
}
}
fn generate_zig_function_sig(func: &WitFunction) -> String {
let mut sig = format!("{}(", to_snake_case(&func.name));
let params: Vec<_> = func
.params
.iter()
.map(|p| {
format!(
"{}: {}",
to_snake_case(&p.name),
Self::wit_type_to_zig(&p.ty)
)
})
.collect();
sig.push_str(¶ms.join(", "));
sig.push(')');
match &func.results {
WitResults::None => sig.push_str(" void"),
WitResults::Anon(ty) => sig.push_str(&format!(" {}", Self::wit_type_to_zig(ty))),
WitResults::Named(params) => {
if params.len() == 1 {
sig.push_str(&format!(" {}", Self::wit_type_to_zig(¶ms[0].ty)));
} else {
let types: Vec<_> = params
.iter()
.map(|p| Self::wit_type_to_zig(&p.ty))
.collect();
sig.push_str(&format!(" struct{{ {} }}", types.join(", ")));
}
}
}
sig
}
fn wit_type_to_zig(ty: &WitType) -> String {
match ty {
WitType::Bool => "bool".to_string(),
WitType::U8 => "u8".to_string(),
WitType::U16 => "u16".to_string(),
WitType::U32 => "u32".to_string(),
WitType::U64 => "u64".to_string(),
WitType::S8 => "i8".to_string(),
WitType::S16 => "i16".to_string(),
WitType::S32 => "i32".to_string(),
WitType::S64 => "i64".to_string(),
WitType::F32 => "f32".to_string(),
WitType::F64 => "f64".to_string(),
WitType::Char => "u21".to_string(),
WitType::String => "[]const u8".to_string(),
WitType::List(inner) => format!("[]const {}", Self::wit_type_to_zig(inner)),
WitType::Option(inner) => format!("?{}", Self::wit_type_to_zig(inner)),
WitType::Named(name) => to_pascal_case(name),
_ => "anytype".to_string(),
}
}
}
fn to_pascal_case(s: &str) -> String {
s.split(|c: char| c == '-' || c == '_')
.map(|part| {
let mut chars = part.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().chain(chars).collect(),
}
})
.collect()
}
fn to_camel_case(s: &str) -> String {
let pascal = to_pascal_case(s);
let mut chars = pascal.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_lowercase().chain(chars).collect(),
}
}
fn to_snake_case(s: &str) -> String {
s.replace('-', "_")
}
fn to_screaming_snake_case(s: &str) -> String {
s.replace('-', "_").to_uppercase()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_case_conversion() {
assert_eq!(to_pascal_case("my-interface"), "MyInterface");
assert_eq!(to_camel_case("my-interface"), "myInterface");
assert_eq!(to_snake_case("my-interface"), "my_interface");
assert_eq!(to_screaming_snake_case("my-flag"), "MY_FLAG");
}
#[test]
fn test_generate_rust_interface() {
let interface = WitInterface {
name: "calculator".to_string(),
types: std::collections::HashMap::new(),
functions: std::collections::HashMap::from([(
"add".to_string(),
WitFunction {
name: "add".to_string(),
params: vec![
WitParam {
name: "a".to_string(),
ty: WitType::S32,
},
WitParam {
name: "b".to_string(),
ty: WitType::S32,
},
],
results: WitResults::Anon(WitType::S32),
docs: None,
},
)]),
docs: None,
};
let rust_code = WitCodegen::generate_rust(&interface).unwrap();
assert!(rust_code.contains("pub trait Calculator"));
assert!(rust_code.contains("fn add(&self, a: i32, b: i32) -> i32"));
}
}