use sigil_stitch::lang::javascript::JavaScript;
use sigil_stitch::prelude::*;
fn param(name: &str) -> ParameterSpec {
ParameterSpec::new(name, TypeName::primitive("")).unwrap()
}
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 builder_approach() -> String {
let format_msg = TypeName::importable("./utils", "formatMessage");
let event_emitter = TypeName::importable("events", "EventEmitter");
let mut ctor_body = CodeBlock::builder();
ctor_body.add_statement("super()", ());
ctor_body.add_statement("this.name = name", ());
let mut log_body = CodeBlock::builder();
log_body.add_statement("const msg = %T(this.name, message)", (format_msg,));
log_body.add_statement("console.log(msg)", ());
let mut create_body = CodeBlock::builder();
create_body.add_statement("return new Logger(name)", ());
let logger = TypeSpec::builder("Logger", TypeKind::Class)
.visibility(Visibility::Public)
.extends(TypeName::primitive("EventEmitter"))
.doc("Logger with event emission.")
.add_method(
FunSpec::builder("constructor")
.add_param(param("name"))
.body(ctor_body.build().unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("log")
.add_param(param("message"))
.body(log_body.build().unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("create")
.is_static()
.add_param(param("name"))
.body(create_body.build().unwrap())
.build()
.unwrap(),
)
.build()
.unwrap();
let mut fetch_body = CodeBlock::builder();
fetch_body.add_statement("const response = await fetch(url)", ());
fetch_body.begin_control_flow("if (!response.ok)", ());
fetch_body.add_statement(
"throw new Error(%S + response.status)",
(StringLitArg("HTTP error: ".into()),),
);
fetch_body.end_control_flow();
fetch_body.add_statement("return response.json()", ());
let fetch_fn = FunSpec::builder("fetchJSON")
.visibility(Visibility::Public)
.is_async()
.add_param(param("url"))
.body(fetch_body.build().unwrap())
.build()
.unwrap();
let mut configure_body = CodeBlock::builder();
configure_body.add_statement("console.log(host, port)", ());
let configure_fn = FunSpec::builder("configure")
.visibility(Visibility::Public)
.add_param(param("host"))
.add_param(
ParameterSpec::builder("port", TypeName::primitive(""))
.default_value(CodeBlock::of("8080", ()).unwrap())
.build()
.unwrap(),
)
.body(configure_body.build().unwrap())
.build()
.unwrap();
let mut parse_body = CodeBlock::builder();
parse_body.begin_control_flow("try", ());
parse_body.add_statement("return JSON.parse(input)", ());
parse_body.next_control_flow("catch (e)", ());
parse_body.add_statement("return null", ());
parse_body.end_control_flow();
let safe_parse_fn = FunSpec::builder("safeParse")
.visibility(Visibility::Public)
.add_param(param("input"))
.body(parse_body.build().unwrap())
.build()
.unwrap();
let mut range_body = CodeBlock::builder();
range_body.begin_control_flow("for (let i = start; i < end; i++)", ());
range_body.add_statement("yield i", ());
range_body.end_control_flow();
let range_fn = FunSpec::builder("*range")
.visibility(Visibility::Public)
.add_param(param("start"))
.add_param(param("end"))
.body(range_body.build().unwrap())
.build()
.unwrap();
let trigger = CodeBlock::of("// %T", (event_emitter,)).unwrap();
FileSpec::builder_with("app.js", JavaScript::new())
.add_code(trigger)
.add_type(logger)
.add_function(fetch_fn)
.add_function(configure_fn)
.add_function(safe_parse_fn)
.add_function(range_fn)
.build()
.unwrap()
.render(80)
.unwrap()
}
fn macro_approach() -> String {
let format_msg = TypeName::importable("./utils", "formatMessage");
let event_emitter = TypeName::importable("events", "EventEmitter");
let ctor_body = sigil_quote!(JavaScript {
super();
this.name = name;
})
.unwrap();
let log_body = sigil_quote!(JavaScript {
const msg = $T(format_msg)(this.name, message);
console.log(msg);
})
.unwrap();
let create_body = sigil_quote!(JavaScript {
return new Logger(name);
})
.unwrap();
let logger = TypeSpec::builder("Logger", TypeKind::Class)
.visibility(Visibility::Public)
.extends(TypeName::primitive("EventEmitter"))
.doc("Logger with event emission.")
.add_method(
FunSpec::builder("constructor")
.add_param(param("name"))
.body(ctor_body)
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("log")
.add_param(param("message"))
.body(log_body)
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("create")
.is_static()
.add_param(param("name"))
.body(create_body)
.build()
.unwrap(),
)
.build()
.unwrap();
let fetch_body = sigil_quote!(JavaScript {
const response = await fetch(url);
if(!response.ok) {
throw new Error($S("HTTP error: ") + response.status);
}
return response.json();
})
.unwrap();
let fetch_fn = FunSpec::builder("fetchJSON")
.visibility(Visibility::Public)
.is_async()
.add_param(param("url"))
.body(fetch_body)
.build()
.unwrap();
let configure_body = sigil_quote!(JavaScript {
console.log(host, port);
})
.unwrap();
let configure_fn = FunSpec::builder("configure")
.visibility(Visibility::Public)
.add_param(param("host"))
.add_param(
ParameterSpec::builder("port", TypeName::primitive(""))
.default_value(CodeBlock::of("8080", ()).unwrap())
.build()
.unwrap(),
)
.body(configure_body)
.build()
.unwrap();
let parse_body = sigil_quote!(JavaScript {
try {
return JSON.parse(input);
} catch(e) {
return null;
}
})
.unwrap();
let safe_parse_fn = FunSpec::builder("safeParse")
.visibility(Visibility::Public)
.add_param(param("input"))
.body(parse_body)
.build()
.unwrap();
let range_body = sigil_quote!(JavaScript {
for(let i = start; i < end; i++) {
yield i;
}
})
.unwrap();
let range_fn = FunSpec::builder("*range")
.visibility(Visibility::Public)
.add_param(param("start"))
.add_param(param("end"))
.body(range_body)
.build()
.unwrap();
let trigger = CodeBlock::of("// %T", (event_emitter,)).unwrap();
FileSpec::builder_with("app.js", JavaScript::new())
.add_code(trigger)
.add_type(logger)
.add_function(fetch_fn)
.add_function(configure_fn)
.add_function(safe_parse_fn)
.add_function(range_fn)
.build()
.unwrap()
.render(80)
.unwrap()
}