diff options
Diffstat (limited to 'kittybox-rs/templates/src/lib.rs')
-rw-r--r-- | kittybox-rs/templates/src/lib.rs | 367 |
1 files changed, 0 insertions, 367 deletions
diff --git a/kittybox-rs/templates/src/lib.rs b/kittybox-rs/templates/src/lib.rs deleted file mode 100644 index 8d5d5fa..0000000 --- a/kittybox-rs/templates/src/lib.rs +++ /dev/null @@ -1,367 +0,0 @@ -mod templates; -pub use templates::{ErrorPage, MainPage, Template}; -mod onboarding; -pub use onboarding::OnboardingPage; -mod indieauth; -pub use indieauth::AuthorizationRequestPage; -mod login; -pub use login::LoginPage; -mod mf2; -pub use mf2::{Entry, VCard, Feed, Food, POSTS_PER_PAGE}; - -pub mod assets { - use axum::response::{IntoResponse, Response}; - use axum::extract::Path; - use axum::http::StatusCode; - use axum::http::header::{CONTENT_TYPE, CONTENT_ENCODING, CACHE_CONTROL}; - - const ASSETS: include_dir::Dir<'static> = include_dir::include_dir!("$OUT_DIR/"); - const CACHE_FOR_A_DAY: &str = "max-age=86400"; - const GZIP: &str = "gzip"; - - pub async fn statics( - Path(path): Path<String> - ) -> Response { - let content_type: &'static str = if path.ends_with(".js") { - "application/javascript" - } else if path.ends_with(".css") { - "text/css" - } else if path.ends_with(".html") { - "text/html; charset=\"utf-8\"" - } else { - "application/octet-stream" - }; - - match ASSETS.get_file(path.clone() + ".gz") { - Some(file) => (StatusCode::OK, - [(CONTENT_TYPE, content_type), - (CONTENT_ENCODING, GZIP), - (CACHE_CONTROL, CACHE_FOR_A_DAY)], - file.contents()).into_response(), - None => match ASSETS.get_file(path) { - Some(file) => (StatusCode::OK, - [(CONTENT_TYPE, content_type), - (CACHE_CONTROL, CACHE_FOR_A_DAY)], - file.contents()).into_response(), - None => StatusCode::NOT_FOUND.into_response() - } - } - } -} - -#[cfg(test)] -mod tests { - use faker_rand::en_us::internet::Domain; - use faker_rand::lorem::Word; - use microformats::types::{Document, Item, PropertyValue, Url}; - use serde_json::json; - use std::cell::RefCell; - use std::rc::Rc; - - enum PostType { - Note, - Article, - ReplyTo(serde_json::Value), - ReplyToLink(String), - LikeOf(serde_json::Value), - LikeOfLink(String), - } - - fn gen_hcard(domain: &str) -> serde_json::Value { - use faker_rand::en_us::names::FirstName; - - json!({ - "type": ["h-card"], - "properties": { - "name": [rand::random::<FirstName>().to_string()], - "photo": [format!("https://{domain}/media/me.png")], - "uid": [format!("https://{domain}/")], - "url": [format!("https://{domain}/")] - } - }) - } - - fn gen_random_post(domain: &str, kind: PostType) -> serde_json::Value { - use faker_rand::lorem::{Paragraph, Sentence}; - - fn html(content: Paragraph) -> serde_json::Value { - json!({ - "html": format!("<p>{}</p>", content), - "value": content.to_string() - }) - } - - let uid = format!( - "https://{domain}/posts/{}-{}-{}", - rand::random::<Word>(), - rand::random::<Word>(), - rand::random::<Word>() - ); - let dt = chrono::offset::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true); - - match kind { - PostType::Note => { - let content = rand::random::<Paragraph>(); - - json!({ - "type": ["h-entry"], - "properties": { - "content": [html(content)], - "published": [dt], - "uid": [&uid], "url": [&uid], - "author": [gen_hcard(domain)] - } - }) - } - PostType::Article => { - let content = rand::random::<Paragraph>(); - let name = rand::random::<Sentence>(); - - json!({ - "type": ["h-entry"], - "properties": { - "content": [html(content)], - "published": [dt], - "uid": [&uid], "url": [&uid], - "author": [gen_hcard(domain)], - "name": [name.to_string()] - } - }) - } - PostType::ReplyTo(ctx) => { - let content = rand::random::<Paragraph>(); - - json!({ - "type": ["h-entry"], - "properties": { - "content": [html(content)], - "published": [dt], - "uid": [&uid], "url": [&uid], - "author": [gen_hcard(domain)], - "in-reply-to": [{ - "type": ["h-cite"], - "properties": ctx["properties"] - }] - } - }) - } - PostType::ReplyToLink(link) => { - let content = rand::random::<Paragraph>(); - - json!({ - "type": ["h-entry"], - "properties": { - "content": [html(content)], - "published": [dt], - "uid": [&uid], "url": [&uid], - "author": [gen_hcard(domain)], - "in-reply-to": [link] - } - }) - } - PostType::LikeOf(ctx) => { - json!({ - "type": ["h-entry"], - "properties": { - "published": [dt], - "author": [gen_hcard(domain)], - "uid": [&uid], "url": [&uid], - "like-of": [{ - "type": ["h-cite"], - "properties": ctx["properties"] - }] - } - }) - } - PostType::LikeOfLink(link) => { - json!({ - "type": ["h-entry"], - "properties": { - "published": [dt], - "author": [gen_hcard(domain)], - "uid": [&uid], "url": [&uid], - "like-of": [link] - } - }) - } - } - } - - fn check_dt_published(mf2: &serde_json::Value, item: &Rc<RefCell<Item>>) { - use microformats::types::temporal::Value as TemporalValue; - - let _item = item.borrow(); - let props = _item.properties.borrow(); - assert!(props.contains_key("published")); - - if let Some(PropertyValue::Temporal(TemporalValue::Timestamp(item))) = - props.get("published").and_then(|v| v.first()) - { - use chrono::{DateTime, FixedOffset, NaiveDateTime}; - - // Faithfully reconstruct the original datetime - // I wonder why not just have an Enum that would - // get you either date, time or a datetime, - // potentially with an offset? - let offset = item.as_offset().unwrap().data; - let ndt: NaiveDateTime = item.as_date().unwrap().data - .and_time(item.as_time().unwrap().data) - // subtract the offset here, since we will add it back - - offset; - let dt = DateTime::<FixedOffset>::from_utc(ndt, offset); - - let expected: DateTime<FixedOffset> = chrono::DateTime::parse_from_rfc3339( - mf2["properties"]["published"][0].as_str().unwrap(), - ) - .unwrap(); - - assert_eq!(dt, expected); - } else { - unreachable!() - } - } - - fn check_e_content(mf2: &serde_json::Value, item: &Rc<RefCell<Item>>) { - let _item = item.borrow(); - let props = _item.properties.borrow(); - assert!(props.contains_key("content")); - - if let Some(PropertyValue::Fragment(content)) = props.get("content").and_then(|v| v.first()) - { - assert_eq!( - content.html, - mf2["properties"]["content"][0]["html"].as_str().unwrap() - ); - } else { - unreachable!() - } - } - - #[test] - #[ignore = "see https://gitlab.com/maxburon/microformats-parser/-/issues/7"] - fn test_note() { - let mf2 = gen_random_post(&rand::random::<Domain>().to_string(), PostType::Note); - - let html = crate::mf2::Entry { post: &mf2 }.to_string(); - - let url: Url = mf2 - .pointer("/properties/uid/0") - .and_then(|i| i.as_str()) - .and_then(|u| u.parse().ok()) - .unwrap(); - let parsed: Document = microformats::from_html(&html, url.clone()).unwrap(); - - if let Some(PropertyValue::Item(item)) = parsed.get_item_by_url(&url) { - let _item = item.borrow(); - let props = _item.properties.borrow(); - - check_e_content(&mf2, &item); - check_dt_published(&mf2, &item); - assert!(props.contains_key("uid")); - assert!(props.contains_key("url")); - assert!(props - .get("url") - .unwrap() - .iter() - .any(|i| i == props.get("uid").and_then(|v| v.first()).unwrap())); - // XXX: fails because of https://gitlab.com/maxburon/microformats-parser/-/issues/7 - assert!(!props.contains_key("name")); - } else { - unreachable!() - } - } - - #[test] - fn test_article() { - let mf2 = gen_random_post(&rand::random::<Domain>().to_string(), PostType::Article); - let html = crate::mf2::Entry { post: &mf2 }.to_string(); - let url: Url = mf2 - .pointer("/properties/uid/0") - .and_then(|i| i.as_str()) - .and_then(|u| u.parse().ok()) - .unwrap(); - let parsed: Document = microformats::from_html(&html, url.clone()).unwrap(); - - if let Some(PropertyValue::Item(item)) = parsed.get_item_by_url(&url) { - let _item = item.borrow(); - let props = _item.properties.borrow(); - - check_e_content(&mf2, &item); - check_dt_published(&mf2, &item); - assert!(props.contains_key("uid")); - assert!(props.contains_key("url")); - assert!(props - .get("url") - .unwrap() - .iter() - .any(|i| i == props.get("uid").and_then(|v| v.first()).unwrap())); - assert!(props.contains_key("name")); - if let Some(PropertyValue::Plain(name)) = props.get("name").and_then(|v| v.first()) { - assert_eq!( - name, - mf2.pointer("/properties/name/0") - .and_then(|v| v.as_str()) - .unwrap() - ); - } else { - panic!("Name wasn't a plain property!"); - } - } else { - unreachable!() - } - } - - #[test] - fn test_like_of() { - for likeof in [ - PostType::LikeOf(gen_random_post( - &rand::random::<Domain>().to_string(), - PostType::Note, - )), - PostType::LikeOfLink(format!( - "https://{}/posts/{}-{}-{}", - &rand::random::<Domain>(), - &rand::random::<Word>(), - &rand::random::<Word>(), - &rand::random::<Word>(), - )), - ] { - let mf2 = gen_random_post(&rand::random::<Domain>().to_string(), likeof); - let url: Url = mf2 - .pointer("/properties/uid/0") - .and_then(|i| i.as_str()) - .and_then(|u| u.parse().ok()) - .unwrap(); - let html = crate::mf2::Entry { post: &mf2 }.to_string(); - let parsed: Document = microformats::from_html(&html, url.clone()).unwrap(); - - if let Some(item) = parsed.items.get(0) { - let _item = item.borrow(); - let props = _item.properties.borrow(); - - check_dt_published(&mf2, item); - assert!(props.contains_key("like-of")); - match props.get("like-of").and_then(|v| v.first()) { - Some(PropertyValue::Url(url)) => { - assert_eq!( - url, - &mf2.pointer("/properties/like-of/0") - .and_then(|i| i.as_str()) - .or_else(|| mf2 - .pointer("/properties/like-of/0/properties/uid/0") - .and_then(|i| i.as_str())) - .and_then(|u| u.parse::<Url>().ok()) - .unwrap() - ); - } - Some(PropertyValue::Item(_cite)) => { - todo!() - } - other => panic!("Unexpected value in like-of: {:?}", other), - } - } else { - unreachable!() - } - } - } -} |