use sigil_stitch::prelude::*;
fn render_ts(block: &CodeBlock) -> String {
FileSpec::builder("demo.ts")
.add_code(block.clone())
.build()
.unwrap()
.render(80)
.unwrap()
}
fn main() {
meta_if_else();
meta_for_loop();
meta_let_binding();
splice_each();
nested_meta();
schema_driven_codegen();
}
fn meta_if_else() {
println!("--- $if / $else_if / $else ---\n");
let auth_mode = "bearer";
let block = sigil_quote!(TypeScript {
$if(auth_mode == "bearer") {
const token = getAuthToken();
headers.set($S("Authorization"), $S("Bearer ") + token);
} $else_if(auth_mode == "basic") {
const creds = btoa(username + $S(":") + password);
headers.set($S("Authorization"), $S("Basic ") + creds);
} $else_if(auth_mode == "api_key") {
headers.set($S("X-API-Key"), apiKey);
} $else {
headers.set($S("X-Anonymous"), $S("true"));
}
})
.unwrap();
println!("With auth_mode = \"bearer\":");
println!("{}", render_ts(&block));
let auth_mode = "api_key";
let block2 = sigil_quote!(TypeScript {
$if(auth_mode == "bearer") {
const token = getAuthToken();
headers.set($S("Authorization"), $S("Bearer ") + token);
} $else_if(auth_mode == "basic") {
const creds = btoa(username + $S(":") + password);
headers.set($S("Authorization"), $S("Basic ") + creds);
} $else_if(auth_mode == "api_key") {
headers.set($S("X-API-Key"), apiKey);
} $else {
headers.set($S("X-Anonymous"), $S("true"));
}
})
.unwrap();
println!("With auth_mode = \"api_key\":");
println!("{}", render_ts(&block2));
}
fn meta_for_loop() {
println!("--- $for ---\n");
let fields = vec!["name", "email", "age"];
let block = sigil_quote!(TypeScript {
$for(field in &fields) {
this.$L(*field) = data.$L(*field);
}
})
.unwrap();
println!("Generated from fields = {:?}:", fields);
println!("{}", render_ts(&block));
}
fn meta_let_binding() {
println!("--- $let ---\n");
let fields = vec![("user_name", "String"), ("is_active", "boolean")];
let block = sigil_quote!(TypeScript {
$for((name, ty) in &fields) {
$let(pascal = name
.split('_')
.map(|w| {
let mut c = w.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().to_string() + c.as_str(),
}
})
.collect::<String>());
$let(getter = format!("get{}", pascal));
$N(getter)(): $L(*ty);
}
})
.unwrap();
println!("Generated getters:");
println!("{}", render_ts(&block));
}
fn splice_each() {
println!("--- $C_each ---\n");
let field_names = ["host", "port", "debug"];
let assignments: Vec<CodeBlock> = field_names
.iter()
.map(|f| {
sigil_quote!(TypeScript {
this.$L(*f) = config.$L(*f);
})
.unwrap()
})
.collect();
let block = sigil_quote!(TypeScript {
$C_each(assignments);
})
.unwrap();
println!("Spliced {} assignments:", field_names.len());
println!("{}", render_ts(&block));
}
fn nested_meta() {
println!("--- Nested Meta-Statements ---\n");
let fields: Vec<(&str, &str, bool)> = vec![
("id", "number", true),
("name", "string", false),
("email", "string", true),
];
let block = sigil_quote!(TypeScript {
$for((name, ty, required) in &fields) {
$if(*required) {
$L(*name): $L(*ty);
} $else {
$L(format!("{}?: {}", name, ty));
}
}
})
.unwrap();
println!("Conditional field generation:");
println!("{}", render_ts(&block));
}
fn schema_driven_codegen() {
println!("--- Schema-Driven Codegen ---\n");
struct Field {
name: &'static str,
ty: &'static str,
optional: bool,
}
let schema = vec![
Field {
name: "id",
ty: "number",
optional: false,
},
Field {
name: "name",
ty: "string",
optional: false,
},
Field {
name: "email",
ty: "string",
optional: true,
},
Field {
name: "avatar",
ty: "string",
optional: true,
},
];
let body = sigil_quote!(TypeScript {
$for(field in &schema) {
$let(line = if field.optional {
format!("{}?: {};", field.name, field.ty)
} else {
format!("{}: {};", field.name, field.ty)
});
$L(line)
}
})
.unwrap();
let iface = TypeSpec::builder("User", TypeKind::Interface)
.visibility(Visibility::Public)
.doc("Auto-generated from schema.")
.extra_member(body)
.build()
.unwrap();
let mut getters_body = CodeBlock::builder();
for field in &schema {
if !field.optional {
let body = sigil_quote!(TypeScript {
return this.$L(field.name);
})
.unwrap();
let getter = FunSpec::builder(&format!("get{}", capitalize(field.name)))
.returns(TypeName::primitive(field.ty))
.body(body)
.build()
.unwrap();
getters_body.add_code(
getter
.emit(
&sigil_stitch::lang::typescript::TypeScript::new(),
DeclarationContext::Member,
)
.unwrap(),
);
getters_body.add_line();
}
}
let cls = TypeSpec::builder("UserImpl", TypeKind::Class)
.visibility(Visibility::Public)
.implements(TypeName::primitive("User"))
.extra_member(getters_body.build().unwrap())
.build()
.unwrap();
let file = FileSpec::builder("user.ts")
.add_type(iface)
.add_type(cls)
.build()
.unwrap();
println!("{}", file.render(80).unwrap());
}
fn capitalize(s: &str) -> String {
let mut chars = s.chars();
match chars.next() {
None => String::new(),
Some(f) => f.to_uppercase().to_string() + chars.as_str(),
}
}