From 340a804e550cb5b733bd2e64e515e79740bb6338 Mon Sep 17 00:00:00 2001 From: "Adam T. Carpenter" Date: Sat, 14 Sep 2024 11:22:54 -0400 Subject: feat: impl tutors/about/team view --- src/main.rs | 211 ++++++++++++++++++++------------------------ templates/about/blurb.html | 5 +- templates/about/figure.html | 8 +- templates/about/team.html | 4 +- templates/post.html | 2 +- templates/posts.html | 4 +- 6 files changed, 105 insertions(+), 129 deletions(-) diff --git a/src/main.rs b/src/main.rs index d07d201..7b5ddfc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,13 @@ +use std::cmp::Ordering; +use std::borrow::Cow; use tower_http::services::ServeDir; use axum::response::Html; use axum::extract::State; use std::sync::Arc; use askama_axum::Template; use axum::{routing::get, Router}; -use std::borrow::Cow; -use std::ffi::OsString; use std::fmt; use std::fs; -use std::io::*; -use std::path::Path; use std::path::PathBuf; use chrono::Datelike; @@ -18,177 +16,158 @@ fn current_year() -> i32 { } trait Post: fmt::Debug { - fn dump(&self); - fn display_name(&self) -> Cow; - fn get_content(&self) -> Cow; -} - -trait Tutor: fmt::Debug { - fn id(&self) -> &str; - fn display_name(&self) -> &str; - fn blurb(&self) -> &str; + fn get_title(&self) -> &str; + fn get_article(&self) -> Cow; } -struct MdFilePost { - path: PathBuf, - name: OsString, +#[derive(Debug)] +struct FsPost { + file: PathBuf, } -impl MdFilePost { - fn new(path: &Path) -> Self { - Self { - path: path.to_path_buf(), - name: path.file_stem().unwrap_or_default().to_owned(), - } +impl Post for FsPost { + fn get_title(&self) -> &str { + self.file.file_name().unwrap().to_str().unwrap() } -} -impl Post for MdFilePost { - fn dump(&self) { - println!("{}: {}", &self.display_name(), &self.get_content()); + fn get_article(&self) -> Cow { + let article = fs::read_to_string(&self.file).unwrap(); + Cow::Owned(article) } +} - fn display_name(&self) -> Cow { - self.name.to_string_lossy() - } +trait PostRepo { + fn load(&self) -> impl IntoIterator; +} - fn get_content(&self) -> Cow { - let mut file = std::fs::File::open(&self.path).unwrap(); - let mut markdown = String::new(); - file.read_to_string(&mut markdown).unwrap(); - Cow::Owned(markdown) - } +struct FsPostRepo { + dir: PathBuf, } -impl fmt::Debug for MdFilePost { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "MdFilePost '{}'", self.display_name()) +impl FsPostRepo { + fn with_dir(path: impl Into) -> Self { + Self { + dir: path.into() + } } } -trait Posts { - fn get_posts(&self) -> impl IntoIterator; +impl PostRepo for FsPostRepo { + fn load(&self) -> impl IntoIterator { + let files = fs::read_dir(&self.dir).unwrap(); + files + .flatten() + .filter(|d| !d.file_name().to_string_lossy().starts_with('.')) + .map(|d| FsPost { file: d.path() }) + } } -trait Tutors { - fn get_tutors(&self) -> impl IntoIterator; -} -struct FsDirPosts { - path: PathBuf, +#[derive(Template)] +#[template(path = "posts.html")] +struct PostsView { + posts: Vec

} -impl FsDirPosts { - fn new(path: &str) -> Self { +impl PostsView

{ + fn with_posts(posts: impl IntoIterator) -> Self { Self { - path: PathBuf::from(path), + posts: posts.into_iter().collect() } } } -struct FsDirTutors { - path: PathBuf, +async fn posts_handler(State(repo): State>) -> Html { + let view = PostsView::with_posts(repo.load()); + Html(view.render().unwrap()) } -impl Tutors for FsDirTutors { - fn get_tutors(&self) -> impl IntoIterator { - let test: Vec = Vec::new(); - test - } +trait Tutor: fmt::Debug + Ord { + fn get_name(&self) -> &str; + fn get_id(&self) -> &str; + fn get_blurb(&self) -> Cow; } -impl FsDirTutors { - fn new(path: &str) -> Self { - Self { - path: PathBuf::from(path), - } - } +#[derive(Debug, Eq)] +struct FsTutor { + dir: PathBuf } -impl Posts for FsDirPosts { - fn get_posts(&self) -> impl IntoIterator { - let files = fs::read_dir(&self.path).unwrap(); - files.flatten().filter(|d| !d.path().file_stem().unwrap().to_str().unwrap_or_default().starts_with('.')).map(|d| MdFilePost::new(&d.path())) +impl Tutor for FsTutor { + fn get_id(&self) -> &str { + self.dir.file_name().unwrap().to_str().unwrap() } -} -trait Page: Template { - fn from_post(post: &impl Post) -> Self; -} + fn get_name(&self) -> &str { + self.get_id() + } -#[derive(Template)] -#[template(path = "post.html")] -struct MdPage { - article: String, + fn get_blurb(&self) -> Cow { + let mut path = self.dir.to_owned(); + path.push(format!("{}.md", self.get_id())); + let blurb = fs::read_to_string(path).unwrap(); + Cow::Owned(blurb) + } } -impl Page for MdPage { - fn from_post(post: &impl Post) -> Self { - Self { - article: post.get_content().into_owned(), - } +impl Ord for FsTutor { + fn cmp(&self, other: &Self) -> Ordering { + self.get_id().cmp(other.get_id()) } } -#[derive(Template)] -#[template(path = "posts.html")] -struct PostsView { - post_titles: Vec +impl PartialOrd for FsTutor { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } -impl PostsView { - fn with_posts(posts: &impl Posts) -> Self { - Self { - post_titles: posts.get_posts().into_iter().map(|p| p.display_name().into_owned()).map(String::from).collect() - } +impl PartialEq for FsTutor { + fn eq(&self, other: &Self) -> bool { + self.get_id() == other.get_id() } } -// TODO: one single Markdown struct with id, name, and content can take over for both MdTutor and -// MdPost if it implements the Tutor and Post traits -// With this, I could have a repo which does all the same loading but returns things of a generic T -// as long as that T is provided it'll map values into those fields? - -async fn posts_handler(State(posts): State>) -> Html { - let view = PostsView::with_posts(&*posts); - Html(view.render().unwrap()) +trait TutorRepo { + fn load(&self) -> impl IntoIterator; } -#[derive(Debug)] -struct MdTutor { - id: String, - name: String, - blurb: String, +struct FsTutorRepo { + dir: PathBuf } -impl Tutor for MdTutor { - fn id(&self) -> &str { - self.id.as_str() +impl FsTutorRepo { + fn with_dir(path: impl Into) -> Self { + Self { + dir: path.into() + } } +} - fn display_name(&self) -> &str { - self.name.as_str() - } - fn blurb(&self) -> &str { - self.blurb.as_str() +impl TutorRepo for FsTutorRepo { + fn load(&self) -> impl IntoIterator { + let dirs = fs::read_dir(&self.dir).unwrap(); + dirs.flatten().filter(|d| !d.path().file_stem().unwrap().to_str().unwrap_or_default().starts_with('.')).map(|d| FsTutor { dir: d.path() }) } } + #[derive(Template)] #[template(path = "about/index.html")] struct AboutView { tutors: Vec, } - -impl AboutView { - fn with_tutors(tutors: &impl Tutors) -> Self { - todo!() +impl AboutView { + fn with_tutors(tutors: impl IntoIterator) -> Self { + let mut tutors: Vec = tutors.into_iter().collect(); + tutors.sort(); + Self { tutors } } } -async fn about_handler(State(tutors): State>) -> Html { - let view: AboutView = AboutView::with_tutors(&*tutors); +async fn about_handler(State(repo): State>) -> Html { + let view = AboutView::with_tutors(repo.load()); Html(view.render().unwrap()) } @@ -218,8 +197,8 @@ async fn brochure_handler() -> Html { #[tokio::main] async fn main() { - let posts = Arc::new(FsDirPosts::new(&format!("/data/ct/{}", "blog"))); - let tutors = Arc::new(FsDirTutors::new(&format!("/data/ct/{}", "team"))); + let posts = Arc::new(FsPostRepo::with_dir(format!("/data/ct/{}", "blog"))); + let tutors = Arc::new(FsTutorRepo::with_dir(format!("/data/ct/{}", "team"))); let app = Router::new() .route("/", get(index_handler)) diff --git a/templates/about/blurb.html b/templates/about/blurb.html index 630cf65..4389d47 100644 --- a/templates/about/blurb.html +++ b/templates/about/blurb.html @@ -1,4 +1,3 @@ -

-

{{ tutor.display_name() }}

-

{{ tutor.blurb()|markdown }}

+
+ {{ tutor.get_blurb()|markdown }}
diff --git a/templates/about/figure.html b/templates/about/figure.html index 2b8e308..4a4c365 100644 --- a/templates/about/figure.html +++ b/templates/about/figure.html @@ -1,8 +1,8 @@
- - - {{ tutor.display_name() }} + + + {{ tutor.get_name() }} -
{{ tutor.display_name() }}
+
{{ tutor.get_name() }}
diff --git a/templates/about/team.html b/templates/about/team.html index b1f0588..2857efe 100644 --- a/templates/about/team.html +++ b/templates/about/team.html @@ -1,8 +1,6 @@ -{% let flip = true %} - {% for tutor in tutors %}
- {% if flip == true %} + {% if loop.index0 % 2 == 0 %} {% include "blurb.html" %} {% include "figure.html" %} {% else %} diff --git a/templates/post.html b/templates/post.html index 57964e9..8346f16 100644 --- a/templates/post.html +++ b/templates/post.html @@ -2,7 +2,7 @@
- {{ article|markdown }} + {{ post.get_article()|markdown }}
diff --git a/templates/posts.html b/templates/posts.html index 9f522a6..57d2480 100644 --- a/templates/posts.html +++ b/templates/posts.html @@ -4,8 +4,8 @@

    - {% for title in post_titles %} -
  • {{ title }}
  • + {% for post in posts %} +
  • {{ post.get_title() }}
  • {% endfor %}

-- cgit v1.2.3