Files
zero2prod/src/templates.rs
2025-10-03 18:30:09 +02:00

222 lines
5.1 KiB
Rust

use crate::{
authentication::AuthenticatedUser,
domain::{CommentEntry, PostEntry, SubscriberEntry, UserEntry},
routes::{AppError, DashboardStats},
};
use askama::Template;
use axum::response::{Html, IntoResponse};
use uuid::Uuid;
pub struct HtmlTemplate<T>(pub T);
impl<T> IntoResponse for HtmlTemplate<T>
where
T: Template,
{
fn into_response(self) -> axum::response::Response {
match self.0.render() {
Ok(html) => Html(html).into_response(),
Err(err) => AppError::unexpected_page(anyhow::anyhow!(err)).into_response(),
}
}
}
#[derive(Template)]
#[template(path = "user/profile.html")]
pub struct UserTemplate {
pub user: UserEntry,
pub posts: Vec<PostEntry>,
}
#[derive(Template)]
#[template(path = "message.html")]
pub struct MessageTemplate {
pub message: String,
pub error: bool,
}
impl MessageTemplate {
pub fn success(message: String) -> Self {
Self {
message,
error: false,
}
}
pub fn error(message: String) -> Self {
Self {
message,
error: true,
}
}
}
#[derive(Template)]
#[template(path = "../templates/login.html")]
pub struct LoginTemplate;
#[derive(Template)]
#[template(path = "dashboard/dashboard.html")]
pub struct DashboardTemplate {
pub user: AuthenticatedUser,
pub idempotency_key_1: String,
pub idempotency_key_2: String,
pub stats: DashboardStats,
pub subscribers: Vec<SubscriberEntry>,
pub current_page: i64,
pub max_page: i64,
pub users: Vec<UserEntry>,
pub posts: Vec<PostEntry>,
pub posts_current_page: i64,
pub posts_max_page: i64,
}
#[derive(Template)]
#[template(path = "dashboard/posts/list.html", block = "posts")]
pub struct PostsPageDashboardTemplate {
pub posts: Vec<PostEntry>,
pub posts_current_page: i64,
pub posts_max_page: i64,
}
#[derive(Template)]
#[template(path = "home.html")]
pub struct HomeTemplate;
#[derive(Template)]
#[template(path = "posts/list.html")]
pub struct PostsTemplate {
pub posts: Vec<PostEntry>,
pub next_page: Option<i64>,
}
#[derive(Template)]
#[template(path = "posts/list.html", block = "posts")]
pub struct PostListTemplate {
pub posts: Vec<PostEntry>,
pub next_page: Option<i64>,
}
#[derive(Template)]
#[template(path = "posts/page.html")]
pub struct PostTemplate {
pub post: PostEntry,
pub comments: Vec<CommentEntry>,
pub current_page: i64,
pub max_page: i64,
pub comments_count: i64,
}
#[derive(Template)]
#[template(path = "posts/comments/list.html", block = "comments")]
pub struct CommentsList {
pub comments: Vec<CommentEntry>,
pub current_page: i64,
pub max_page: i64,
}
#[derive(Template)]
#[template(path = "dashboard/subscribers/list.html", block = "subs")]
pub struct SubListTemplate {
pub subscribers: Vec<SubscriberEntry>,
pub current_page: i64,
pub max_page: i64,
}
#[derive(Template)]
#[template(path = "subscribe/confirm.html")]
pub struct ConfirmTemplate;
#[derive(Template)]
pub enum ErrorTemplate {
#[template(path = "error/404.html")]
NotFound,
#[template(path = "error/500.html")]
InternalServer,
}
#[derive(Template)]
#[template(path = "unsubscribe/confirm.html")]
pub struct UnsubscribeConfirmTemplate;
#[derive(Template)]
#[template(path = "unsubscribe/form.html")]
pub struct UnsubscribeTemplate;
#[derive(Template)]
#[template(path = "email/new_post.html")]
pub struct NewPostEmailTemplate<'a> {
pub base_url: &'a str,
pub post_title: &'a str,
pub post_id: &'a Uuid,
pub post_excerpt: &'a str,
}
#[derive(Template)]
#[template(path = "email/standalone.html")]
pub struct StandaloneEmailTemplate<'a> {
pub base_url: &'a str,
pub html_content: &'a str,
pub text_content: &'a str,
}
pub trait EmailTemplate: Sync {
fn html(&self) -> String;
fn text(&self) -> String;
}
impl<'a> EmailTemplate for NewPostEmailTemplate<'a> {
fn html(&self) -> String {
self.render().unwrap()
}
fn text(&self) -> String {
format!(
r#"New post available!
Hello there!
I just published a new post that I think you'll find interesting:
"{}"
Read the full post: {}/posts/{}?origin=EMAIL_ID
This post covers practical insights and real-world examples that I hope will be valuable for your backend development journey.
Thanks for being a subscriber!
Best regards,
Alphonse
---
zero2prod - Building better backends with Rust
Visit the blog: {}
Unsubscribe: {}/unsubscribe/confirm?token=UNSUBSCRIBE_TOKEN
You're receiving this because you subscribed to the zero2prod newsletter."#,
self.post_title, self.base_url, self.post_id, self.base_url, self.base_url,
)
}
}
impl<'a> EmailTemplate for StandaloneEmailTemplate<'a> {
fn html(&self) -> String {
self.render().unwrap()
}
fn text(&self) -> String {
format!(
r#"{}
---
zero2prod - Building better backends with Rust
Visit the blog: {}
Unsubscribe: {}/unsubscribe/confirm?token=UNSUBSCRIBE_TOKEN
You're receiving this because you subscribed to the zero2prod newsletter."#,
self.text_content, self.base_url, self.base_url
)
}
}