Compare commits
	
		
			No commits in common. "1b22c8d118baf81fc87688f7ee3402512ac1f179" and "9ec0e6f17980e4e88335b5e7d1c857bddd14adce" have entirely different histories. 
		
	
	
		
			1b22c8d118
			...
			9ec0e6f179
		
	
		| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
mod cli;
 | 
			
		||||
mod db;
 | 
			
		||||
mod server;
 | 
			
		||||
mod template;
 | 
			
		||||
 | 
			
		||||
use std::{
 | 
			
		||||
    fs,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,7 @@
 | 
			
		|||
use axum::{extract::State, response::Html, routing::post, Form, Router};
 | 
			
		||||
use tera::Context;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    db,
 | 
			
		||||
    template::{Template, Update},
 | 
			
		||||
};
 | 
			
		||||
use crate::db;
 | 
			
		||||
 | 
			
		||||
pub fn app() -> axum::Router<crate::Context> {
 | 
			
		||||
    Router::new().route("/", post(post_event))
 | 
			
		||||
| 
						 | 
				
			
			@ -20,5 +17,5 @@ async fn post_event(
 | 
			
		|||
 | 
			
		||||
    let mut context = Context::new();
 | 
			
		||||
    context.insert("event", &event);
 | 
			
		||||
    Ok(Html(Update::EventLi.render(&ctx.tera, &context)?))
 | 
			
		||||
    Ok(Html(ctx.tera.render("updates/event_li.html", &context)?))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,6 @@ use std::{io::BufWriter, path::PathBuf};
 | 
			
		|||
use super::error::AppError;
 | 
			
		||||
use crate::{
 | 
			
		||||
    db::{self, Image, NewImage, Pagination},
 | 
			
		||||
    template::{Template, View},
 | 
			
		||||
    IMG_DIR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -78,9 +77,12 @@ async fn get_images(
 | 
			
		|||
    let is_final_page = images.len() < page.per_page.try_into().unwrap();
 | 
			
		||||
    context.insert("is_final_page", &is_final_page);
 | 
			
		||||
 | 
			
		||||
    Ok(Html(
 | 
			
		||||
        View::Images.headers(&headers).render(&ctx.tera, &context)?,
 | 
			
		||||
    ))
 | 
			
		||||
    Ok(Html(super::render_view(
 | 
			
		||||
        &ctx.tera,
 | 
			
		||||
        "views/images.html",
 | 
			
		||||
        &context,
 | 
			
		||||
        &headers,
 | 
			
		||||
    )?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_image_original(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,14 +18,40 @@ use axum::{
 | 
			
		|||
use tera::Context;
 | 
			
		||||
use tower_http::{services::ServeDir, set_header::SetResponseHeaderLayer};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    db::Plant,
 | 
			
		||||
    template::{Template, View},
 | 
			
		||||
};
 | 
			
		||||
use crate::db::Plant;
 | 
			
		||||
 | 
			
		||||
pub type Result<T> = std::result::Result<T, error::AppError>;
 | 
			
		||||
 | 
			
		||||
const HX_REQUEST_HEADER: &str = "HX-Request";
 | 
			
		||||
const HX_HISTORY_RESTORE_HEADER: &str = "HX-History-Restore-Request";
 | 
			
		||||
 | 
			
		||||
pub fn should_render_full(headers: &HeaderMap) -> bool {
 | 
			
		||||
    let is_htmx_req = headers.get(HX_REQUEST_HEADER).is_some();
 | 
			
		||||
    let is_hist_restore_req = headers
 | 
			
		||||
        .get(HX_HISTORY_RESTORE_HEADER)
 | 
			
		||||
        .map(|val| val == HeaderValue::from_static("true"))
 | 
			
		||||
        .unwrap_or(false);
 | 
			
		||||
 | 
			
		||||
    !is_htmx_req || is_hist_restore_req
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn render_view(
 | 
			
		||||
    tera: &tera::Tera,
 | 
			
		||||
    view: &str,
 | 
			
		||||
    ctx: &tera::Context,
 | 
			
		||||
    headers: &HeaderMap,
 | 
			
		||||
) -> tera::Result<String> {
 | 
			
		||||
    let view = tera.render(view, ctx)?;
 | 
			
		||||
 | 
			
		||||
    if should_render_full(headers) {
 | 
			
		||||
        let mut ctx = tera::Context::new();
 | 
			
		||||
        ctx.insert("view", &view);
 | 
			
		||||
 | 
			
		||||
        tera.render("base.html", &ctx)
 | 
			
		||||
    } else {
 | 
			
		||||
        Ok(view)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn app(ctx: crate::Context, static_dir: impl AsRef<Path>) -> axum::Router {
 | 
			
		||||
    let router = Router::new()
 | 
			
		||||
| 
						 | 
				
			
			@ -58,18 +84,23 @@ pub async fn render_home(ctx: crate::Context, headers: &HeaderMap) -> Result<Htm
 | 
			
		|||
 | 
			
		||||
    let mut context = Context::new();
 | 
			
		||||
    context.insert("plants", &plants);
 | 
			
		||||
 | 
			
		||||
    Ok(Html(
 | 
			
		||||
        View::Index.headers(headers).render(&ctx.tera, &context)?,
 | 
			
		||||
    ))
 | 
			
		||||
    Ok(Html(render_view(
 | 
			
		||||
        &ctx.tera,
 | 
			
		||||
        "views/index.html",
 | 
			
		||||
        &context,
 | 
			
		||||
        headers,
 | 
			
		||||
    )?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn render_login(ctx: crate::Context, headers: &HeaderMap) -> Result<Html<String>> {
 | 
			
		||||
    let context = Context::new();
 | 
			
		||||
 | 
			
		||||
    Ok(Html(
 | 
			
		||||
        View::Login.headers(headers).render(&ctx.tera, &context)?,
 | 
			
		||||
    ))
 | 
			
		||||
    Ok(Html(render_view(
 | 
			
		||||
        &ctx.tera,
 | 
			
		||||
        "views/login.html",
 | 
			
		||||
        &context,
 | 
			
		||||
        &headers,
 | 
			
		||||
    )?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_index(State(ctx): State<crate::Context>, headers: HeaderMap) -> Result<Html<String>> {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,10 +7,7 @@ use axum::{
 | 
			
		|||
};
 | 
			
		||||
use tera::Context;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    db::{self, DbError, Event, Pagination, Plant},
 | 
			
		||||
    template::{Template, Update, View},
 | 
			
		||||
};
 | 
			
		||||
use crate::db::{self, DbError, Event, Pagination, Plant};
 | 
			
		||||
 | 
			
		||||
use super::{error::AppError, query::ToQuery};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,9 +43,12 @@ async fn get_plant_page(
 | 
			
		|||
            context.insert("events", &events);
 | 
			
		||||
            context.insert("event_types", &db::EVENT_TYPES);
 | 
			
		||||
 | 
			
		||||
            Ok(Html(
 | 
			
		||||
                View::Plant.headers(&headers).render(&ctx.tera, &context)?,
 | 
			
		||||
            ))
 | 
			
		||||
            Ok(Html(super::render_view(
 | 
			
		||||
                &ctx.tera,
 | 
			
		||||
                "views/plant.html",
 | 
			
		||||
                &context,
 | 
			
		||||
                &headers,
 | 
			
		||||
            )?))
 | 
			
		||||
        }
 | 
			
		||||
        None => Err(AppError::NotFound),
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -75,9 +75,12 @@ async fn get_plants(
 | 
			
		|||
        context.insert("query", &page.to_query().encode());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(Html(
 | 
			
		||||
        View::Plants.headers(&headers).render(&ctx.tera, &context)?,
 | 
			
		||||
    ))
 | 
			
		||||
    Ok(Html(super::render_view(
 | 
			
		||||
        &ctx.tera,
 | 
			
		||||
        "views/plants.html",
 | 
			
		||||
        &context,
 | 
			
		||||
        &headers,
 | 
			
		||||
    )?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn post_plant(
 | 
			
		||||
| 
						 | 
				
			
			@ -90,5 +93,5 @@ async fn post_plant(
 | 
			
		|||
 | 
			
		||||
    let mut context = Context::new();
 | 
			
		||||
    context.insert("plant", &plant);
 | 
			
		||||
    Ok(Html(Update::PlantLi.render(&ctx.tera, &context)?))
 | 
			
		||||
    Ok(Html(ctx.tera.render("updates/plant_li.html", &context)?))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +0,0 @@
 | 
			
		|||
mod update;
 | 
			
		||||
mod view;
 | 
			
		||||
 | 
			
		||||
pub use update::Update;
 | 
			
		||||
pub use view::View;
 | 
			
		||||
 | 
			
		||||
pub trait Template {
 | 
			
		||||
    /// Returns the name or path used to identify the template in the Tera struct
 | 
			
		||||
    fn template(&self) -> &'static str;
 | 
			
		||||
 | 
			
		||||
    /// Render the template with the given context
 | 
			
		||||
    fn render(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result<String> {
 | 
			
		||||
        tera.render(self.template(), ctx)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,15 +0,0 @@
 | 
			
		|||
use super::Template;
 | 
			
		||||
 | 
			
		||||
pub enum Update {
 | 
			
		||||
    EventLi,
 | 
			
		||||
    PlantLi,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Template for Update {
 | 
			
		||||
    fn template(&self) -> &'static str {
 | 
			
		||||
        match self {
 | 
			
		||||
            Update::EventLi => "updates/event_li.html",
 | 
			
		||||
            Update::PlantLi => "updates/plant_li.html",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,68 +0,0 @@
 | 
			
		|||
use axum::http::{HeaderMap, HeaderValue};
 | 
			
		||||
 | 
			
		||||
use super::Template;
 | 
			
		||||
 | 
			
		||||
const HX_REQUEST_HEADER: &str = "HX-Request";
 | 
			
		||||
const HX_HISTORY_RESTORE_HEADER: &str = "HX-History-Restore-Request";
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum View {
 | 
			
		||||
    Plant,
 | 
			
		||||
    Plants,
 | 
			
		||||
    Images,
 | 
			
		||||
    Index,
 | 
			
		||||
    Login,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl View {
 | 
			
		||||
    pub fn headers(self, headers: &HeaderMap) -> ViewWrapper {
 | 
			
		||||
        let is_htmx_req = headers.get(HX_REQUEST_HEADER).is_some();
 | 
			
		||||
        let is_hist_restore_req = headers
 | 
			
		||||
            .get(HX_HISTORY_RESTORE_HEADER)
 | 
			
		||||
            .map(|val| val == HeaderValue::from_static("true"))
 | 
			
		||||
            .unwrap_or(false);
 | 
			
		||||
 | 
			
		||||
        let include_base = !is_htmx_req || is_hist_restore_req;
 | 
			
		||||
 | 
			
		||||
        ViewWrapper {
 | 
			
		||||
            view: self,
 | 
			
		||||
            include_base,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Template for View {
 | 
			
		||||
    fn template(&self) -> &'static str {
 | 
			
		||||
        match self {
 | 
			
		||||
            View::Plant => "views/plant.html",
 | 
			
		||||
            View::Plants => "views/plants.html",
 | 
			
		||||
            View::Index => "views/index.html",
 | 
			
		||||
            View::Images => "views/images.html",
 | 
			
		||||
            View::Login => "views/login.html",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct ViewWrapper {
 | 
			
		||||
    view: View,
 | 
			
		||||
    include_base: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Template for ViewWrapper {
 | 
			
		||||
    fn template(&self) -> &'static str {
 | 
			
		||||
        self.view.template()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn render(&self, tera: &tera::Tera, ctx: &tera::Context) -> tera::Result<String> {
 | 
			
		||||
        let view = self.view.render(tera, ctx)?;
 | 
			
		||||
 | 
			
		||||
        if self.include_base {
 | 
			
		||||
            let mut ctx = tera::Context::new();
 | 
			
		||||
            ctx.insert("view", &view);
 | 
			
		||||
 | 
			
		||||
            tera.render("base.html", &ctx)
 | 
			
		||||
        } else {
 | 
			
		||||
            Ok(view)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue