server: more news title/body cleanup, and don't search news so much

This commit is contained in:
2024-08-12 20:53:48 -07:00
parent c314e3c798
commit d1cfc77148
3 changed files with 218 additions and 98 deletions

View File

@@ -3,14 +3,21 @@ pub mod graphql;
pub mod newsreader;
pub mod nm;
use std::{convert::Infallible, str::FromStr};
use css_inline::{CSSInliner, InlineError, InlineOptions};
use linkify::{LinkFinder, LinkKind};
use log::error;
use lol_html::{element, errors::RewritingError, rewrite_str, RewriteStrSettings};
use log::{error, info};
use lol_html::{element, errors::RewritingError, rewrite_str, text, RewriteStrSettings};
use maplit::{hashmap, hashset};
use thiserror::Error;
use url::Url;
use crate::newsreader::{
extract_thread_id, is_newsreader_search, is_newsreader_thread, make_news_tag,
};
const NON_EXISTENT_SITE_NAME: &'static str = "NO-SUCH-SITE";
// TODO: figure out how to use Cow
trait Transformer {
fn should_run(&self, _html: &str) -> bool {
@@ -46,13 +53,38 @@ struct EscapeHtml;
impl Transformer for EscapeHtml {
fn should_run(&self, html: &str) -> bool {
html.starts_with("&lt")
html.contains("&")
}
fn transform(&self, html: &str) -> Result<String, TransformError> {
Ok(html_escape::decode_html_entities(html).to_string())
}
}
struct StripHtml;
impl Transformer for StripHtml {
fn should_run(&self, html: &str) -> bool {
// Lame test
html.contains("<")
}
fn transform(&self, html: &str) -> Result<String, TransformError> {
let mut text = String::new();
let element_content_handlers = vec![text!("*", |t| {
text += t.as_str();
Ok(())
})];
let _ = rewrite_str(
html,
RewriteStrSettings {
element_content_handlers,
..RewriteStrSettings::default()
},
)?;
Ok(text)
}
}
struct InlineStyle;
impl Transformer for InlineStyle {
@@ -381,3 +413,83 @@ fn compute_offset_limit(
}
}
}
#[derive(Debug)]
pub struct Query {
pub unread_only: bool,
pub tag: Option<String>,
pub uid: Option<String>,
pub remainder: Vec<String>,
pub is_notmuch: bool,
pub is_newsreader: bool,
}
impl Query {
// Converts the internal state of Query to something suitable for notmuch queries. Removes and
// letterbox specific '<key>:<value' tags
fn to_notmuch(&self) -> String {
let mut parts = Vec::new();
if !self.is_notmuch {
return String::new();
}
if self.unread_only {
parts.push("is:unread".to_string());
}
if let Some(site) = &self.tag {
parts.push(format!("tag:{site}"));
}
if let Some(uid) = &self.uid {
parts.push(uid.clone());
}
parts.extend(self.remainder.clone());
parts.join(" ")
}
}
impl FromStr for Query {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut unread_only = false;
let mut tag = None;
let mut uid = None;
let mut remainder = Vec::new();
let site_prefix = make_news_tag("");
let mut is_notmuch = false;
let mut is_newsreader = false;
for word in s.split_whitespace() {
if word == "is:unread" {
unread_only = true
} else if word.starts_with("tag:") {
tag = Some(word["tag:".len()..].to_string())
/*
} else if word.starts_with("tag:") {
// Any tag that doesn't match site_prefix should explicitly set the site to something not in the
// database
site = Some(NON_EXISTENT_SITE_NAME.to_string());
*/
} else if is_newsreader_thread(word) {
uid = Some(extract_thread_id(word).to_string())
} else if word == "is:mail" || word == "is:email" || word == "is:notmuch" {
is_notmuch = true;
} else if word == "is:news" || word == "is:newsreader" {
is_newsreader = true;
} else {
remainder.push(word.to_string());
}
}
// If we don't see any explicit filters for a corpus, flip them all on
if !(is_notmuch || is_newsreader) {
is_newsreader = true;
is_notmuch = true;
}
Ok(Query {
unread_only,
tag,
uid,
remainder,
is_notmuch,
is_newsreader,
})
}
}