Enum Page to render templates with content from a struct, set html titles, with rocket responder

For each Struct to be rendered in template, put in a mod called "content" for easy importing
This commit is contained in:
Philippe Loctaux 2023-03-03 08:16:06 +01:00
parent 49a46acef3
commit 279f0bfaa3
12 changed files with 187 additions and 12 deletions

10
Cargo.lock generated
View file

@ -490,6 +490,15 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "erased-serde"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ca605381c017ec7a5fef5e548f1cfaa419ed0f6df6367339300db74c92aa7d"
dependencies = [
"serde",
]
[[package]]
name = "errno"
version = "0.2.8"
@ -522,6 +531,7 @@ name = "ezidam"
version = "0.1.0"
dependencies = [
"database_pool",
"erased-serde",
"infer",
"rocket",
"rocket_db_pools",

View file

@ -8,6 +8,7 @@ rocket = "0.5.0-rc.2"
rocket_db_pools = { version = "0.1.0-rc.2", features = ["sqlx_sqlite"] }
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] }
infer = { version = "0.12.0", default-features = false }
erased-serde = "0.3"
# local crates
database_pool = { path = "../database_pool" }

View file

@ -2,8 +2,21 @@ pub(super) mod catchers;
mod conversion;
mod responder;
use crate::page::Page;
use rocket::http::Status;
use rocket_dyn_templates::{context, Template};
use rocket_dyn_templates::Template;
pub mod content {
use rocket::serde::Serialize;
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Error {
pub message: String,
pub http_code: u16,
pub http_reason: &'static str,
}
}
pub struct Error {
status: Status,
@ -14,16 +27,12 @@ impl Error {
fn new<M: std::fmt::Display>(status: Status, message: M) -> Self {
Self {
status,
template: Template::render(
"error",
context! {
title: format!("{} Error", status.code),
message: message.to_string(),
version: env!("CARGO_PKG_VERSION"),
http_code: status.code,
http_reason: status.reason_lossy()
},
),
template: Page::Error(content::Error {
message: message.to_string(),
http_code: status.code,
http_reason: status.reason_lossy(),
})
.into(),
}
}

View file

@ -1,6 +1,7 @@
mod database;
mod error;
mod file_from_bytes;
mod page;
mod response_timer;
mod routes;
mod shutdown;

View file

@ -0,0 +1,2 @@
pub use crate::error::content::*;
pub use crate::routes::root::content::*;

View file

@ -0,0 +1,41 @@
mod content;
mod responder;
mod template;
use self::content::*;
use erased_serde::Serialize;
pub enum Page {
Error(Error),
Setup(),
Homepage(Homepage),
}
impl Page {
/// Template to render, the names comes from the file in "/templates"
fn template_name(&self) -> &'static str {
match self {
Page::Error(_) => "error",
Page::Setup() => "setup",
Page::Homepage(_) => "homepage",
}
}
/// Title in the html page
fn page_title(&self) -> &'static str {
match self {
Page::Error(_) => "Error",
Page::Setup() => "Setup",
Page::Homepage(_) => "Home",
}
}
/// Structure to render in page
fn content(self) -> Box<dyn Serialize> {
match self {
Page::Error(error) => Box::new(error),
Page::Setup() => Box::new(()),
Page::Homepage(homepage) => Box::new(homepage),
}
}
}

View file

@ -0,0 +1,11 @@
use super::Page;
use rocket::response::Responder;
use rocket::{response, Request};
use rocket_dyn_templates::Template;
impl<'r, 'o: 'r> Responder<'r, 'o> for Page {
fn respond_to(self, req: &Request) -> response::Result<'o> {
// Render template and respond to http request
Template::from(self).respond_to(req)
}
}

View file

@ -0,0 +1,26 @@
use super::Page;
use rocket::serde::Serialize;
use rocket_dyn_templates::Template;
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
struct TemplateContent<S: Serialize> {
title: &'static str,
version: &'static str,
#[serde(flatten)]
content: S,
}
impl From<Page> for Template {
fn from(p: Page) -> Self {
Self::render(
p.template_name(),
TemplateContent {
title: p.page_title(),
version: env!("CARGO_PKG_VERSION"),
content: p.content(),
},
)
}
}

View file

@ -6,6 +6,7 @@ pub(self) mod prelude {
pub use crate::database::Database;
pub use crate::error::Error;
pub use crate::file_from_bytes::FileFromBytes;
pub use crate::page::Page;
pub use rocket_db_pools::Connection;
pub type Result<T> = std::result::Result<T, Error>;

View file

@ -3,7 +3,7 @@ use rocket::{get, routes};
use settings::Settings;
pub fn routes() -> Vec<rocket::Route> {
routes![logo]
routes![logo, setup, homepage]
}
#[get("/logo")]
@ -14,3 +14,25 @@ async fn logo(mut db: Connection<Database>) -> Result<FileFromBytes> {
// HTTP response
Ok(FileFromBytes::from(settings.business_logo()))
}
#[get("/setup")]
async fn setup() -> Page {
Page::Setup()
}
pub mod content {
use rocket::serde::Serialize;
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Homepage {
pub abc: String,
}
}
#[get("/homepage")]
async fn homepage() -> Page {
Page::Homepage(content::Homepage {
abc: "string".to_string(),
})
}

View file

@ -0,0 +1,5 @@
{% extends "base" %}
{% block content %}
<body>homepage goes here {{ abc }}</body>
{% endblock content %}

View file

@ -0,0 +1,46 @@
{% extends "base" %}
{% block content %}
<body class=" d-flex flex-column">
<script src="/js/demo-theme.min.js"></script>
<div class="page page-center">
<div class="container container-tight py-4">
<div class="text-center mb-4">
<a href="/" class="navbar-brand navbar-brand-autodark"><img src="http://localhost:8000/logo" height="96" alt=""></a>
</div>
<div class="card card-md">
<div class="card-body text-center py-4 p-sm-5">
<h1 class="">Welcome to Ezidam!</h1>
<p class="text-muted">Initial setup</p>
</div>
<div class="hr-text hr-text-center hr-text-spaceless">first admin account</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">Username</label>
<input type="text" class="form-control">
</div>
<div class="mb-3">
<label class="form-label">Password</label>
<div class="input-group input-group-flat">
<input type="password" class="form-control" autocomplete="off">
</div>
</div>
</div>
</div>
<div class="row align-items-center mt-3">
<div class="col">
<div class="btn-list justify-content-end">
<a href="#" class="btn btn-primary">
Continue
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Libs JS -->
<!-- Tabler Core -->
<script src="/js/tabler.min.js" defer></script>
<script src="/js/demo.min.js" defer></script>
</body>
{% endblock content %}