From 70f13d8277e71191b94443dde0758587e9e56115 Mon Sep 17 00:00:00 2001 From: "Jakob L. Kreuze" Date: Sun, 14 Jun 2020 18:43:48 -0400 Subject: Completely rewrite database.rs --- src/birka-cli.rs | 41 +--- src/birka-web.rs | 164 +++++++------ src/database.rs | 687 ++++++++++++++++++++++++++++++++++--------------------- 3 files changed, 511 insertions(+), 381 deletions(-) (limited to 'src') diff --git a/src/birka-cli.rs b/src/birka-cli.rs index 3fd5420..703da7d 100644 --- a/src/birka-cli.rs +++ b/src/birka-cli.rs @@ -51,20 +51,7 @@ fn main() { std::process::exit(1); } let path = Path::new(&args[2]).canonicalize().unwrap(); - let hash = database::hash_file(&path).unwrap(); - let id = tb.import_image(&path, &hash).unwrap(); - for arg in args[3..].iter() { - tb.add_tag(arg).unwrap(); - tb.tag_image(id, arg).unwrap(); - } - } - "id_for" => { - if args.len() < 3 { - eprintln!("usage: {} id_for PATH ", args[0]); - std::process::exit(1); - } - let path = Path::new(&args[2]).canonicalize().unwrap(); - println!("{}", tb.image_id(&path).unwrap()); + let id = tb.add_file(&path, &args[3..].to_vec()).unwrap(); } "add_tags" => { if args.len() < 3 { @@ -72,10 +59,7 @@ fn main() { std::process::exit(1); } let id = args[2].parse::().unwrap(); - for arg in args[3..].iter() { - tb.add_tag(arg).unwrap(); - tb.tag_image(id, arg).unwrap(); - } + tb.tag_file(id, &args[3..].to_vec()).unwrap(); } "remove_tags" => { if args.len() < 3 { @@ -83,23 +67,16 @@ fn main() { std::process::exit(1); } let id = args[2].parse::().unwrap(); - for arg in args[3..].iter() { - tb.untag_image(id, arg).unwrap(); - } + tb.untag_file(id, &args[3..].to_vec()).unwrap(); } "query" => { - let tags = args[2..].iter().map(|s| s.as_str()).collect::>(); - for tag in tags.iter() { - if tb.tag_id(tag).is_err() { - eprintln!("unknown tag: {}", tag); - std::process::exit(1); - } + if args.len() < 3 { + eprintln!("usage: {} query QUERY_STRING", args[0]); + std::process::exit(1); } - for image in tb.images_by_tags(&tags[..]).unwrap() { - println!( - "{},{},{}{}", - image.id, image.blake2, image.orig_dir, image.filename - ); + let query = database::parse_query(&args[2]).unwrap(); + for file in tb.query(&query, None).unwrap() { + println!("{}", file.path); } } _ => { diff --git a/src/birka-web.rs b/src/birka-web.rs index 359b153..76a194f 100644 --- a/src/birka-web.rs +++ b/src/birka-web.rs @@ -31,7 +31,7 @@ extern crate serde_derive; mod database; use anyhow::Result; -use database::{Image, Query, TagDatabase}; +use database::{Keyset, TagDatabase}; use image::ImageFormat; use multipart::{MultipartFormData, MultipartFormDataField, MultipartFormDataOptions}; use rand::distributions::Alphanumeric; @@ -43,6 +43,7 @@ use rocket_contrib::json::Json; use rocket_contrib::serve::StaticFiles; use rocket_contrib::templates::Template; +use std::convert::AsRef; use std::fs; use std::fs::File; use std::io::Write; @@ -56,10 +57,10 @@ const RESULTS_PER_QUERY: i64 = 50; /// Shorthand for the state that is passed to all handlers. type SiteState<'a> = State<'a, Mutex>; -/// Everything necessary to serve images from the tag database. +/// Everything necessary to serve files from the tag database. struct DatabaseConnection { tb: TagDatabase, - image_dir: String, + file_dir: String, } impl DatabaseConnection { @@ -68,15 +69,15 @@ impl DatabaseConnection { // containing symbolic links and thumbnails be relative to the binary. let bin = std::env::current_exe()?.canonicalize()?; let bin_dir = bin.parent().unwrap(); - let image_dir = bin_dir.join("image/").to_str().unwrap().to_string(); + let file_dir = bin_dir.join("files/").to_str().unwrap().to_string(); let tb = TagDatabase::new(&bin_dir.join("birka.db"))?; - Ok(DatabaseConnection { tb, image_dir }) + Ok(DatabaseConnection { tb, file_dir }) } } -/// Information about an image, as returned by the 'posts' endpoint. +/// Information about a file, as returned by the 'posts' endpoint. #[derive(Debug, Serialize)] -struct ImageResult { +struct FileResult { id: i64, filename: String, thumb_filename: String, @@ -84,71 +85,84 @@ struct ImageResult { tags: Vec, } -/// Return the store file name of the image at `filename`. -fn image_store_filename(id: i64, filename: &str) -> String { - if let Some(n) = filename.rfind('.') { - format!("{id}.{ext}", id = id, ext = &filename[n + 1..]) +/// Return the store file name of `filename`. +fn store_filename>(id: i64, path: T) -> String { + if let Some(filename) = path.as_ref().file_name() { + let filename = filename.to_string_lossy(); + if let Some(n) = filename.rfind('.') { + format!("{id}.{ext}", id = id, ext = &filename[n + 1..]) + } else { + id.to_string() + } } else { id.to_string() } } -/// Return the store file name of the thumbnail for the image at `filename`. -fn thumb_filename(id: i64, filename: &str) -> String { - if let Some(n) = filename.rfind('.') { - format!("{id}_thumb.{ext}", id = id, ext = &filename[n + 1..]) +/// Return the store file name of the thumbnail of `filename`. +fn thumb_filename>(id: i64, path: T) -> String { + if let Some(filename) = path.as_ref().file_name() { + let filename = filename.to_string_lossy(); + if let Some(n) = filename.rfind('.') { + format!("{id}_thumb.{ext}", id = id, ext = &filename[n + 1..]) + } else { + format!("{}_thumb", id) + } } else { - format!("{id}_thumb", id = id) + format!("{}_thumb", id) } } #[get("/posts?&")] -fn get_posts(conn: SiteState, tags: String, last: Option) -> Result>> { +fn get_posts(conn: SiteState, tags: String, last: Option) -> Result>> { let tb = &conn .inner() .lock() .map_err(|_| anyhow!("Could not lock database."))? .tb; - // Ensure that an empty vector is passed to `tb.query` if no tags were - // specified. - let tags = if !tags.is_empty() { - tags.split(",").collect::>() + let keyset = if let Some(id) = last { + Keyset { + last_id: id, + how_many: RESULTS_PER_QUERY, + } } else { - vec![] + Keyset { + last_id: i64::MAX, + how_many: RESULTS_PER_QUERY, + } }; - - let results = tb.query(Query::new(&tags[..], last, Some(RESULTS_PER_QUERY)))?; + let results = tb.query(&database::parse_query(tags)?, Some(keyset))?; Ok(Json( results .iter() - .map(|image| ImageResult { - id: image.id, - filename: image_store_filename(image.id, &image.filename), - thumb_filename: thumb_filename(image.id, &image.filename), - orig_filename: image.filename.clone(), - tags: tb.tags_for_image(image.id).unwrap(), + .map(|file| FileResult { + id: file.id, + filename: store_filename(file.id, &file.path), + thumb_filename: thumb_filename(file.id, &file.path), + orig_filename: file.path.clone(), + tags: file.tags.clone(), }) .collect(), )) } #[get("/posts/")] -fn get_post(conn: SiteState, id: i64) -> Result> { +fn get_post(conn: SiteState, id: i64) -> Result> { let tb = &conn .inner() .lock() .map_err(|_| anyhow!("Could not lock database."))? .tb; - let image = tb.image_by_id(id)?; - - Ok(Json(ImageResult { - id: image.id, - filename: image_store_filename(image.id, &image.filename), - thumb_filename: thumb_filename(image.id, &image.filename), - orig_filename: image.filename.clone(), - tags: tb.tags_for_image(image.id)?, + let file = tb.file_by_id(id)?; + + Ok(Json(FileResult { + id: file.id, + filename: store_filename(file.id, &file.path), + thumb_filename: thumb_filename(file.id, &file.path), + orig_filename: file.path.clone(), + tags: file.tags, })) } @@ -164,12 +178,7 @@ fn update_post(conn: SiteState, id: i64, form: Form) -> Result Result) -> Result, - images: Vec, + images: Vec, next_page: String, } @@ -298,27 +294,26 @@ fn display_post(conn: SiteState, id: i64) -> Result