From 6f47cd5b5ed220f3fcfe6566166f774ccd26d0c3 Mon Sep 17 00:00:00 2001 From: Vika Date: Wed, 9 Apr 2025 23:00:11 +0300 Subject: templates-neo: remove The `html` crate is not currently maintained, and it's not exactly production-ready anyway. This was a fine experiment, but I don't think it currently belongs in-tree. Change-Id: I38019e5fb3ee549ec0b883dd7bd14f32dc746bdb --- templates-neo/src/lib.rs | 2 - templates-neo/src/main.rs | 18 -- templates-neo/src/mf2.rs | 467 ---------------------------------------------- 3 files changed, 487 deletions(-) delete mode 100644 templates-neo/src/lib.rs delete mode 100644 templates-neo/src/main.rs delete mode 100644 templates-neo/src/mf2.rs (limited to 'templates-neo/src') diff --git a/templates-neo/src/lib.rs b/templates-neo/src/lib.rs deleted file mode 100644 index 1ae9e03..0000000 --- a/templates-neo/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -#![recursion_limit = "512"] -pub mod mf2; diff --git a/templates-neo/src/main.rs b/templates-neo/src/main.rs deleted file mode 100644 index d374e3f..0000000 --- a/templates-neo/src/main.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![recursion_limit = "512"] -use std::io::Write; - -use kittybox_html::mf2::Entry; - -fn main() { - let mf2 = serde_json::from_reader::<_, microformats::types::Item>(std::io::stdin()).unwrap(); - let entry = Entry::try_from(mf2).unwrap(); - - let mut article = html::content::Article::builder(); - entry.build(&mut article); - - let mut stdout = std::io::stdout().lock(); - stdout - .write_all(article.build().to_string().as_bytes()) - .unwrap(); - stdout.write_all(b"\n").unwrap(); -} diff --git a/templates-neo/src/mf2.rs b/templates-neo/src/mf2.rs deleted file mode 100644 index 3cf453f..0000000 --- a/templates-neo/src/mf2.rs +++ /dev/null @@ -1,467 +0,0 @@ -use std::{borrow::Cow, collections::HashMap}; - -use html::{ - content::builders::{ArticleBuilder, SectionBuilder}, - inline_text::Anchor, - media::builders, -}; -use microformats::types::{ - temporal::Value as Temporal, Class, Fragment, Item, KnownClass, PropertyValue, -}; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("wrong mf2 class, expected {expected:?}, got {got:?}")] - WrongClass { - expected: Vec, - got: Vec, - }, - #[error("entry lacks `uid` property")] - NoUid, - #[error("unexpected type of property value: expected {expected}, got {got:?}")] - WrongValueType { - expected: &'static str, - got: PropertyValue, - }, - #[error("missing property: {0}")] - MissingProperty(&'static str), -} - -pub enum Image { - Plain(url::Url), - Accessible { src: url::Url, alt: String }, -} - -impl Image { - pub fn build( - self, - img: &mut html::media::builders::ImageBuilder, - ) -> &mut html::media::builders::ImageBuilder { - match self { - Image::Plain(url) => img.src(String::from(url)), - Image::Accessible { src, alt } => img.src(String::from(src)).alt(alt), - } - } -} - -pub struct Card { - uid: url::Url, - urls: Vec, - name: String, - note: Option, - photo: Image, - pronouns: Vec, -} - -impl TryFrom for Card { - type Error = Error; - - fn try_from(card: Item) -> Result { - if card.r#type.as_slice() != [Class::Known(KnownClass::Card)] { - return Err(Error::WrongClass { - expected: vec![KnownClass::Card], - got: card.r#type, - }); - } - - let mut props = card.properties; - let uid = { - let uids = props.remove("uid").ok_or(Error::NoUid)?; - if let Some(PropertyValue::Url(uid)) = uids.into_iter().take(1).next() { - uid - } else { - return Err(Error::NoUid); - } - }; - - Ok(Self { - uid, - urls: props - .remove("url") - .unwrap_or_default() - .into_iter() - .filter_map(|v| { - if let PropertyValue::Url(url) = v { - Some(url) - } else { - None - } - }) - .collect(), - name: props - .remove("name") - .unwrap_or_default() - .into_iter() - .next() - .ok_or(Error::MissingProperty("name")) - .and_then(|v| match v { - PropertyValue::Plain(plain) => Ok(plain), - other => Err(Error::WrongValueType { - expected: "string", - got: other, - }), - })?, - note: props - .remove("note") - .unwrap_or_default() - .into_iter() - .next() - .map(|v| match v { - PropertyValue::Plain(plain) => Ok(plain), - other => Err(Error::WrongValueType { - expected: "string", - got: other, - }), - }) - .transpose()?, - photo: props - .remove("photo") - .unwrap_or_default() - .into_iter() - .next() - .ok_or(Error::MissingProperty("photo")) - .and_then(|v| match v { - PropertyValue::Url(url) => Ok(Image::Plain(url)), - PropertyValue::Image(image) => match image.alt { - Some(alt) => Ok(Image::Accessible { - src: image.value, - alt, - }), - None => Ok(Image::Plain(image.value)) - }, - other => Err(Error::WrongValueType { - expected: "string", - got: other, - }), - })?, - pronouns: props - .remove("pronoun") - .unwrap_or_default() - .into_iter() - .map(|v| match v { - PropertyValue::Plain(plain) => Ok(plain), - other => Err(Error::WrongValueType { - expected: "string", - got: other, - }), - }) - .collect::, _>>()?, - }) - } -} - -impl Card { - pub fn build_section( - self, - section: &mut html::content::builders::SectionBuilder, - ) -> &mut html::content::builders::SectionBuilder { - section.class("mini-h-card").anchor(|a| { - a.class("larger u-author") - .href(String::from(self.uid)) - .image(move |img| self.photo.build(img).loading("lazy")) - .text(self.name) - }) - } - - pub fn build( - self, - article: &mut html::content::builders::ArticleBuilder, - ) -> &mut html::content::builders::ArticleBuilder { - let urls: Vec<_> = self.urls.into_iter().filter(|u| *u != self.uid).collect(); - - article - .class("h-card") - .image(move |builder| self.photo.build(builder)) - .heading_1(move |builder| { - builder.anchor(|builder| { - builder - .class("u-url u-uid p-name") - .href(String::from(self.uid)) - .text(self.name) - }) - }); - - if !self.pronouns.is_empty() { - article.span(move |span| { - span.text("("); - self.pronouns.into_iter().for_each(|p| { - span.text(p); - }); - span.text(")") - }); - } - - if let Some(note) = self.note { - article.paragraph(move |p| p.class("p-note").text(note)); - } - - if !urls.is_empty() { - article.paragraph(|p| p.text("Can be found elsewhere at:")); - article.unordered_list(move |ul| { - for url in urls { - let url = String::from(url); - ul.list_item(move |li| { - li.push(Anchor::builder().href(url.clone()).text(url).build()) - }); - } - - ul - }); - } - - article - } -} - -impl TryFrom for Card { - type Error = Error; - - fn try_from(v: PropertyValue) -> Result { - match v { - PropertyValue::Item(item) => item.try_into(), - other => Err(Error::WrongValueType { - expected: "h-card", - got: other, - }), - } - } -} - -pub struct Cite { - uid: url::Url, - url: Vec, - in_reply_to: Option>, - author: Card, - published: Option, - content: Content, -} - -impl TryFrom for Cite { - type Error = Error; - - fn try_from(cite: Item) -> Result { - if cite.r#type.as_slice() != [Class::Known(KnownClass::Cite)] { - return Err(Error::WrongClass { - expected: vec![KnownClass::Cite], - got: cite.r#type, - }); - } - - todo!() - } -} - -pub enum Citation { - Brief(url::Url), - Full(Cite), -} - -impl TryFrom for Citation { - type Error = Error; - fn try_from(v: PropertyValue) -> Result { - match v { - PropertyValue::Url(url) => Ok(Self::Brief(url)), - PropertyValue::Item(item) => Ok(Self::Full(item.try_into()?)), - other => Err(Error::WrongValueType { - expected: "url or h-cite", - got: other, - }), - } - } -} - -pub struct Content(Fragment); - -impl From for html::content::Main { - fn from(content: Content) -> Self { - let mut builder = Self::builder(); - builder.class("e-content").text(content.0.html); - if let Some(lang) = content.0.lang { - builder.lang(Cow::Owned(lang)); - } - builder.build() - } -} - -pub struct Entry { - uid: url::Url, - url: Vec, - in_reply_to: Option, - author: Card, - category: Vec, - syndication: Vec, - published: time::OffsetDateTime, - content: Content, -} - -impl TryFrom for Entry { - type Error = Error; - fn try_from(entry: Item) -> Result { - if entry.r#type.as_slice() != [Class::Known(KnownClass::Entry)] { - return Err(Error::WrongClass { - expected: vec![KnownClass::Entry], - got: entry.r#type, - }); - } - - let mut props = entry.properties; - let uid = { - let uids = props.remove("uid").ok_or(Error::NoUid)?; - if let Some(PropertyValue::Url(uid)) = uids.into_iter().take(1).next() { - uid - } else { - return Err(Error::NoUid); - } - }; - Ok(Entry { - uid, - url: props - .remove("url") - .unwrap_or_default() - .into_iter() - .filter_map(|v| { - if let PropertyValue::Url(url) = v { - Some(url) - } else { - None - } - }) - .collect(), - in_reply_to: props - .remove("in-reply-to") - .unwrap_or_default() - .into_iter() - .next() - .map(|v| v.try_into()) - .transpose()?, - author: props - .remove("author") - .unwrap_or_default() - .into_iter() - .next() - .map(|v| v.try_into()) - .transpose()? - .ok_or(Error::MissingProperty("author"))?, - category: props - .remove("category") - .unwrap_or_default() - .into_iter() - .map(|v| match v { - PropertyValue::Plain(string) => Ok(string), - other => Err(Error::WrongValueType { - expected: "string", - got: other, - }), - }) - .collect::, _>>()?, - syndication: props - .remove("syndication") - .unwrap_or_default() - .into_iter() - .map(|v| match v { - PropertyValue::Url(url) => Ok(url), - other => Err(Error::WrongValueType { - expected: "link", - got: other, - }), - }) - .collect::, _>>()?, - published: props - .remove("published") - .unwrap_or_default() - .into_iter() - .next() - .map( - |v| -> Result { - match v { - PropertyValue::Temporal(Temporal::Timestamp(ref dt)) => { - // This is incredibly sketchy. - let (date, time, offset) = ( - dt.date.to_owned().ok_or_else(|| Error::WrongValueType { - expected: "timestamp (date, time, offset)", - got: v.clone() - })?.data, - dt.time.to_owned().ok_or_else(|| Error::WrongValueType { - expected: "timestamp (date, time, offset)", - got: v.clone() - })?.data, - dt.offset.to_owned().ok_or_else(|| Error::WrongValueType { - expected: "timestamp (date, time, offset)", - got: v.clone() - })?.data, - ); - - Ok(date.with_time(time).assume_offset(offset)) - } - other => Err(Error::WrongValueType { - expected: "timestamp", - got: other, - }), - } - }, - ) - .ok_or(Error::MissingProperty("published"))??, - content: props - .remove("content") - .unwrap_or_default() - .into_iter() - .next() - .ok_or(Error::MissingProperty("content")) - .and_then(|v| match v { - PropertyValue::Fragment(fragment) => Ok(Content(fragment)), - other => Err(Error::WrongValueType { - expected: "html", - got: other, - }), - })?, - }) - } -} - -impl Entry { - pub fn build(self, article: &mut ArticleBuilder) -> &mut ArticleBuilder { - article - .class("h-entry") - .header(|header| { - header - .class("metadata") - .section(|section| self.author.build_section(section)) - .section(|section| { - section - .division(|div| { - div.anchor(|a| { - a.class("u-url u-uid").href(String::from(self.uid)).push( - html::inline_text::Time::builder() - .text( - self.published - .format(&time::format_description::well_known::Rfc2822) - .unwrap() - ) - .date_time(self.published.format(&time::format_description::well_known::Rfc3339).unwrap()) - .build(), - ) - }) - }) - .division(|div| { - div.text("Tagged").unordered_list(|ul| { - for category in self.category { - ul.list_item(|li| li.class("p-category").text(category)); - } - - ul - }) - }) - }) - }) - .main(|main| { - if let Some(lang) = self.content.0.lang { - main.lang(lang); - } - - // XXX .text() and .push() are completely equivalent - // since .text() does no escaping - main.push(self.content.0.html) - }) - .footer(|footer| footer) - } -} -- cgit 1.4.1