article_scraper 3.0.0-alpha1

Scrap article contents from the web. Powered by fivefilters full text feed configurations & mozilla readability.
Documentation
use regex::{Regex, RegexBuilder};
use std::sync::LazyLock;

/// Markup that identifies the software behind a site, and the config (without `.txt`) to use
/// for sites that don't have their own. Checked in order, as in Graby.
static FINGERPRINTS: LazyLock<Vec<(Regex, &'static str)>> = LazyLock::new(|| {
    [
        (
            r#"<meta\s+content=["']blogger["']\s+name=["']generator["']"#,
            "fingerprint.blogspot.com",
        ),
        (
            r#"<meta\s+name=["']generator["']\s+content=["']blogger["']"#,
            "fingerprint.blogspot.com",
        ),
        (
            r#"<meta\s+name=["']generator["']\s+content=["']wordpress"#,
            "fingerprint.wordpress.com",
        ),
        (
            r#"<div\s+class=["']id-SiteBEEPWrap["']\s*>"#,
            "fingerprint.ippen.media",
        ),
    ]
    .into_iter()
    .map(|(pattern, host)| {
        let regex = RegexBuilder::new(pattern)
            .case_insensitive(true)
            .build()
            .expect("failed to build static regex");
        (regex, host)
    })
    .collect()
});

pub struct Fingerprints;

impl Fingerprints {
    /// The host name of the fingerprint config matching `html`.
    pub fn detect(html: &str) -> Option<&'static str> {
        FINGERPRINTS
            .iter()
            .find(|(regex, _)| regex.is_match(html))
            .map(|(_, host)| *host)
    }
}

#[cfg(test)]
mod tests {
    use super::Fingerprints;

    #[test]
    fn detect() {
        assert_eq!(
            Fingerprints::detect(r#"<meta name="generator" content="Blogger" />"#),
            Some("fingerprint.blogspot.com")
        );
        assert_eq!(
            Fingerprints::detect("<meta content='blogger' name='generator'>"),
            Some("fingerprint.blogspot.com")
        );
        assert_eq!(
            Fingerprints::detect(r#"<meta name="generator" content="WordPress 6.5" />"#),
            Some("fingerprint.wordpress.com")
        );
        assert_eq!(
            Fingerprints::detect(r#"<body><div class="id-SiteBEEPWrap"><p>x</p></div>"#),
            Some("fingerprint.ippen.media")
        );
        assert_eq!(
            Fingerprints::detect(r#"<meta name="generator" content="Hugo 0.120" />"#),
            None
        );
    }
}