use sigil_stitch::lang::java_lang::JavaLang;
use sigil_stitch::prelude::*;
fn main() {
println!("=== Builder API ===\n");
let builder_output = builder_approach();
println!("{builder_output}");
println!("=== sigil_quote! Macro ===\n");
let macro_output = macro_approach();
println!("{macro_output}");
}
fn build_enum() -> TypeSpec {
TypeSpec::builder("Priority", TypeKind::Enum)
.visibility(Visibility::Public)
.add_variant(
EnumVariantSpec::builder("LOW")
.value(CodeBlock::of("1", ()).unwrap())
.build()
.unwrap(),
)
.add_variant(
EnumVariantSpec::builder("MEDIUM")
.value(CodeBlock::of("2", ()).unwrap())
.build()
.unwrap(),
)
.add_variant(
EnumVariantSpec::builder("HIGH")
.value(CodeBlock::of("3", ()).unwrap())
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("value", TypeName::primitive("int"))
.visibility(Visibility::Private)
.is_readonly()
.build()
.unwrap(),
)
.build()
.unwrap()
}
fn build_interface() -> TypeSpec {
let optional = TypeName::importable("java.util", "Optional");
let list = TypeName::importable("java.util", "List");
TypeSpec::builder("Repository", TypeKind::Interface)
.visibility(Visibility::Public)
.add_type_param(TypeParamSpec::new("T"))
.add_method(
FunSpec::builder("findById")
.returns(TypeName::generic(optional, vec![TypeName::primitive("T")]))
.add_param(ParameterSpec::new("id", TypeName::primitive("long")).unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("save")
.returns(TypeName::primitive("void"))
.add_param(ParameterSpec::new("entity", TypeName::primitive("T")).unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("findAll")
.returns(TypeName::generic(list, vec![TypeName::primitive("T")]))
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("addAll")
.returns(TypeName::primitive("void"))
.add_param(
ParameterSpec::new(
"items",
TypeName::generic(
TypeName::importable("java.util", "Collection"),
vec![TypeName::wildcard_extends(TypeName::primitive("T"))],
),
)
.unwrap(),
)
.build()
.unwrap(),
)
.build()
.unwrap()
}
fn builder_approach() -> String {
let priority_enum = build_enum();
let repo_iface = build_interface();
let mut get_name_body = CodeBlock::builder();
get_name_body.add_statement("return this.name", ());
let base_entity = TypeSpec::builder("BaseEntity", TypeKind::Class)
.visibility(Visibility::Public)
.is_abstract()
.add_field(
FieldSpec::builder("name", TypeName::primitive("String"))
.visibility(Visibility::Protected)
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("BaseEntity")
.visibility(Visibility::Protected)
.add_param(ParameterSpec::new("name", TypeName::primitive("String")).unwrap())
.body({
let mut b = CodeBlock::builder();
b.add_statement("this.name = name", ());
b.build().unwrap()
})
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("getName")
.visibility(Visibility::Public)
.returns(TypeName::primitive("String"))
.body(get_name_body.build().unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("validate")
.visibility(Visibility::Public)
.is_abstract()
.returns(TypeName::primitive("boolean"))
.build()
.unwrap(),
)
.build()
.unwrap();
let mut validate_body = CodeBlock::builder();
validate_body.add_statement("return this.name != null && !this.name.isEmpty()", ());
let mut to_string_body = CodeBlock::builder();
to_string_body.add_statement(
"return \"User{name=\" + this.name + \", email=\" + this.email + \"}\"",
(),
);
let user_cls = TypeSpec::builder("User", TypeKind::Class)
.visibility(Visibility::Public)
.extends(TypeName::primitive("BaseEntity"))
.add_field(
FieldSpec::builder("email", TypeName::primitive("String"))
.visibility(Visibility::Private)
.is_readonly()
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("User")
.visibility(Visibility::Public)
.add_param(ParameterSpec::new("name", TypeName::primitive("String")).unwrap())
.add_param(ParameterSpec::new("email", TypeName::primitive("String")).unwrap())
.delegation(CodeBlock::of("super(name)", ()).unwrap())
.body({
let mut b = CodeBlock::builder();
b.add_statement("this.email = email", ());
b.build().unwrap()
})
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("validate")
.visibility(Visibility::Public)
.is_override()
.returns(TypeName::primitive("boolean"))
.body(validate_body.build().unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("getEmail")
.visibility(Visibility::Public)
.returns(TypeName::primitive("String"))
.body({
let mut b = CodeBlock::builder();
b.add_statement("return this.email", ());
b.build().unwrap()
})
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("toString")
.visibility(Visibility::Public)
.is_override()
.returns(TypeName::primitive("String"))
.body(to_string_body.build().unwrap())
.build()
.unwrap(),
)
.build()
.unwrap();
let collections = TypeName::importable("java.util", "Collections");
let comparable = TypeName::primitive("Comparable");
let mut sort_body = CodeBlock::builder();
sort_body.add_statement("%T.sort(list)", (collections,));
sort_body.add_statement("return list", ());
let sort_fn = FunSpec::builder("sortList")
.visibility(Visibility::Public)
.is_static()
.add_type_param(TypeParamSpec::new("T").with_bound(comparable))
.add_param(
ParameterSpec::new(
"list",
TypeName::generic(
TypeName::importable("java.util", "List"),
vec![TypeName::primitive("T")],
),
)
.unwrap(),
)
.returns(TypeName::generic(
TypeName::importable("java.util", "List"),
vec![TypeName::primitive("T")],
))
.body(sort_body.build().unwrap())
.build()
.unwrap();
FileSpec::builder_with("User.java", JavaLang::new())
.add_type(priority_enum)
.add_type(repo_iface)
.add_type(base_entity)
.add_type(user_cls)
.add_function(sort_fn)
.build()
.unwrap()
.render(80)
.unwrap()
}
fn macro_approach() -> String {
let priority_enum = build_enum();
let repo_iface = build_interface();
let base_entity = TypeSpec::builder("BaseEntity", TypeKind::Class)
.visibility(Visibility::Public)
.is_abstract()
.add_field(
FieldSpec::builder("name", TypeName::primitive("String"))
.visibility(Visibility::Protected)
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("BaseEntity")
.visibility(Visibility::Protected)
.add_param(ParameterSpec::new("name", TypeName::primitive("String")).unwrap())
.body(sigil_quote!(JavaLang { this.name = name; }).unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("getName")
.visibility(Visibility::Public)
.returns(TypeName::primitive("String"))
.body(sigil_quote!(JavaLang { return this.name; }).unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("validate")
.visibility(Visibility::Public)
.is_abstract()
.returns(TypeName::primitive("boolean"))
.build()
.unwrap(),
)
.build()
.unwrap();
let user_cls = TypeSpec::builder("User", TypeKind::Class)
.visibility(Visibility::Public)
.extends(TypeName::primitive("BaseEntity"))
.add_field(
FieldSpec::builder("email", TypeName::primitive("String"))
.visibility(Visibility::Private)
.is_readonly()
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("User")
.visibility(Visibility::Public)
.add_param(ParameterSpec::new("name", TypeName::primitive("String")).unwrap())
.add_param(ParameterSpec::new("email", TypeName::primitive("String")).unwrap())
.delegation(CodeBlock::of("super(name)", ()).unwrap())
.body(sigil_quote!(JavaLang { this.email = email; }).unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("validate")
.visibility(Visibility::Public)
.is_override()
.returns(TypeName::primitive("boolean"))
.body(
sigil_quote!(JavaLang {
return this.name != null && !this.name.isEmpty();
})
.unwrap(),
)
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("getEmail")
.visibility(Visibility::Public)
.returns(TypeName::primitive("String"))
.body(sigil_quote!(JavaLang { return this.email; }).unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("toString")
.visibility(Visibility::Public)
.is_override()
.returns(TypeName::primitive("String"))
.body(
sigil_quote!(JavaLang {
return "User{name=" + this.name + ", email=" + this.email + "}";
})
.unwrap(),
)
.build()
.unwrap(),
)
.build()
.unwrap();
let collections = TypeName::importable("java.util", "Collections");
let sort_body = sigil_quote!(JavaLang {
$T(collections).sort(list);
return list;
})
.unwrap();
let sort_fn = FunSpec::builder("sortList")
.visibility(Visibility::Public)
.is_static()
.add_type_param(TypeParamSpec::new("T").with_bound(TypeName::primitive("Comparable")))
.add_param(
ParameterSpec::new(
"list",
TypeName::generic(
TypeName::importable("java.util", "List"),
vec![TypeName::primitive("T")],
),
)
.unwrap(),
)
.returns(TypeName::generic(
TypeName::importable("java.util", "List"),
vec![TypeName::primitive("T")],
))
.body(sort_body)
.build()
.unwrap();
FileSpec::builder_with("User.java", JavaLang::new())
.add_type(priority_enum)
.add_type(repo_iface)
.add_type(base_entity)
.add_type(user_cls)
.add_function(sort_fn)
.build()
.unwrap()
.render(80)
.unwrap()
}