How to optionally call a method in a chain

This is the code to build a lettre email:

let email = Message::builder()
    .from(from)
    .to(to)
    .reply_to(reply_to)
    .header(ContentType::TEXT_PLAIN)
    .subject(subject)
    .body(body)
    .unwrap();

The thing is, reply_to could be empty, in which case I don't want to include the .reply_to(reply_to) method in this chain, because it will lead to a panic. There must be a way, but I am such a beginner...

let mut email_builder = Message::builder()
    .from(from)
    .to(to);
if ... {
    email_builder = email_builder.reply_to(reply_to);
}
2 Likes

...
alternative solution:

...
.to(to)
.map(|builder| match reply_to {
    Some(val) => build.reply_to(val),
    None => builder
})
.header(...
1 Like

Something like this I hope will work. The problem here is that 'MessageBuilder' is not an iterator, it seems there is no item builder to work with.

You can use the Pipe trait from the tap crate to get the if into the chain:

let email = Message::builder()
    .from(from)
    .to(to)
    .pipe(|m| if ... { m.reply_to(...) } else { m })
2 Likes

I tried that before, but: no method named 'reply_to' found for enum 'Result' in the current scope. It seems like that when the builder finishes, it is no longer a builder type...

Call pipe before calling body. body returns a Result<Message, ...> and should be called after all the builder methods.

1 Like

Looked very hopeful, but the pipe seems to return () and nothing after it applies... method not found in '()'

It seems even after calling .from and .to, the object is already no longer a MessageBuilder, but a Message... Is it something in the lettre design that makes it very difficult to conditionally apply a method on their builder objects? I don't like to have to make a separate builder for every occasion, I am already splitting up between "plain" and "with attachment"...

That shouldn't be the case, based on the docs for lettre::message::MessageBuilder - only body, multipart or singlepart will change ti from a MessageBuilder into Result<Message, EmailError>.

Can you put together a complete example showing the error, that we can clone and build locally? The ideal is if I can run git clone to get your example code, then cargo build to see the failure. That way, we're more likely to be able to help, because we can see exactly what you code looks like

1 Like

You can check out GitHub - pepa65/listsend: Send emails to CSV list from template and try solutions at the bottom of main.rs, that would be amazing,

I also tried this with tap and tap_mut, which at least compiled, but didn't act on the object...

Actually building and making that work is rather challenging - it's not as simple as cargo build and observe the error you're seeing. Could you commit a branch that fails to compile, and explain what you're expecting to see?

At a guess, the following would work for you:

            let mut email = Message::builder()
                .from(env.smtp_from.parse().unwrap())
                .to(to.parse().unwrap());
             if Some(reply_to) = reply_to {
                email = email.reply_to(reply_to);
             }
             email.header(if html {ContentType::TEXT_HTML} else {ContentType::TEXT_PLAIN})
                .subject(hbs.render("subject", &line).unwrap())
                .body(hbs.render("body", &line).unwrap())
                .unwrap();
1 Like

Yes, everybody says that should work and it doesn't. How about you just put that in the file and do cargo rel?

EDIT: Actually, one thing in your suggestion is different, you're not proposing email = email... but plain email... and if I put that in the match statement, it works! I don't think it's very beautiful, but I'll go with this.

Without changes, this fails to cargo build for me because it's got a huge pile of dependencies, and I don't have a C musl toolchain installed - can you give me a version without (at least) the psm dependency, since that fails for me.

I've updated the repo with the 0.4.1 working version for reply_to.

I don't know what the psm dependency is, but to install the musl toolchain I think is just rustup target add x86_64-unknown-linux-musl.

Strange I thought, that the .pipe didn't work, you would really expect it to. Then it occurred to me: in my rush to do everything 'proper' I had added a semicolon before the closing curly brackets, which is why it returned ().

This is the solution I was hoping for, and I will use this in the future!

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.