Refactored project to use Builder pattern.

This commit is contained in:
2025-10-26 22:01:13 +01:00
parent a908bf0e8a
commit 146509efdf
7 changed files with 669 additions and 284 deletions

View File

@@ -1,115 +1,160 @@
use crate::html::Render; use crate::html::Render;
#[allow(non_camel_case_types)] pub(crate) struct Heading {
pub(crate) struct h1 { level: u8,
id: String,
text: String, text: String,
classes: Vec<String>,
} }
impl Render for h1 { impl Render for Heading {
fn render(&self) -> String { fn render(&self) -> String {
format!("<h1>{}</h1>", self.text) let classes = self.classes.join(" ");
format!(
"<h{} id=\"{}\" class=\"{}\">{}</h{}>",
self.level, self.id, classes, self.text, self.level
)
} }
} }
impl h1 { impl Heading {
pub(crate) fn new<T>(text: T) -> Self pub(crate) fn builder() -> HeadingBuilder {
HeadingBuilder::new()
}
}
pub(crate) struct HeadingBuilder {
level: Option<u8>,
id: Option<String>,
text: Option<String>,
classes: Option<Vec<String>>,
}
impl HeadingBuilder {
pub(crate) fn new() -> Self {
Self {
level: None,
id: None,
text: None,
classes: None,
}
}
pub(crate) fn level(self, level: u8) -> Self {
Self {
level: Some(level),
..self
}
}
pub(crate) fn id<T>(self, id: T) -> Self
where where
T: Into<String>, T: Into<String>,
{ {
Self { text: text.into() } Self {
id: Some(id.into()),
..self
} }
}
#[allow(non_camel_case_types)]
pub(crate) struct h2 {
text: String,
}
impl Render for h2 {
fn render(&self) -> String {
format!("<h2>{}</h2>", self.text)
} }
}
impl h2 { pub(crate) fn text<T>(self, text: T) -> Self
pub(crate) fn new<T>(text: T) -> Self
where where
T: Into<String>, T: Into<String>,
{ {
Self { text: text.into() } Self {
text: Some(text.into()),
..self
}
}
pub(crate) fn classes<T>(self, classes: Vec<T>) -> Self
where
T: Into<String>,
{
Self {
classes: Some(classes.into_iter().map(|x| x.into()).collect()),
..self
}
}
pub(crate) fn build(self) -> Heading {
let level = self.level.unwrap_or(1);
let id = self.id.unwrap_or(String::new());
let text = self.text.unwrap_or(String::new());
let classes = self.classes.unwrap_or(Vec::new());
Heading {
level,
id,
text,
classes,
}
} }
} }
#[allow(non_camel_case_types)] pub(crate) struct Link {
pub(crate) struct link {
rel: String, rel: String,
href: String, href: String,
} }
impl Render for link { impl Render for Link {
fn render(&self) -> String { fn render(&self) -> String {
format!("<link rel=\"{}\" href=\"{}\">", self.rel, self.href) format!("<link rel=\"{}\" href=\"{}\">", self.rel, self.href)
} }
} }
impl link { impl Link {
pub(crate) fn new<T, U>(rel: T, href: U) -> Self pub(crate) fn builder() -> LinkBuilder {
LinkBuilder::new()
}
}
pub(crate) struct LinkBuilder {
rel: Option<String>,
href: Option<String>,
}
impl LinkBuilder {
pub(crate) fn new() -> Self {
Self {
rel: None,
href: None,
}
}
pub(crate) fn rel<T>(self, rel: T) -> Self
where where
T: Into<String>, T: Into<String>,
U: Into<String>,
{ {
Self { Self {
rel: rel.into(), rel: Some(rel.into()),
href: href.into(), ..self
} }
} }
}
#[allow(non_camel_case_types)] pub(crate) fn href<T>(self, href: T) -> Self
pub(crate) struct div {
id: String,
classes: Vec<String>,
content: Vec<Box<dyn Render>>,
}
impl Render for div {
fn render(&self) -> String {
let classes = self.classes.join(" ");
format!(
"<div id=\"{}\" class=\"{}\">{}</div>",
self.id,
classes,
self.content.render()
)
}
}
impl div {
pub(crate) fn new<T, U>(id: T, classes: Vec<U>) -> Self
where where
T: Into<String>, T: Into<String>,
U: Into<String> + Clone,
{ {
Self { Self {
id: id.into(), href: Some(href.into()),
classes: classes.iter().map(|x| x.clone().into()).collect(), ..self
content: vec![],
} }
} }
pub(crate) fn append_element(&mut self, element: impl Render + 'static) { pub(crate) fn build(self) -> Link {
self.content.push(Box::new(element)); let rel = self.rel.unwrap_or(String::new());
let href = self.href.unwrap_or(String::new());
Link { rel, href }
} }
} }
#[allow(non_camel_case_types)] pub(crate) struct Paragraph {
pub(crate) struct p {
id: String, id: String,
classes: Vec<String>, classes: Vec<String>,
text: String, text: String,
} }
impl Render for p { impl Render for Paragraph {
fn render(&self) -> String { fn render(&self) -> String {
let classes = self.classes.join(" "); let classes = self.classes.join(" ");
format!( format!(
@@ -119,29 +164,72 @@ impl Render for p {
} }
} }
impl p { impl Paragraph {
pub(crate) fn new<T, U, V>(id: T, classes: Vec<U>, text: V) -> Self pub(crate) fn builder() -> ParagraphBuilder {
where ParagraphBuilder::new()
T: Into<String>,
U: Into<String> + Clone,
V: Into<String>,
{
Self {
id: id.into(),
classes: classes.iter().map(|x| x.clone().into()).collect(),
text: text.into(),
}
} }
} }
#[allow(non_camel_case_types)] pub(crate) struct ParagraphBuilder {
pub(crate) struct img { id: Option<String>,
classes: Option<Vec<String>>,
text: Option<String>,
}
impl ParagraphBuilder {
pub(crate) fn new() -> Self {
Self {
id: None,
classes: None,
text: None,
}
}
pub(crate) fn id<T>(self, id: T) -> Self
where
T: Into<String>,
{
Self {
id: Some(id.into()),
..self
}
}
pub(crate) fn classes<T>(self, classes: Vec<T>) -> Self
where
T: Into<String>,
{
Self {
classes: Some(classes.into_iter().map(|x| x.into()).collect()),
..self
}
}
pub(crate) fn text<T>(self, text: T) -> Self
where
T: Into<String>,
{
Self {
text: Some(text.into()),
..self
}
}
pub(crate) fn build(self) -> Paragraph {
let id = self.id.unwrap_or(String::new());
let classes = self.classes.unwrap_or(Vec::new());
let text = self.text.unwrap_or(String::new());
Paragraph { id, classes, text }
}
}
pub(crate) struct Image {
id: String, id: String,
classes: Vec<String>, classes: Vec<String>,
src: String, src: String,
} }
impl Render for img { impl Render for Image {
fn render(&self) -> String { fn render(&self) -> String {
let classes = self.classes.join(" "); let classes = self.classes.join(" ");
format!( format!(
@@ -151,30 +239,73 @@ impl Render for img {
} }
} }
impl img { impl Image {
pub(crate) fn new<T, U, V>(id: T, classes: Vec<U>, src: V) -> Self pub(crate) fn builder() -> ImageBuilder {
where ImageBuilder::new()
T: Into<String>,
U: Into<String> + Clone,
V: Into<String>,
{
Self {
id: id.into(),
classes: classes.iter().map(|x| x.clone().into()).collect(),
src: src.into(),
}
} }
} }
#[allow(non_camel_case_types)] pub(crate) struct ImageBuilder {
pub(crate) struct a { id: Option<String>,
classes: Option<Vec<String>>,
src: Option<String>,
}
impl ImageBuilder {
pub(crate) fn new() -> Self {
Self {
id: None,
classes: None,
src: None,
}
}
pub(crate) fn id<T>(self, id: T) -> Self
where
T: Into<String>,
{
Self {
id: Some(id.into()),
..self
}
}
pub(crate) fn classes<T>(self, classes: Vec<T>) -> Self
where
T: Into<String>,
{
Self {
classes: Some(classes.into_iter().map(|x| x.into()).collect()),
..self
}
}
pub(crate) fn src<T>(self, src: T) -> Self
where
T: Into<String>,
{
Self {
src: Some(src.into()),
..self
}
}
pub(crate) fn build(self) -> Image {
let id = self.id.unwrap_or(String::new());
let classes = self.classes.unwrap_or(Vec::new());
let src = self.src.unwrap_or(String::new());
Image { id, classes, src }
}
}
pub(crate) struct Anchor {
id: String, id: String,
classes: Vec<String>, classes: Vec<String>,
href: String, href: String,
text: String, text: String,
} }
impl Render for a { impl Render for Anchor {
fn render(&self) -> String { fn render(&self) -> String {
let classes = self.classes.join(" "); let classes = self.classes.join(" ");
format!( format!(
@@ -184,19 +315,78 @@ impl Render for a {
} }
} }
impl a { impl Anchor {
pub(crate) fn new<T, U, V, W>(id: T, classes: Vec<U>, href: V, text: W) -> Self pub(crate) fn builder() -> AnchorBuilder {
AnchorBuilder::new()
}
}
pub(crate) struct AnchorBuilder {
id: Option<String>,
classes: Option<Vec<String>>,
href: Option<String>,
text: Option<String>,
}
impl AnchorBuilder {
pub(crate) fn new() -> Self {
Self {
id: None,
classes: None,
href: None,
text: None,
}
}
pub(crate) fn id<T>(self, id: T) -> Self
where where
T: Into<String>, T: Into<String>,
U: Into<String> + Clone,
V: Into<String>,
W: Into<String>,
{ {
Self { Self {
id: id.into(), id: Some(id.into()),
classes: classes.iter().map(|x| x.clone().into()).collect(), ..self
href: href.into(), }
text: text.into(), }
pub(crate) fn classes<T>(self, classes: Vec<T>) -> Self
where
T: Into<String>,
{
Self {
classes: Some(classes.into_iter().map(|x| x.into()).collect()),
..self
}
}
pub(crate) fn href<T>(self, href: T) -> Self
where
T: Into<String>,
{
Self {
href: Some(href.into()),
..self
}
}
pub(crate) fn text<T>(self, text: T) -> Self
where
T: Into<String>,
{
Self {
text: Some(text.into()),
..self
}
}
pub(crate) fn build(self) -> Anchor {
let id = self.id.unwrap_or(String::new());
let classes = self.classes.unwrap_or(Vec::new());
let href = self.href.unwrap_or(String::new());
let text = self.text.unwrap_or(String::new());
Anchor {
id,
classes,
href,
text,
} }
} }
} }

83
src/html/layouts.rs Normal file
View File

@@ -0,0 +1,83 @@
use crate::html::Render;
pub(crate) struct Division {
id: String,
classes: Vec<String>,
elements: Vec<Box<dyn Render>>,
}
impl Render for Division {
fn render(&self) -> String {
let classes = self.classes.join(" ");
format!(
"<div id=\"{}\" class=\"{}\">{}</div>",
self.id,
classes,
self.elements.render()
)
}
}
impl Division {
pub(crate) fn builder() -> DivisionBuilder {
DivisionBuilder::new()
}
pub(crate) fn append_element(&mut self, element: impl Render + 'static) {
self.elements.push(Box::new(element));
}
}
pub(crate) struct DivisionBuilder {
id: Option<String>,
classes: Option<Vec<String>>,
elements: Option<Vec<Box<dyn Render>>>,
}
impl DivisionBuilder {
pub(crate) fn new() -> Self {
Self {
id: None,
classes: None,
elements: None,
}
}
pub(crate) fn id<T>(self, id: T) -> Self
where
T: Into<String>,
{
Self {
id: Some(id.into()),
..self
}
}
pub(crate) fn classes<T>(self, classes: Vec<T>) -> Self
where
T: Into<String>,
{
Self {
classes: Some(classes.into_iter().map(|x| x.into()).collect()),
..self
}
}
pub(crate) fn elements(self, elements: Vec<Box<dyn Render>>) -> Self {
Self {
elements: Some(elements),
..self
}
}
pub(crate) fn build(self) -> Division {
let id = self.id.unwrap_or(String::new());
let classes = self.classes.unwrap_or(Vec::new());
let elements = self.elements.unwrap_or(Vec::new());
Division {
id,
classes,
elements,
}
}
}

View File

@@ -1,92 +1,31 @@
pub(crate) mod elements; pub(crate) mod elements;
pub(crate) mod layouts;
pub(crate) mod pages;
macro_rules! boxed_vec {
($($element:ident),+) => {
vec![
$(
Box::new($element) as Box<dyn Render>,
)*
]
};
}
pub(crate) use boxed_vec;
pub(crate) trait Render { pub(crate) trait Render {
fn render(&self) -> String; fn render(&self) -> String;
} }
pub(crate) struct Page {
title: String,
head: Vec<Box<dyn Render>>,
body: Vec<Box<dyn Render>>,
}
pub(crate) struct PageBuilder {
title: Option<String>,
head: Option<Vec<Box<dyn Render>>>
body: Option<Vec<Box<dyn Render>>>
}
impl PageBuilder {
pub(crate) fn new() -> Self {
Self {
title: None,
head: None,
body: None,
}
}
pub(crate) fn title<T>(mut self, title: T)
where T: Into<String>
{
self.title = Some(title.into());
}
}
impl Render for Vec<Box<dyn Render>> { impl Render for Vec<Box<dyn Render>> {
fn render(&self) -> String { fn render(&self) -> String {
let mut result = String::new(); let mut result = String::new();
for element in self { for element in self {
let render = element.render(); let render = element.render();
result.push_str(&render); result.push_str(&render);
result.push('\n');
} }
result result
} }
} }
impl Render for Page {
fn render(&self) -> String {
format!(
"\
<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\">
<title>{}</title>
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />
{}
</head>
<body>
{}
</body>
</html>",
self.title,
self.head.render(),
self.body.render()
)
}
}
impl Page {
pub(crate) fn builder() -> PageBuilder {
PageBuilder::new()
}
pub(crate) fn new<T>(title: T) -> Self
where
T: Into<String>,
{
Page {
title: title.into(),
head: vec![],
body: vec![],
}
}
pub(crate) fn append_element_to_body(&mut self, element: impl Render + 'static) {
self.body.push(Box::new(element));
}
pub(crate) fn append_element_to_head(&mut self, element: impl Render + 'static) {
self.head.push(Box::new(element));
}
}

199
src/html/pages.rs Normal file
View File

@@ -0,0 +1,199 @@
use crate::html::Render;
use crate::html::boxed_vec;
use crate::html::elements::{Anchor, Image, Link, Paragraph};
use crate::html::layouts::Division;
pub(crate) struct Page {
title: String,
head: Vec<Box<dyn Render>>,
body: Vec<Box<dyn Render>>,
}
impl Render for Page {
fn render(&self) -> String {
format!(
"\
<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\">
<title>{}</title>
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />
{}
</head>
<body>
{}
</body>
</html>",
self.title,
self.head.render(),
self.body.render()
)
}
}
impl Page {
pub(crate) fn builder() -> PageBuilder {
PageBuilder::new()
}
pub(crate) fn append_element_to_body(&mut self, element: impl Render + 'static) {
self.body.push(Box::new(element));
}
pub(crate) fn append_element_to_head(&mut self, element: impl Render + 'static) {
self.head.push(Box::new(element));
}
}
pub(crate) struct PageBuilder {
title: Option<String>,
head: Option<Vec<Box<dyn Render>>>,
body: Option<Vec<Box<dyn Render>>>,
}
impl PageBuilder {
pub(crate) fn new() -> Self {
Self {
title: None,
head: None,
body: None,
}
}
pub(crate) fn title<T>(self, title: T) -> Self
where
T: Into<String>,
{
Self {
title: Some(title.into()),
..self
}
}
pub(crate) fn head(self, head: Vec<Box<dyn Render>>) -> Self {
Self {
head: Some(head),
..self
}
}
pub(crate) fn body(self, body: Vec<Box<dyn Render>>) -> Self {
Self {
body: Some(body),
..self
}
}
pub(crate) fn build(self) -> Page {
let title = self.title.unwrap_or(String::new());
let head = self.head.unwrap_or(Vec::new());
let body = self.body.unwrap_or(Vec::new());
Page { title, head, body }
}
}
pub(crate) struct BasePage {
page: Page,
}
impl BasePage {
pub(crate) fn builder() -> BasePageBuilder {
BasePageBuilder::new()
}
pub(crate) fn append_element_to_head(&mut self, element: impl Render + 'static) {
self.page.append_element_to_head(element);
}
pub(crate) fn append_element_to_body(&mut self, element: impl Render + 'static) {
self.page.append_element_to_body(element);
}
}
impl Render for BasePage {
fn render(&self) -> String {
self.page.render()
}
}
pub(crate) struct BasePageBuilder {
title: Option<String>,
head: Option<Vec<Box<dyn Render>>>,
body: Option<Vec<Box<dyn Render>>>,
}
impl BasePageBuilder {
pub(crate) fn new() -> Self {
Self {
title: None,
head: None,
body: None,
}
}
pub(crate) fn title<T>(self, title: T) -> Self
where
T: Into<String>,
{
Self {
title: Some(title.into()),
..self
}
}
pub(crate) fn head(self, head: Vec<Box<dyn Render>>) -> Self {
Self {
head: Some(head),
..self
}
}
pub(crate) fn body(self, body: Vec<Box<dyn Render>>) -> Self {
Self {
body: Some(body),
..self
}
}
pub(crate) fn build(self) -> BasePage {
let title = self.title.unwrap_or(String::new());
let head = self.head.unwrap_or(Vec::new());
let body = self.body.unwrap_or(Vec::new());
let name = Paragraph::builder()
.id("name")
.classes(vec!["name"])
.text("AINDUSTRIES")
.build();
let home = Anchor::builder().id("home").classes(vec!["nav-button"]).href("/").text("Home").build();
let projects = Anchor::builder().id("projects").classes(vec!["nav-button"]).href("/projects").text("Projects").build();
let buttons = Division::builder()
.classes(vec!["nav-buttons"])
.elements(boxed_vec![home, projects])
.build();
let header = Division::builder()
.id("header")
.classes(vec!["header"])
.elements(boxed_vec![name, buttons])
.build();
// background
let image = Image::builder()
.id("background")
.classes(vec!["background"])
.src("/static/img/background.png")
.build();
// css
let base = Link::builder()
.rel("stylesheet")
.href("/static/css/base.css")
.build();
let mut final_head = boxed_vec![base];
final_head.extend(head);
let mut final_body = boxed_vec![image, header];
final_body.extend(body);
let page = Page::builder()
.title(title)
.head(final_head)
.body(final_body)
.build();
BasePage { page }
}
}

View File

@@ -20,7 +20,12 @@ use pages::{
#[derive(Parser)] #[derive(Parser)]
#[command(name = "aindustries-be", version, about = "This is the amazing webserver for aindustries.be!", long_about = None)] #[command(name = "aindustries-be", version, about = "This is the amazing webserver for aindustries.be!", long_about = None)]
struct Cli { struct Cli {
#[arg(short, long, value_name = "CONFIG", help = "Path to config file, defaults to /etc/aindustries-be/config.toml")] #[arg(
short,
long,
value_name = "CONFIG",
help = "Path to config file, defaults to /etc/aindustries-be/config.toml"
)]
config: Option<String>, config: Option<String>,
} }

View File

@@ -1,76 +1,39 @@
pub(crate) mod files; pub(crate) mod files;
pub(crate) mod projects; pub(crate) mod projects;
use crate::html::elements::{a, div, h1, h2, img, link, p}; use crate::html::elements::{Heading, Link};
use crate::html::{Page, Render}; use crate::html::layouts::Division;
use crate::html::pages::BasePage;
use crate::html::{Render, boxed_vec};
use actix_web::{Responder, get}; use actix_web::{Responder, get};
pub(crate) struct BasePage {
page: Page,
}
impl BasePage {
pub(crate) fn new<T>(title: T) -> Self
where
T: Into<String>,
{
let mut page = Page::new(title);
// header
let mut header = div::new("header", vec!["header"]);
let name = p::new("name", vec!["name"], "AINDUSTRIES");
let mut buttons = div::new("buttons", vec!["nav-buttons"]);
let home = a::new("home", vec!["nav-button"], "/", "Home");
let projects = a::new("projects", vec!["nav-button"], "/projects", "Projects");
buttons.append_element(home);
buttons.append_element(projects);
header.append_element(name);
header.append_element(buttons);
// background
let image = img::new(
"background",
vec!["background"],
"/static/img/background.png",
);
// css
let base = link::new("stylesheet", "/static/css/base.css");
// add elements to the page in order
page.append_element_to_head(base);
page.append_element_to_body(image);
page.append_element_to_body(header);
Self { page }
}
pub(crate) fn append_element_to_head(&mut self, element: impl Render + 'static) {
self.page.append_element_to_head(element);
}
pub(crate) fn append_element_to_body(&mut self, element: impl Render + 'static) {
self.page.append_element_to_body(element);
}
}
impl Render for BasePage {
fn render(&self) -> String {
self.page.render()
}
}
#[get("/")] #[get("/")]
async fn index() -> impl Responder { async fn index() -> impl Responder {
let mut page = BasePage::new("AINDUSTRIES");
// introduction // introduction
let mut intro = div::new("intro", vec!["intro"]); let hi = Heading::builder().text("Hello and welcome!").build();
let hi = h1::new("Hello and welcome!"); let detail = Heading::builder()
let detail = h2::new( .level(2)
.text(
"This here is a small website to show the passion I have for computers.<br>\ "This here is a small website to show the passion I have for computers.<br>\
I have always had a good time creating and discovering new things.<br>\ I have always had a good time creating and discovering new things.<br>\
Your may discover some of my projects here on this showcase.", Your may discover some of my projects here on this little showcase.",
); )
intro.append_element(hi); .build();
intro.append_element(detail); let intro = Division::builder()
.id("intro")
.classes(vec!["intro"])
.elements(boxed_vec![hi, detail])
.build();
// css // css
let index = link::new("stylesheet", "static/css/index.css"); let index = Link::builder()
.rel("stylesheet")
.href("static/css/index.css")
.build();
// add elements to the page in order // add elements to the page in order
page.append_element_to_head(index); let page = BasePage::builder()
page.append_element_to_body(intro); .title("AINDUSTRIES")
.head(boxed_vec![index])
.body(boxed_vec![intro])
.build();
page.render() page.render()
} }

View File

@@ -1,64 +1,70 @@
use super::BasePage; use crate::html::elements::{Anchor, Heading, Link, Paragraph};
use crate::html::Render; use crate::html::layouts::Division;
use crate::html::elements::{a, div, h1, h2, link, p}; use crate::html::pages::BasePage;
use crate::html::{Render, boxed_vec};
use actix_web::{Responder, get, web}; use actix_web::{Responder, get, web};
#[get("/projects")] #[get("/projects")]
async fn projects() -> impl Responder { async fn projects() -> impl Responder {
let mut page = BasePage::new("Projects"); let title = Heading::builder().text("My projects").build();
let title = h1::new("My projects"); let desc = Heading::builder()
let desc = h2::new( .level(2)
.text(
"Here you will find all my projects which deserve to be shown<br>\ "Here you will find all my projects which deserve to be shown<br>\
(I've done a lot of small projects but they are not worth it.)", (I've done a lot of small projects but they are not worth it.)",
); )
let mut website = div::new("project-website", vec!["project"]); .build();
let website_title = h1::new("Website"); let website_title = Heading::builder().text("Website").build();
let mut info = div::new("project-website-info", vec!["project-info"]); let website_desc = Paragraph::builder()
let website_desc = p::new( .classes(vec!["project-desc"])
"website-desc", .text("This project is the website you currently are on.")
vec!["project-desc"], .build();
"This project is the website you currently are on.", let view = Anchor::builder().classes(vec!["project-view"]).href("/projects/website").text("Learn More").build();
); let info = Division::builder()
let view = a::new( .classes(vec!["project-info"])
"website-view", .elements(boxed_vec![website_desc, view])
vec!["project-view"], .build();
"/projects/website", let website = Division::builder()
"Learn More", .classes(vec!["project"])
); .elements(boxed_vec![website_title, info])
info.append_element(website_desc); .build();
info.append_element(view); let css = Link::builder()
website.append_element(website_title); .rel("stylesheet")
website.append_element(info); .href("/static/css/projects.css")
let css = link::new("stylesheet", "/static/css/projects.css"); .build();
page.append_element_to_head(css); let page = BasePage::builder()
page.append_element_to_body(title); .title("Projects")
page.append_element_to_body(desc); .head(boxed_vec![css])
page.append_element_to_body(website); .body(boxed_vec![title, desc, website])
.build();
page.render() page.render()
} }
#[get("/projects/{project}")] #[get("/projects/{project}")]
async fn project(project: web::Path<String>) -> impl Responder { async fn project(project: web::Path<String>) -> impl Responder {
let project = project.into_inner(); let project = project.into_inner();
let mut page = BasePage::new(format!("Project-{}", project)); let css = Link::builder()
.rel("stylesheet")
.href("/static/css/project.css")
.build();
let mut page = BasePage::builder()
.title(format!("Project-{}", project))
.head(boxed_vec![css])
.build();
match project.as_str() { match project.as_str() {
"website" => { "website" => {
let title = h1::new("Website"); let title = Heading::builder().text("Website").build();
let desc = p::new( let desc = Paragraph::builder().classes(vec!["description"]).text(
"description",
vec!["description"],
"This project, the website you are on, \ "This project, the website you are on, \
is made in Rust such that all the pages are generated by code.<br>\ is made in Rust such that all the pages are generated by code.<br>\
That is that each html element is represented by a struct which implements the Render trait (as in render the element to html).<br>\ That is that each html element is represented by a struct which implements the Render trait (as in render the element to html).<br>\
As it is right now the system is not that impressive but I believe it can do amazing things in the futur.<br>\ As it is right now the system is not that impressive but I believe it can do amazing things in the futur.<br>\
<br>\ <br>\
Wish to see more? Check out the gitea repository: ", Wish to see more? Check out the gitea repository: ",
); ).build();
page.append_element_to_body(title); page.append_element_to_body(title);
page.append_element_to_body(desc); page.append_element_to_body(desc);
} }
_ => {} _ => {}
} }
let css = link::new("stylesheet", "/static/css/project.css");
page.append_element_to_head(css);
page.render() page.render()
} }