use sigil_stitch::lang::swift::Swift;
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) {
let result_enum = TypeSpec::builder("NetworkResult", TypeKind::Enum)
.visibility(Visibility::Public)
.add_variant(
EnumVariantSpec::builder("success")
.associated_type(TypeName::primitive("Data"))
.build()
.unwrap(),
)
.add_variant(
EnumVariantSpec::builder("failure")
.associated_type(TypeName::primitive("Error"))
.build()
.unwrap(),
)
.add_variant(EnumVariantSpec::builder("loading").build().unwrap())
.build()
.unwrap();
let proto = TypeSpec::builder("Repository", TypeKind::Interface)
.add_type_param(TypeParamSpec::new("T"))
.add_method(
FunSpec::builder("findById")
.returns(TypeName::optional(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(),
)
.add_method(
FunSpec::builder("findAll")
.returns(TypeName::array(TypeName::primitive("T")))
.build()
.unwrap(),
)
.build()
.unwrap();
(result_enum, proto)
}
fn builder_approach() -> String {
let url = TypeName::importable("Foundation", "URL");
let url_session = TypeName::importable("Foundation", "URLSession");
let (result_enum, proto) = build_shared_types();
let task = TypeSpec::builder("Task", TypeKind::Struct)
.visibility(Visibility::Public)
.doc("A task entity.")
.add_field(
FieldSpec::builder("id", TypeName::primitive("String"))
.visibility(Visibility::Public)
.is_readonly()
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("name", TypeName::primitive("String"))
.visibility(Visibility::Public)
.is_readonly()
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("tags", TypeName::array(TypeName::primitive("String")))
.visibility(Visibility::Public)
.initializer(CodeBlock::of("[]", ()).unwrap())
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder(
"assignee",
TypeName::optional(TypeName::primitive("String")),
)
.visibility(Visibility::Public)
.build()
.unwrap(),
)
.build()
.unwrap();
let mut factory_body = CodeBlock::builder();
factory_body.add_statement(
"guard let url = %T(string: urlString) else { return nil }",
(url,),
);
factory_body.add_statement("return url", ());
let make_url = FunSpec::builder("makeURL")
.visibility(Visibility::Public)
.is_static()
.returns(TypeName::optional(TypeName::primitive("URL")))
.add_param(ParameterSpec::new("urlString", TypeName::primitive("String")).unwrap())
.body(factory_body.build().unwrap())
.build()
.unwrap();
let mut fetch_body = CodeBlock::builder();
fetch_body.add_statement(
"let (data, _) = try await %T.shared.data(from: url)",
(url_session,),
);
fetch_body.add_statement("return data", ());
let fetch_fn = FunSpec::builder("fetchData")
.visibility(Visibility::Public)
.is_async()
.add_param(ParameterSpec::new("url", TypeName::primitive("URL")).unwrap())
.returns(TypeName::primitive("Data"))
.body(fetch_body.build().unwrap())
.build()
.unwrap();
FileSpec::builder_with("Task.swift", Swift::new())
.add_type(result_enum)
.add_type(proto)
.add_type(task)
.add_function(make_url)
.add_function(fetch_fn)
.build()
.unwrap()
.render(80)
.unwrap()
}
fn macro_approach() -> String {
let url = TypeName::importable("Foundation", "URL");
let url_session = TypeName::importable("Foundation", "URLSession");
let (result_enum, proto) = build_shared_types();
let task = TypeSpec::builder("Task", TypeKind::Struct)
.visibility(Visibility::Public)
.doc("A task entity.")
.add_field(
FieldSpec::builder("id", TypeName::primitive("String"))
.visibility(Visibility::Public)
.is_readonly()
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("name", TypeName::primitive("String"))
.visibility(Visibility::Public)
.is_readonly()
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("tags", TypeName::array(TypeName::primitive("String")))
.visibility(Visibility::Public)
.initializer(CodeBlock::of("[]", ()).unwrap())
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder(
"assignee",
TypeName::optional(TypeName::primitive("String")),
)
.visibility(Visibility::Public)
.build()
.unwrap(),
)
.build()
.unwrap();
let factory_body = sigil_quote!(Swift {
guard let url = $T(url)(string: urlString) else {
return nil;
}
return url;
})
.unwrap();
let make_url = FunSpec::builder("makeURL")
.visibility(Visibility::Public)
.is_static()
.returns(TypeName::optional(TypeName::primitive("URL")))
.add_param(ParameterSpec::new("urlString", TypeName::primitive("String")).unwrap())
.body(factory_body)
.build()
.unwrap();
let fetch_body = sigil_quote!(Swift {
let(data, _) = try await $T(url_session).shared.data(from: url);
return data;
})
.unwrap();
let fetch_fn = FunSpec::builder("fetchData")
.visibility(Visibility::Public)
.is_async()
.add_param(ParameterSpec::new("url", TypeName::primitive("URL")).unwrap())
.returns(TypeName::primitive("Data"))
.body(fetch_body)
.build()
.unwrap();
FileSpec::builder_with("Task.swift", Swift::new())
.add_type(result_enum)
.add_type(proto)
.add_type(task)
.add_function(make_url)
.add_function(fetch_fn)
.build()
.unwrap()
.render(80)
.unwrap()
}