Rocket
Rocket copied to clipboard
Template::custom but without configured template directory
Existing Functionality
I want to ship a single file without a templates directory.
One way it is possible now is to use a managed state Tera instance but this requires changing the routes which means it is not possible to have templates included in release and reloaded from files during development.
With rocket.attach(Template::custom(… it is possible to cfg!(debug_assertions) engines.tera.add_raw_template("index.html", include_str!("../templates/index.html.tera")). This works but still requires an empty templates folder to be present.
(Well it almost works. Since rocket strips two extensions and templates added with add_raw_template only one template names aren't the same in debug and release. Fortunaltely it is possible to create symlinks from index.html.html.tera to index.html.tera and then one can use index.html as template name)
Suggested Changes
Add Template::fully_custom which uses Tera::default instead of Tera::new
Alternatives Considered
-
I tried
engines.tera = Tera::default()but that didn't have any effect. -
Adding
engines.clear_config()which would reset the Tera instance to itsTera::defalt()state and could be called inTemplate::custom -
Ideally there would be a
Template::compiled_in_fairing()which would behave likeTemplate::fairingbut load templates at compile time but this might be hard to implement.
Full example
// Symlinks from index.html.html.tera to index.html.tera are required
// Refer to templates with index.html instead of index
rocket::build()
.attach(Template::custom(|engines| {
if !cfg!(debug_assertions) {
engines
.tera
.add_raw_template("index.html", include_str!("../templates/index.html.tera"))
.unwrap();
}
}))
.mount("/", routes![index]);
I want to ship a single file without a templates directory.
One way it is possible now is to use a managed state Tera instance but this requires changing the routes which means it is not possible to have templates included in release and reloaded from files during development.
This actually seems pretty close to a good solution already: you could manually manage a Tera instance with the include_str to cover release builds, and in debug only, load the template from the file at every render instead of using the compiled-in copy.
With
rocket.attach(Template::custom(…it is possible tocfg!(debug_assertions)engines.tera.add_raw_template("index.html", include_str!("../templates/index.html.tera")). This works but still requires an empty templates folder to be present.(Well it almost works. Since rocket strips two extensions and templates added with
add_raw_templateonly one template names aren't the same in debug and release. Fortunaltely it is possible to create symlinks fromindex.html.html.teratoindex.html.teraand then one can useindex.htmlas template name)
Note that you could also call engines.tera.add_raw_template("index", include_str!("../templates/index.html.tera")) in order to use the same name in both cases, instead of the symlink.
Suggested Changes
Add
Template::fully_customwhich usesTera::defaultinstead ofTera::new
Tera::default is already used: https://api.rocket.rs/v0.5-rc/src/rocket_dyn_templates/tera_templates.rs.html#15. I think the desired effect here is simply for Rocket to (optionally) not load any templates, and additionally for the configuration to not require a template directory. But yes, I think it would be possible to implement this.
- Ideally there would be a
Template::compiled_in_fairing()which would behave likeTemplate::fairingbut load templates at compile time but this might be hard to implement.
Yes - unfortunately this option is a lot of complexity to add especially at compile time. But I think this need should already be largely fulfilled by other compile-time template engines.
This actually seems pretty close to a good solution already: you could manually manage a
Terainstance with theinclude_strto cover release builds, and in debug only, load the template from the file at every render instead of using the compiled-in copy.
That could work but would still require returning something else than Template. Or maybe implement some trait to make it work?
Note that you could also call
engines.tera.add_raw_template("index", include_str!("../templates/index.html.tera"))in order to use the same name in both cases, instead of the symlink.
That's what I tried first and thought worked until i realized that they were served with mime type text/plain maybe there is another way to get the right mime type without the double html.
Tera::defaultis already used: https://api.rocket.rs/v0.5-rc/src/rocket_dyn_templates/tera_templates.rs.html#15. I think the desired effect here is simply for Rocket to (optionally) not load any templates, and additionally for the configuration to not require a template directory. But yes, I think it would be possible to implement this.
Yes I didn't realize rocket was already loading the templates itself I just found the Tera docs that said new loads templates default does not. I just tried copying the contents of that file to my crate but couldn't proceed since rocket_dyn_templates::engine is private. But even if that worked I may have kept the version with the symlinks since that solution requires less code.
Yes, that particluar suggestion includes to not using Template at all: you would instead call .render(), possibly via your own Responder implementation.
And ouch, I neglected to consider the content-type. The symlink solution seems appropriate since it's already working as desired, and I think it's unlikely for that approach to fundamentally change.
Do you know if it's possible to configure template_dir during compile time? I was able to get it working without an empty templates directory if I set template_dir to ".", but I needed Rocket.toml to configure that and it didn't work when I ran the binary from a different directory. The rocket::Config struct doesn't have an option to set this value either.
Sure! Values can be set programmatically and used with the the rocket::custom constructor.
I think there should be an option similar to embed_migrations!() which now exists for databases. I try with rust-embed but is more complicated than add_raw_template and the problem is the same.