use sigil_stitch::lang::dart::DartLang;
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_shared_types() -> (TypeSpec, TypeSpec, TypeSpec) {
let status = TypeSpec::builder("TaskStatus", TypeKind::Enum)
.add_variant(EnumVariantSpec::new("Pending").unwrap())
.add_variant(EnumVariantSpec::new("InProgress").unwrap())
.add_variant(EnumVariantSpec::new("Done").unwrap())
.build()
.unwrap();
let base_entity = TypeSpec::builder("BaseEntity", TypeKind::Class)
.is_abstract()
.doc("Base class for all domain entities.")
.add_field(
FieldSpec::builder("id", TypeName::primitive("String"))
.is_readonly()
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("BaseEntity")
.add_param(ParameterSpec::new("id", TypeName::primitive("String")).unwrap())
.body(CodeBlock::of("this.id = id", ()).unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("validate")
.is_abstract()
.returns(TypeName::primitive("bool"))
.build()
.unwrap(),
)
.build()
.unwrap();
let repo = TypeSpec::builder("TaskRepository", TypeKind::Interface)
.add_type_param(TypeParamSpec::new("T"))
.add_method(
FunSpec::builder("findById")
.returns(TypeName::primitive("T?"))
.add_param(ParameterSpec::new("id", TypeName::primitive("String")).unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("save")
.add_param(ParameterSpec::new("entity", TypeName::primitive("T")).unwrap())
.build()
.unwrap(),
)
.build()
.unwrap();
(status, base_entity, repo)
}
fn builder_approach() -> String {
let convert = TypeName::importable("dart:convert", "jsonDecode");
let (status, base_entity, repo) = build_shared_types();
let task = TypeSpec::builder("Task", TypeKind::Class)
.doc("A task entity.")
.extends(TypeName::primitive("BaseEntity"))
.add_field(
FieldSpec::builder("name", TypeName::primitive("String"))
.is_readonly()
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("status", TypeName::primitive("TaskStatus"))
.initializer(CodeBlock::of("TaskStatus.Pending", ()).unwrap())
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("done", TypeName::primitive("bool"))
.initializer(CodeBlock::of("false", ()).unwrap())
.build()
.unwrap(),
);
let mut ctor_body = CodeBlock::builder();
ctor_body.add_statement("super(id)", ());
ctor_body.add_statement("this.name = name", ());
let task = task
.add_method(
FunSpec::builder("Task")
.add_param(ParameterSpec::new("id", TypeName::primitive("String")).unwrap())
.add_param(ParameterSpec::new("name", TypeName::primitive("String")).unwrap())
.body(ctor_body.build().unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("validate")
.is_override()
.returns(TypeName::primitive("bool"))
.body(CodeBlock::of("return id.isNotEmpty && name.isNotEmpty", ()).unwrap())
.build()
.unwrap(),
)
.build()
.unwrap();
let parse_body = CodeBlock::of(
"final data = %T(json);\nreturn Task(data['id'], data['name']);",
(convert,),
)
.unwrap();
let parse_task = FunSpec::builder("parseTask")
.returns(TypeName::primitive("Task"))
.add_param(ParameterSpec::new("json", TypeName::primitive("String")).unwrap())
.body(parse_body)
.build()
.unwrap();
let mut fetch_body = CodeBlock::builder();
fetch_body.add(
"final json = await http.get('https://api.example.com/task')",
(),
);
fetch_body.add_line();
fetch_body.add("return parseTask(json.body)", ());
let fetch_task = FunSpec::builder("fetchTask")
.is_async()
.returns(TypeName::generic(
TypeName::primitive("Future"),
vec![TypeName::primitive("Task")],
))
.body(fetch_body.build().unwrap())
.build()
.unwrap();
let mut transform_body = CodeBlock::builder();
transform_body.add("return mapper(input)", ());
let transform = FunSpec::builder("transform")
.add_type_param(TypeParamSpec::new("T"))
.add_type_param(TypeParamSpec::new("R"))
.returns(TypeName::primitive("R"))
.add_param(ParameterSpec::new("input", TypeName::primitive("T")).unwrap())
.add_param(ParameterSpec::new("mapper", TypeName::primitive("R Function(T)")).unwrap())
.body(transform_body.build().unwrap())
.build()
.unwrap();
FileSpec::builder_with("task.dart", DartLang::new())
.add_type(status)
.add_type(base_entity)
.add_type(task)
.add_type(repo)
.add_function(parse_task)
.add_function(fetch_task)
.add_function(transform)
.build()
.unwrap()
.render(80)
.unwrap()
}
fn macro_approach() -> String {
let convert = TypeName::importable("dart:convert", "jsonDecode");
let (status, base_entity, repo) = build_shared_types();
let task = TypeSpec::builder("Task", TypeKind::Class)
.doc("A task entity.")
.extends(TypeName::primitive("BaseEntity"))
.add_field(
FieldSpec::builder("name", TypeName::primitive("String"))
.is_readonly()
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("status", TypeName::primitive("TaskStatus"))
.initializer(CodeBlock::of("TaskStatus.Pending", ()).unwrap())
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("done", TypeName::primitive("bool"))
.initializer(CodeBlock::of("false", ()).unwrap())
.build()
.unwrap(),
);
let ctor_body = sigil_quote!(DartLang {
super(id);
this.name = name;
})
.unwrap();
let validate_body = sigil_quote!(DartLang {
return id.isNotEmpty && name.isNotEmpty
})
.unwrap();
let task = task
.add_method(
FunSpec::builder("Task")
.add_param(ParameterSpec::new("id", TypeName::primitive("String")).unwrap())
.add_param(ParameterSpec::new("name", TypeName::primitive("String")).unwrap())
.body(ctor_body)
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("validate")
.is_override()
.returns(TypeName::primitive("bool"))
.body(validate_body)
.build()
.unwrap(),
)
.build()
.unwrap();
let parse_body = sigil_quote!(DartLang {
final data = $T(convert)(json);
return Task(data[$S("id")], data[$S("name")]);
})
.unwrap();
let parse_task = FunSpec::builder("parseTask")
.returns(TypeName::primitive("Task"))
.add_param(ParameterSpec::new("json", TypeName::primitive("String")).unwrap())
.body(parse_body)
.build()
.unwrap();
let fetch_body = sigil_quote!(DartLang {
final json = await http.get($S("https://api.example.com/task"))
return parseTask(json.body)
})
.unwrap();
let fetch_task = FunSpec::builder("fetchTask")
.is_async()
.returns(TypeName::generic(
TypeName::primitive("Future"),
vec![TypeName::primitive("Task")],
))
.body(fetch_body)
.build()
.unwrap();
let transform_body = sigil_quote!(DartLang {
return mapper(input)
})
.unwrap();
let transform = FunSpec::builder("transform")
.add_type_param(TypeParamSpec::new("T"))
.add_type_param(TypeParamSpec::new("R"))
.returns(TypeName::primitive("R"))
.add_param(ParameterSpec::new("input", TypeName::primitive("T")).unwrap())
.add_param(ParameterSpec::new("mapper", TypeName::primitive("R Function(T)")).unwrap())
.body(transform_body)
.build()
.unwrap();
FileSpec::builder_with("task.dart", DartLang::new())
.add_type(status)
.add_type(base_entity)
.add_type(task)
.add_type(repo)
.add_function(parse_task)
.add_function(fetch_task)
.add_function(transform)
.build()
.unwrap()
.render(80)
.unwrap()
}