diff --git a/Cargo.lock b/Cargo.lock index 3d863c7..91f8e0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1062,6 +1062,17 @@ version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +[[package]] +name = "serde_derive" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10be45e22e5597d4b88afcc71f9d7bfadcd604bf0c78a3ab4582b8d2b37f39f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_json" version = "1.0.52" @@ -1188,6 +1199,8 @@ dependencies = [ "pulldown-cmark", "rand 0.6.5", "regex", + "serde", + "serde_derive", "structopt", "tokio", "walkdir", diff --git a/Cargo.toml b/Cargo.toml index 97a4c7a..d9a4a84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,5 @@ digest = "*" hex = "*" walkdir = "*" async-trait = "0.1.30" +serde = "*" +serde_derive = "*" diff --git a/src/index.rs b/src/index.rs index 4f10c88..4524948 100644 --- a/src/index.rs +++ b/src/index.rs @@ -59,9 +59,14 @@ impl Index { pub fn find(&self, tags: &[TagRef]) -> FindResults { let mut matching_pages: Vec = vec![]; let mut matching_tags: Vec = vec![]; + let mut already_tried_tags = HashSet::new(); for tag in tags { + if already_tried_tags.contains(tag) { + continue; + } + already_tried_tags.insert(tag); if matching_tags.is_empty() { - if let Some(ids) = dbg!(&self.page_ids_by_tag).get(*tag) { + if let Some(ids) = &self.page_ids_by_tag.get(*tag) { matching_pages = ids.iter().map(|id| id.to_owned()).collect(); matching_tags.push(tag.to_string()) } else { @@ -98,7 +103,7 @@ impl Index { } fn add_data_for_page(&mut self, page: &page::Parsed) { - for tag in dbg!(&page.tags) { + for tag in &page.tags { self.page_ids_by_tag .entry(tag.clone()) .or_default() diff --git a/src/main.rs b/src/main.rs index 99f8baf..03709d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ use std::sync::Arc; use structopt::StructOpt; use warp::{path::FullPath, Filter}; +use serde_derive::Deserialize; + use page::StoreMut; /// Command line options @@ -34,24 +36,107 @@ fn with_state( warp::any().map(move || state.clone()) } -async fn handler( - state: Arc, - path: FullPath, -) -> std::result::Result, warp::Rejection> { - let tags: Vec<_> = path - .as_str() +fn warp_temporary_redirect(location: &str) -> warp::http::Response<&'static str> { + warp::http::Response::builder() + .status(307) + .header(warp::http::header::LOCATION, location) + .body("") + .expect("correct redirect") +} + +fn warp_temporary_redirect_after_post(location: &str) -> warp::http::Response<&'static str> { + warp::http::Response::builder() + .status(303) + .header(warp::http::header::LOCATION, location) + .body("") + .expect("correct redirect") +} + +fn get_rid_of_windows_newlines(s: String) -> String { + s.chars().filter(|ch| *ch != '\r').collect() +} + +#[derive(Deserialize, Debug)] +struct GetPrompt { + edit: Option, +} + +#[derive(Deserialize, Debug)] +struct PostForm { + body: String, +} + +fn html_for_editing_page(page: &page::Parsed) -> String { + format!( + "

", + page.source_body + ) +} + +fn path_to_tags(path: &FullPath) -> Vec<&str> { + path.as_str() .split('/') .map(|t| t.trim()) .filter(|t| t != &"") - .collect(); + .collect() +} + +async fn handle_post( + state: Arc, + path: FullPath, + form: PostForm, +) -> std::result::Result, warp::Rejection> { + let tags = path_to_tags(&path); + let mut write = state.page_store.write().await; + let results = write.find(tags.as_slice()); + + match results.matching_pages.len() { + 1 => { + let page = write + .get(results.matching_pages[0].clone()) + .await + .map_err(|e| warp::reject::custom(RejectAnyhow(e)))?; + + let page = page.with_new_source_body(&get_rid_of_windows_newlines(form.body)); + + write + .put(&page) + .await + .map_err(|e| warp::reject::custom(RejectAnyhow(e)))?; + + Ok(Box::new(warp_temporary_redirect_after_post(".".into()))) + } + _ => { + // TODO: ERROR + Ok(Box::new(format!("Results: {:?}", results))) + } + } +} + +async fn handle_get( + state: Arc, + path: FullPath, + query: GetPrompt, +) -> std::result::Result, warp::Rejection> { + let tags = path_to_tags(&path); let read = state.page_store.read().await; let results = read.find(tags.as_slice()); + if results.matching_tags != tags { + return Ok(Box::new(warp_temporary_redirect( + &("/".to_string() + &results.matching_tags.join("/")), + ))); + } if results.matching_pages.len() == 1 { let page = read .get(results.matching_pages[0].clone()) .await .map_err(|e| warp::reject::custom(RejectAnyhow(e)))?; - Ok(Box::new(warp::reply::html(page.html))) + Ok(Box::new(warp::reply::html(if query.edit.is_none() { + page.html + + "