Comments (1)
In this case, are the values needed by the subject a subset of the values needed by the body? If so, I'd just use one type for the former and a second type for the latter, which includes the former in one of its fields.
At work, we use a procedural macro to duplicate a type for emails that we send with both plaintext and HTML bodies.
Usage:
#[email(template = "contact-email")]
pub struct ContactEmail<'a> {
pub site_name: &'a str,
pub sender: &'a str,
pub message: &'a str,
pub inbox_url: &'a str,
}
Macro:
#[proc_macro_attribute]
pub fn email(meta: TokenStream, item: TokenStream) -> TokenStream {
let ast = parse_macro_input!(item as syn::DeriveInput);
let meta = parse_macro_input!(meta as EmailMeta);
// Introduce an `'email` lifetime for the reference to outer type.
// We set up the generics such that all lifetimes used on the item should outlive the
// `'email` lifetime, which is necessary to make some of the impls below work.
let mut email_generics = ast.generics.clone();
let email_lifetime = syn::LifetimeParam::new(syn::Lifetime::new("'email", Span::call_site()));
for lt in email_generics.lifetimes_mut() {
lt.bounds.push(email_lifetime.lifetime.clone());
}
email_generics
.params
.push(syn::GenericParam::Lifetime(email_lifetime));
// Split the generics for use in impls and type definitions, below.
let (_, inner_ty_generics, _) = ast.generics.split_for_impl();
let (impl_generics, ty_generics, where_clause) = email_generics.split_for_impl();
// Set up some bindings for use in the quote!() call below.
let visibility = &ast.vis;
let name = &ast.ident;
let text_type = Ident::new(&format!("{name}Text"), Span::call_site());
let text_template = format!("{}.txt", &meta.template);
let html_type = Ident::new(&format!("{name}Html"), Span::call_site());
let html_template = format!("{}.html", &meta.template);
quote!(
#ast
impl #impl_generics email::BodyTemplates<'email> for #name #inner_ty_generics #where_clause {
type Text = #text_type #ty_generics;
type Html = #html_type #ty_generics;
}
#[derive(askama::Template)]
#[template(path = #text_template)]
#visibility struct #text_type #ty_generics(&'email #name #inner_ty_generics) #where_clause;
impl #impl_generics From<&'email #name #inner_ty_generics> for #text_type #ty_generics #where_clause {
fn from(email: &'email #name #inner_ty_generics) -> Self {
Self(email)
}
}
impl #impl_generics std::ops::Deref for #text_type #ty_generics {
type Target = &'email #name #inner_ty_generics;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(askama::Template)]
#[template(path = #html_template)]
#visibility struct #html_type #ty_generics(&'email #name #inner_ty_generics) #where_clause;
impl #impl_generics std::ops::Deref for #html_type #ty_generics {
type Target = &'email #name #inner_ty_generics;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl #impl_generics From<&'email #name #inner_ty_generics> for #html_type #ty_generics #where_clause {
fn from(email: &'email #name #inner_ty_generics) -> Self {
Self(email)
}
}
).into()
}
struct EmailMeta {
template: String,
}
impl Parse for EmailMeta {
fn parse(input: ParseStream) -> syn::Result<Self> {
for field in Punctuated::<syn::MetaNameValue, Comma>::parse_terminated(input)? {
if field.path.is_ident("template") {
if let syn::Expr::Lit(lit) = &field.value {
if let syn::Lit::Str(lit) = &lit.lit {
return Ok(Self {
template: lit.value(),
});
}
}
}
}
panic!("require template key for email macro");
}
}
from askama.
Related Issues (20)
- global variables HOT 5
- Associated functions on structs with generics are not callable HOT 2
- How could i get session flash message in a view after redirect HOT 2
- Question: How should I return templates in actix-web HOT 2
- Cannot specify single named argument in macro HOT 5
- 0.12.1 only allow lower-case variable name in template HOT 5
- Keep the template source code portion corresponding to each AST token HOT 9
- Allow to have namespace for macros HOT 1
- Allowing imports and macro declaration everywhere? HOT 2
- I could not give relative path to template on linux but can give on windows HOT 7
- Issue
- Regression in askama_parser 0.2.0 update HOT 14
- Composing templates HOT 3
- 8e03fa6132669383a763d628b7443d1758b50400 HOT 1
- Document the `fmt` filter HOT 3
- Can't render simple template. Axum integration. HOT 2
- Add support for filter section HOT 5
- Document how integration should be imported HOT 1
- Remove the need to import askama itself when using integration crates HOT 3
- Why if block does not work inside another block HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from askama.