kratenko
kratenko

Reputation: 7594

Rendering a template to string in rust rocket

I'm writing a small website in rust rocket. I'm using handlebar templates, and the way you return a Template form a handler is quite convenient. But I also want to use my template engine for emails I'm sending out. The problem is, that Templates in rocket_contrib do not really support rendering to a String.

There is a method show(), that creates Option<String>, but that one is not supposed to be used.

Render the template named name located at the path root with the context context into a String. This method is very slow and should not be used in any running Rocket application. This method should only be used during testing to validate Template responses. For other uses, use render instead.

The method render() is what you normally use, but this one returns a Template, which is what I started with... You return those and rocket does its magic to produce the resulting html-page.

Is there any way to use my templates in rocket for emails? My mailer (lettre) expects a String.

Upvotes: 4

Views: 1110

Answers (1)

Svetlin Zarev
Svetlin Zarev

Reputation: 15703

It's perfectly fine to use show(). Here is how it's implemented in Rocket:

    pub fn show<S, C>(rocket: &Rocket, name: S, context: C) -> Option<String>
        where S: Into<Cow<'static, str>>, C: Serialize
    {
        let ctxt = rocket.state::<ContextManager>().map(ContextManager::context).or_else(|| {
            // N.B.: removed some logging for brevity
            None
        })?;

        Template::render(name, context).finalize(&ctxt).ok().map(|v| v.0)
    }

As you can see show() does call render() internally, just as the rustdoc told us to do instead. But here it also calls finalize(). Let's check what does it do:

/// Actually render this template given a template context. This method is
/// called by the `Template` `Responder` implementation as well as
/// `Template::show()`.

Ok, so render() does not really render anything. It's finalize() that's doing the job.

We can also check how the Responder is implemented:

/// Returns a response with the Content-Type derived from the template's
/// extension and a fixed-size body containing the rendered template. If
/// rendering fails, an `Err` of `Status::InternalServerError` is returned.
impl Responder<'static> for Template {
    fn respond_to(self, req: &Request) -> response::Result<'static> {
        let ctxt = req.guard::<State<ContextManager>>().succeeded().ok_or_else(|| {
            // N.B.: removed some logging for brevity
            Status::InternalServerError
        })?.inner().context();

        let (render, content_type) = self.finalize(&ctxt)?;
        Content(content_type, render).respond_to(req)
    }
}

TL;DR: It seems that the documentation is misleading. The template implementation is doing absolutely the same thing when you call show() and when you send a response. In both cases it invokes finalize() internally, which produces the String rendering of the template. Thus there should not be any performance penalty of using show()

PS: The code in the current main branch has changed a bit, but the logic is still the same

Upvotes: 2

Related Questions