diff options
author | Adam T. Carpenter <atc@53hor.net> | 2024-09-14 20:30:05 -0400 |
---|---|---|
committer | Adam T. Carpenter <atc@53hor.net> | 2024-09-14 20:30:05 -0400 |
commit | 9f341d439f7aa5fd2365024169ead2d6bdc3210c (patch) | |
tree | c9967da54d4f8ca02385fc3fac4989572fd00eee /src | |
parent | 340a804e550cb5b733bd2e64e515e79740bb6338 (diff) | |
download | carpentertutoring-9f341d439f7aa5fd2365024169ead2d6bdc3210c.tar.xz carpentertutoring-9f341d439f7aa5fd2365024169ead2d6bdc3210c.zip |
feat: rewrite complete
Diffstat (limited to 'src')
-rw-r--r-- | src/handlers.rs | 34 | ||||
-rw-r--r-- | src/helpers.rs | 5 | ||||
-rw-r--r-- | src/main.rs | 213 | ||||
-rw-r--r-- | src/posts.rs | 3 | ||||
-rw-r--r-- | src/posts/abstractions.rs | 2 | ||||
-rw-r--r-- | src/posts/abstractions/post.rs | 6 | ||||
-rw-r--r-- | src/posts/abstractions/repo.rs | 5 | ||||
-rw-r--r-- | src/posts/fs_post.rs | 24 | ||||
-rw-r--r-- | src/posts/fs_post_repo.rs | 23 | ||||
-rw-r--r-- | src/tutors.rs | 3 | ||||
-rw-r--r-- | src/tutors/abstractions.rs | 2 | ||||
-rw-r--r-- | src/tutors/abstractions/tutor.rs | 7 | ||||
-rw-r--r-- | src/tutors/abstractions/tutor_repo.rs | 5 | ||||
-rw-r--r-- | src/tutors/fs_tutor.rs | 48 | ||||
-rw-r--r-- | src/tutors/fs_tutor_repo.rs | 29 | ||||
-rw-r--r-- | src/views.rs | 5 | ||||
-rw-r--r-- | src/views/about.rs | 17 | ||||
-rw-r--r-- | src/views/brochure.rs | 6 | ||||
-rw-r--r-- | src/views/index.rs | 6 | ||||
-rw-r--r-- | src/views/policies.rs | 6 | ||||
-rw-r--r-- | src/views/posts.rs | 17 |
21 files changed, 267 insertions, 199 deletions
diff --git a/src/handlers.rs b/src/handlers.rs new file mode 100644 index 0000000..db5cf7c --- /dev/null +++ b/src/handlers.rs @@ -0,0 +1,34 @@ +use askama::Template; +use crate::views::posts::PostsView; +use crate::posts::abstractions::repo::PostRepo; +use crate::views::policies::PoliciesTemplate; +use crate::views::index::IndexTemplate; +use crate::views::brochure::BrochureTemplate; +use crate::views::about::AboutView; +use crate::tutors::abstractions::tutor_repo::TutorRepo; +use std::sync::Arc; +use axum::response::Html; +use axum::extract::State; + +pub async fn about_handler(State(repo): State<Arc<impl TutorRepo>>) -> Html<String> { + let view = AboutView::with_tutors(repo.load()); + Html(view.render().unwrap()) +} + +pub async fn brochure_handler() -> Html<String> { + Html(BrochureTemplate{}.render().unwrap()) +} + +pub async fn index_handler() -> Html<String> { + Html(IndexTemplate {}.render().unwrap()) +} + +pub async fn policies_handler() -> Html<String> { + Html(PoliciesTemplate{}.render().unwrap()) +} + +pub async fn posts_handler(State(repo): State<Arc<impl PostRepo>>) -> Html<String> { + let view = PostsView::with_posts(repo.load()); + Html(view.render().unwrap()) +} + diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 0000000..c7509de --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,5 @@ +use chrono::{prelude::*, Utc}; + +pub fn current_year() -> i32 { + Utc::now().year() +} diff --git a/src/main.rs b/src/main.rs index 7b5ddfc..fb15e51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,199 +1,14 @@ -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::fmt; -use std::fs; -use std::path::PathBuf; -use chrono::Datelike; - -fn current_year() -> i32 { - chrono::Utc::now().year() -} - -trait Post: fmt::Debug { - fn get_title(&self) -> &str; - fn get_article(&self) -> Cow<str>; -} - -#[derive(Debug)] -struct FsPost { - file: PathBuf, -} - -impl Post for FsPost { - fn get_title(&self) -> &str { - self.file.file_name().unwrap().to_str().unwrap() - } - - fn get_article(&self) -> Cow<str> { - let article = fs::read_to_string(&self.file).unwrap(); - Cow::Owned(article) - } -} - -trait PostRepo { - fn load(&self) -> impl IntoIterator<Item = impl Post>; -} - -struct FsPostRepo { - dir: PathBuf, -} - -impl FsPostRepo { - fn with_dir(path: impl Into<PathBuf>) -> Self { - Self { - dir: path.into() - } - } -} - -impl PostRepo for FsPostRepo { - fn load(&self) -> impl IntoIterator<Item = FsPost> { - 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() }) - } -} - - -#[derive(Template)] -#[template(path = "posts.html")] -struct PostsView<P: Post> { - posts: Vec<P> -} - -impl<P: Post> PostsView<P> { - fn with_posts(posts: impl IntoIterator<Item = P>) -> Self { - Self { - posts: posts.into_iter().collect() - } - } -} - -async fn posts_handler(State(repo): State<Arc<impl PostRepo>>) -> Html<String> { - let view = PostsView::with_posts(repo.load()); - Html(view.render().unwrap()) -} - -trait Tutor: fmt::Debug + Ord { - fn get_name(&self) -> &str; - fn get_id(&self) -> &str; - fn get_blurb(&self) -> Cow<str>; -} - -#[derive(Debug, Eq)] -struct FsTutor { - dir: PathBuf -} - -impl Tutor for FsTutor { - fn get_id(&self) -> &str { - self.dir.file_name().unwrap().to_str().unwrap() - } - - fn get_name(&self) -> &str { - self.get_id() - } - - fn get_blurb(&self) -> Cow<str> { - 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 Ord for FsTutor { - fn cmp(&self, other: &Self) -> Ordering { - self.get_id().cmp(other.get_id()) - } -} - -impl PartialOrd for FsTutor { - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -impl PartialEq for FsTutor { - fn eq(&self, other: &Self) -> bool { - self.get_id() == other.get_id() - } -} - -trait TutorRepo { - fn load(&self) -> impl IntoIterator<Item = impl Tutor>; -} - -struct FsTutorRepo { - dir: PathBuf -} - -impl FsTutorRepo { - fn with_dir(path: impl Into<PathBuf>) -> Self { - Self { - dir: path.into() - } - } -} - - -impl TutorRepo for FsTutorRepo { - fn load(&self) -> impl IntoIterator<Item = FsTutor> { - 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<T: Tutor> { - tutors: Vec<T>, -} -impl<T: Tutor + Ord> AboutView<T> { - fn with_tutors(tutors: impl IntoIterator<Item = T>) -> Self { - let mut tutors: Vec<T> = tutors.into_iter().collect(); - tutors.sort(); - Self { tutors } - } -} - -async fn about_handler(State(repo): State<Arc<impl TutorRepo>>) -> Html<String> { - let view = AboutView::with_tutors(repo.load()); - Html(view.render().unwrap()) -} - -#[derive(Template)] -#[template(path = "index.html")] -struct IndexTemplate; - -async fn index_handler() -> Html<String> { - Html(IndexTemplate {}.render().unwrap()) -} - -#[derive(Template)] -#[template(path = "policies.html")] -struct PoliciesTemplate; - -async fn policies_handler() -> Html<String> { - Html(PoliciesTemplate{}.render().unwrap()) -} - -#[derive(Template)] -#[template(path = "brochure/index.html")] -struct BrochureTemplate; +use tutors::fs_tutor_repo::FsTutorRepo; +use std::sync::Arc; +use tower_http::services::ServeDir; +use posts::fs_post_repo::FsPostRepo; -async fn brochure_handler() -> Html<String> { - Html(BrochureTemplate{}.render().unwrap()) -} +mod helpers; +mod posts; +mod tutors; +mod views; +mod handlers; #[tokio::main] async fn main() { @@ -201,12 +16,12 @@ async fn main() { let tutors = Arc::new(FsTutorRepo::with_dir(format!("/data/ct/{}", "team"))); let app = Router::new() - .route("/", get(index_handler)) - .route("/posts", get(posts_handler)) + .route("/", get(handlers::index_handler)) + .route("/posts", get(handlers::posts_handler)) .with_state(posts) - .route("/policies", get(policies_handler)) - .route("/brochure", get(brochure_handler)) - .route("/about", get(about_handler)) + .route("/policies", get(handlers::policies_handler)) + .route("/brochure", get(handlers::brochure_handler)) + .route("/about", get(handlers::about_handler)) .with_state(tutors) .nest_service("/assets", ServeDir::new("/data/ct/assets")) .nest_service("/team", ServeDir::new("/data/ct/team")) diff --git a/src/posts.rs b/src/posts.rs new file mode 100644 index 0000000..7f2c217 --- /dev/null +++ b/src/posts.rs @@ -0,0 +1,3 @@ +pub mod abstractions; +pub mod fs_post; +pub mod fs_post_repo; diff --git a/src/posts/abstractions.rs b/src/posts/abstractions.rs new file mode 100644 index 0000000..96c8ced --- /dev/null +++ b/src/posts/abstractions.rs @@ -0,0 +1,2 @@ +pub mod post; +pub mod repo; diff --git a/src/posts/abstractions/post.rs b/src/posts/abstractions/post.rs new file mode 100644 index 0000000..6d504db --- /dev/null +++ b/src/posts/abstractions/post.rs @@ -0,0 +1,6 @@ +use std::{borrow::Cow, fmt}; + +pub trait Post: fmt::Debug { + fn get_title(&self) -> &str; + fn get_article(&self) -> Cow<str>; +} diff --git a/src/posts/abstractions/repo.rs b/src/posts/abstractions/repo.rs new file mode 100644 index 0000000..6fcb385 --- /dev/null +++ b/src/posts/abstractions/repo.rs @@ -0,0 +1,5 @@ +use crate::posts::abstractions::post::Post; + +pub trait PostRepo { + fn load(&self) -> impl IntoIterator<Item = impl Post>; +} diff --git a/src/posts/fs_post.rs b/src/posts/fs_post.rs new file mode 100644 index 0000000..e767803 --- /dev/null +++ b/src/posts/fs_post.rs @@ -0,0 +1,24 @@ +use crate::posts::abstractions::post::Post; +use std::{borrow::Cow, fs, path::PathBuf}; + +#[derive(Debug)] +pub struct FsPost { + file: PathBuf, +} + +impl FsPost { + pub fn with_path(path: PathBuf) -> Self { + Self { file: path } + } +} + +impl Post for FsPost { + fn get_title(&self) -> &str { + self.file.file_name().unwrap().to_str().unwrap() + } + + fn get_article(&self) -> Cow<str> { + let article = fs::read_to_string(&self.file).unwrap(); + Cow::Owned(article) + } +} diff --git a/src/posts/fs_post_repo.rs b/src/posts/fs_post_repo.rs new file mode 100644 index 0000000..eb37a6a --- /dev/null +++ b/src/posts/fs_post_repo.rs @@ -0,0 +1,23 @@ +use crate::posts::abstractions::repo::PostRepo; +use crate::posts::fs_post::FsPost; +use std::{fs, path::PathBuf}; + +pub struct FsPostRepo { + dir: PathBuf, +} + +impl FsPostRepo { + pub fn with_dir(path: impl Into<PathBuf>) -> Self { + Self { dir: path.into() } + } +} + +impl PostRepo for FsPostRepo { + fn load(&self) -> impl IntoIterator<Item = FsPost> { + let files = fs::read_dir(&self.dir).unwrap(); + files + .flatten() + .filter(|d| !d.file_name().to_string_lossy().starts_with('.')) + .map(|d| FsPost::with_path(d.path())) + } +} diff --git a/src/tutors.rs b/src/tutors.rs new file mode 100644 index 0000000..d90ef99 --- /dev/null +++ b/src/tutors.rs @@ -0,0 +1,3 @@ +pub mod abstractions; +pub mod fs_tutor; +pub mod fs_tutor_repo; diff --git a/src/tutors/abstractions.rs b/src/tutors/abstractions.rs new file mode 100644 index 0000000..a93c25d --- /dev/null +++ b/src/tutors/abstractions.rs @@ -0,0 +1,2 @@ +pub mod tutor; +pub mod tutor_repo; diff --git a/src/tutors/abstractions/tutor.rs b/src/tutors/abstractions/tutor.rs new file mode 100644 index 0000000..f36da3d --- /dev/null +++ b/src/tutors/abstractions/tutor.rs @@ -0,0 +1,7 @@ +use std::{borrow::Cow, fmt}; + +pub trait Tutor: fmt::Debug + Ord { + fn get_name(&self) -> &str; + fn get_id(&self) -> &str; + fn get_blurb(&self) -> Cow<str>; +} diff --git a/src/tutors/abstractions/tutor_repo.rs b/src/tutors/abstractions/tutor_repo.rs new file mode 100644 index 0000000..e017806 --- /dev/null +++ b/src/tutors/abstractions/tutor_repo.rs @@ -0,0 +1,5 @@ +use crate::tutors::abstractions::tutor::Tutor; + +pub trait TutorRepo { + fn load(&self) -> impl IntoIterator<Item = impl Tutor>; +} diff --git a/src/tutors/fs_tutor.rs b/src/tutors/fs_tutor.rs new file mode 100644 index 0000000..dc8a635 --- /dev/null +++ b/src/tutors/fs_tutor.rs @@ -0,0 +1,48 @@ +use crate::tutors::abstractions::tutor::Tutor; +use std::{borrow::Cow, cmp::Ordering, fs, path::PathBuf}; + +#[derive(Debug, Eq)] +pub struct FsTutor { + dir: PathBuf, +} + +impl FsTutor { + pub fn with_dir(path: PathBuf) -> Self { + Self { dir: path } + } +} + +impl Tutor for FsTutor { + fn get_id(&self) -> &str { + self.dir.file_name().unwrap().to_str().unwrap() + } + + fn get_name(&self) -> &str { + self.get_id() + } + + fn get_blurb(&self) -> Cow<str> { + 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 Ord for FsTutor { + fn cmp(&self, other: &Self) -> Ordering { + self.get_id().cmp(other.get_id()) + } +} + +impl PartialOrd for FsTutor { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl PartialEq for FsTutor { + fn eq(&self, other: &Self) -> bool { + self.get_id() == other.get_id() + } +} diff --git a/src/tutors/fs_tutor_repo.rs b/src/tutors/fs_tutor_repo.rs new file mode 100644 index 0000000..9b9a8d9 --- /dev/null +++ b/src/tutors/fs_tutor_repo.rs @@ -0,0 +1,29 @@ +use crate::tutors::abstractions::tutor_repo::TutorRepo; +use crate::tutors::fs_tutor::FsTutor; +use std::{fs, path::PathBuf}; + +pub struct FsTutorRepo { + dir: PathBuf, +} + +impl FsTutorRepo { + pub fn with_dir(path: impl Into<PathBuf>) -> Self { + Self { dir: path.into() } + } +} + +impl TutorRepo for FsTutorRepo { + fn load(&self) -> impl IntoIterator<Item = FsTutor> { + 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::with_dir(d.path())) + } +} diff --git a/src/views.rs b/src/views.rs new file mode 100644 index 0000000..b986f56 --- /dev/null +++ b/src/views.rs @@ -0,0 +1,5 @@ +pub mod about; +pub mod brochure; +pub mod index; +pub mod policies; +pub mod posts; diff --git a/src/views/about.rs b/src/views/about.rs new file mode 100644 index 0000000..349c9de --- /dev/null +++ b/src/views/about.rs @@ -0,0 +1,17 @@ +use crate::helpers::*; +use crate::tutors::abstractions::tutor::Tutor; +use askama::Template; + +#[derive(Template)] +#[template(path = "about/index.html")] +pub struct AboutView<T: Tutor> { + tutors: Vec<T>, +} + +impl<T: Tutor> AboutView<T> { + pub fn with_tutors(tutors: impl IntoIterator<Item = T>) -> Self { + let mut tutors: Vec<T> = tutors.into_iter().collect(); + tutors.sort(); + Self { tutors } + } +} diff --git a/src/views/brochure.rs b/src/views/brochure.rs new file mode 100644 index 0000000..0d2f8fc --- /dev/null +++ b/src/views/brochure.rs @@ -0,0 +1,6 @@ +use crate::helpers::*; +use askama::Template; + +#[derive(Template)] +#[template(path = "brochure/index.html")] +pub struct BrochureTemplate; diff --git a/src/views/index.rs b/src/views/index.rs new file mode 100644 index 0000000..3ced24d --- /dev/null +++ b/src/views/index.rs @@ -0,0 +1,6 @@ +use crate::helpers::*; +use askama::Template; + +#[derive(Template)] +#[template(path = "index.html")] +pub struct IndexTemplate; diff --git a/src/views/policies.rs b/src/views/policies.rs new file mode 100644 index 0000000..3d9787d --- /dev/null +++ b/src/views/policies.rs @@ -0,0 +1,6 @@ +use crate::helpers::*; +use askama::Template; + +#[derive(Template)] +#[template(path = "policies.html")] +pub struct PoliciesTemplate; diff --git a/src/views/posts.rs b/src/views/posts.rs new file mode 100644 index 0000000..2ce69f8 --- /dev/null +++ b/src/views/posts.rs @@ -0,0 +1,17 @@ +use crate::helpers::*; +use crate::posts::abstractions::post::Post; +use askama::Template; + +#[derive(Template)] +#[template(path = "posts.html")] +pub struct PostsView<P: Post> { + posts: Vec<P>, +} + +impl<P: Post> PostsView<P> { + pub fn with_posts(posts: impl IntoIterator<Item = P>) -> Self { + Self { + posts: posts.into_iter().collect(), + } + } +} |