Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Template::custom but without configured template directory #1792

Open
ssendev opened this issue Jul 28, 2021 · 6 comments
Open

Template::custom but without configured template directory #1792

ssendev opened this issue Jul 28, 2021 · 6 comments
Labels
suggestion A suggestion to change functionality

Comments

@ssendev
Copy link

ssendev commented Jul 28, 2021

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 its Tera::defalt() state and could be called in Template::custom

  • Ideally there would be a Template::compiled_in_fairing() which would behave like Template::fairing but 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]);
@ssendev ssendev added the suggestion A suggestion to change functionality label Jul 28, 2021
@jebrosen
Copy link
Collaborator

jebrosen commented Aug 8, 2021

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 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)

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_custom which uses Tera::default instead of Tera::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 like Template::fairing but 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.

@ssendev
Copy link
Author

ssendev commented Aug 8, 2021

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.

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::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.

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.

@jebrosen
Copy link
Collaborator

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.

@cubetastic33
Copy link

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.

@jebrosen
Copy link
Collaborator

Sure! Values can be set programmatically and used with the the rocket::custom constructor.

@ogarcia
Copy link

ogarcia commented Jan 19, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
suggestion A suggestion to change functionality
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants