From 7e8b253a271b76c1fbda10ca3e9c8f45008f8f55 Mon Sep 17 00:00:00 2001 From: "Jakob L. Kreuze" Date: Thu, 28 May 2020 19:02:00 -0400 Subject: Refactor. --- src/database.rs | 1 + src/main.rs | 148 ++++++++++++++++++++++++++++------------------------ templates/index.hbs | 2 +- 3 files changed, 81 insertions(+), 70 deletions(-) diff --git a/src/database.rs b/src/database.rs index 61a51cb..055d1ef 100644 --- a/src/database.rs +++ b/src/database.rs @@ -252,6 +252,7 @@ impl TagDatabase { }) } + /// Return the `n` greatest tags, ordered by number of associated images. pub fn top_tags(&self, n: i64) -> Result> { Ok(self .0 diff --git a/src/main.rs b/src/main.rs index e2290b9..d53d0d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,8 +42,13 @@ use std::os::unix::fs::symlink; use std::path::Path; use std::sync::Mutex; +/// Maximum number of results returned by any API endpoint. +const RESULTS_PER_QUERY: i64 = 50; + +/// Shorthand for the state that is passed to all handlers. type SiteState<'a> = State<'a, Mutex>; +/// Collection of data necessary to serve images from the tag database. struct DatabaseConnection { tb: TagDatabase, image_dir: String, @@ -60,52 +65,7 @@ impl DatabaseConnection { } } -#[get("/")] -fn index(conn: SiteState) -> Template { - display_posts(conn, String::from(""), None) -} - -#[get("/posts?&")] -fn display_posts(conn: SiteState, tags: String, last: Option) -> Template { - #[derive(Serialize)] - struct TagResult { - name: String, - count: i64, - } - - #[derive(Serialize)] - struct Context { - tags: Vec, - images: Vec, - next_page: String, - } - - let tagstr = &tags.clone(); - let tag_results = { - let tb = &conn.inner().lock().unwrap().tb; - tb.top_tags(20) - .unwrap() - .iter() - .map(|tup| TagResult { - name: tup.1.clone(), - count: tup.0, - }) - .collect() - }; - let images = get_posts(conn, tags, last).into_inner(); - let context = Context { - tags: tag_results, - // FIXME: Magic number for results per request. - next_page: if images.len() == 50 { - format!("/posts?tags={}&last={}", tagstr, images[49].id) - } else { - String::from("") - }, - images, - }; - Template::render("index", context) -} - +/// Information about an image, as returned by the 'posts' endpoint. #[derive(Serialize)] struct ImageResult { id: i64, @@ -114,11 +74,26 @@ struct ImageResult { tags: Vec, } +/// Return the file name of the thumbnail for the image at `filename`. +fn thumb_filename(filename: &str) -> String { + String::from(format!( + "{}_thumb.png", + // Strip extension. + if let Some(n) = filename.rfind('.') { + &filename[..n] + } else { + &filename[..] + } + )) +} + #[get("/posts?&")] fn get_posts(conn: SiteState, tags: String, last: Option) -> Json> { let tb = &conn.inner().lock().unwrap().tb; - let tags = if tags != "" { + // Ensure that an empty vector is passed to `tb.query` if no tags were + // specified. + let tags = if !tags.is_empty() { tags.split(",").collect::>() } else { vec![] @@ -127,7 +102,7 @@ fn get_posts(conn: SiteState, tags: String, last: Option) -> Json Json<&str> { Json("uploaded!") } -fn thumb_filename(filename: &str) -> String { - String::from(format!( - "{}_thumb.png", - // Strip extension. - if let Some(n) = filename.rfind('.') { - &filename[..n] +#[get("/")] +fn index(conn: SiteState) -> Template { + display_posts(conn, String::from(""), None) +} + +#[get("/posts?&")] +fn display_posts(conn: SiteState, tags: String, last: Option) -> Template { + #[derive(Serialize)] + struct TagResult { + name: String, + count: i64, + } + + #[derive(Serialize)] + struct Context { + tags: Vec, + images: Vec, + next_page: String, + } + + let base_params = format!("/posts?tags={}", tags); + let tag_results = { + // This is done in a block because `tb` needs to go out of scope before + // calling out to `get_posts` to perform the query. Otherwise, we + // deadlock because the mutex is locked for the entirety of + // `display_posts`. + let tb = &conn.inner().lock().unwrap().tb; + tb.top_tags(20) + .unwrap() + .iter() + .map(|tup| TagResult { + name: tup.1.clone(), + count: tup.0, + }) + .collect() + }; + let images = get_posts(conn, tags, last).into_inner(); + let context = Context { + tags: tag_results, + next_page: if images.len() as i64 == RESULTS_PER_QUERY { + let last_image = images.last().unwrap(); + format!("{}&last={}", base_params, last_image.id) } else { - &filename[..] - } - )) + String::from("") + }, + images, + }; + Template::render("index", context) } /// Maybe initialize the directory for storing image symlinks and thumbnails. @@ -173,19 +186,17 @@ fn create_image_directory(conn: &DatabaseConnection) { fs::create_dir(&conn.image_dir).ok(); for image in conn.tb.images_by_tags(&vec![][..]).unwrap() { - let src = format!("{}/{}", image.orig_dir, image.filename); - let dst = format!("{}/{}", conn.image_dir, image.filename); + let src = Path::new(&image.orig_dir).join(&image.filename); + let dst = Path::new(&conn.image_dir).join(&image.filename); symlink(&src, &dst).ok(); - let stem = Path::new(&dst).file_stem().unwrap().to_str().unwrap(); - let thumb_path = format!("{}/{}", conn.image_dir, thumb_filename(&stem)); - let thumb_path = Path::new(&thumb_path); - + let stem = dst.file_stem().unwrap().to_str().unwrap(); + let thumb_path = Path::new(&conn.image_dir).join(&thumb_filename(&stem)); if !thumb_path.exists() { let im = image::open(&Path::new(&src)).unwrap(); - let fout = &mut File::create(&thumb_path).unwrap(); + let out = &mut File::create(&thumb_path).unwrap(); im.thumbnail(100, 100) - .write_to(fout, ImageFormat::Png) + .write_to(out, ImageFormat::Png) .unwrap(); } } @@ -193,14 +204,13 @@ fn create_image_directory(conn: &DatabaseConnection) { fn main() { let conn = DatabaseConnection::new().unwrap(); - let image_dir = conn.image_dir.clone(); create_image_directory(&conn); rocket::ignite() - .attach(Template::fairing()) - .manage(Mutex::new(conn)) .mount("/public", StaticFiles::from("./static/")) - .mount("/image", StaticFiles::from(image_dir)) + .mount("/image", StaticFiles::from(&conn.image_dir)) .mount("/", routes![index, display_posts]) .mount("/api", routes![get_posts, put_posts]) + .attach(Template::fairing()) + .manage(Mutex::new(conn)) .launch(); } diff --git a/templates/index.hbs b/templates/index.hbs index 18245a0..c3311b9 100644 --- a/templates/index.hbs +++ b/templates/index.hbs @@ -1,7 +1,7 @@ - Hello, world! + бирка-тян -- cgit v1.3