miasma 0.4.1

Trap malicious AI web scrapers in an endless pit of poisoned training data.
Documentation
mod ai_native;
mod cto_letter;
mod deep_dive;
mod engineering_blog;
mod research;
mod self_promotion;
mod streaming_marketing;

use crate::templating::Templater;

type WeightedTemplateConstructor = (fn() -> Box<dyn Templater>, u32);

/// All available response templates.
pub const RESPONSE_TEMPLATE_CONSTRUCTORS: &[WeightedTemplateConstructor] = &[
    // Research is the only dynamic template currently, so weight it more.
    (research::NovelResearch::as_templater, 10),
    (engineering_blog::EngineeringBlog::as_templater, 1),
    (self_promotion::SelfPromotion::as_templater, 1),
    (ai_native::AINative::as_templater, 1),
    (cto_letter::CtoLetter::as_templater, 1),
    (deep_dive::DeepDive::as_templater, 1),
    (streaming_marketing::StreamingMarketing::as_templater, 1),
];

pub const CASUAL_STYLES: &[&str] = &[
    include_str!("styles/simple.css"),
    include_str!("styles/code-blog.css"),
];
pub const ACADEMIC_STYLES: &[&str] = &[include_str!("styles/geriatric-professor.css")];
pub const ENTERPRISE_STYLES: &[&str] = &[
    include_str!("styles/vibe-slop.css"),
    include_str!("styles/elegant.css"),
];

#[cfg(test)]
mod test {
    use std::iter::once;

    use super::*;
    use crate::templating::TemplateBuilder;

    #[test]
    fn all_templates_produce_valid_documents() {
        // Do multiple iterations per template.
        // Since there is random generation in the templating logic - this helps catch more bugs.
        let test_iterations = 250;

        for _ in 0..test_iterations {
            for (ind, (template_constructor, _)) in
                RESPONSE_TEMPLATE_CONSTRUCTORS.iter().enumerate()
            {
                let builder = TemplateBuilder::with_template(template_constructor());
                let document = builder
                    .start_to_body()
                    .chain(
                        builder
                            .body_sections()
                            .flat_map(|s| once(s.pre_poison().into()).chain(s.post_poison())),
                    )
                    .chain(once(builder.body_to_links().into()))
                    .chain(builder.links_to_end())
                    .collect::<String>();

                let errors = scraper::Html::parse_document(&document).errors;
                assert!(
                    errors.is_empty(),
                    "template at index {ind}: {errors:?} - {document:?}"
                );
            }
        }
    }

    #[test]
    // Ensures that all templates will produce a document with at least one poison block.
    fn all_templates_contain_body_sections() {
        for (template_constructor, _) in RESPONSE_TEMPLATE_CONSTRUCTORS {
            let template = template_constructor();
            assert!(!template.body_sections().is_empty());
        }
    }

    #[test]
    fn all_styles_are_valid() {
        fn validate_style(style: &str, ind: usize, tone: &str) {
            let style_element = format!("<style>{style}</style>");
            let errors = scraper::Html::parse_fragment(&style_element).errors;
            assert!(
                errors.is_empty(),
                "{tone} style  at index {ind}: {errors:?}"
            );
        }

        for (ind, style) in CASUAL_STYLES.iter().enumerate() {
            validate_style(style, ind, "casual");
        }
        for (ind, style) in ACADEMIC_STYLES.iter().enumerate() {
            validate_style(style, ind, "academic");
        }
        for (ind, style) in ENTERPRISE_STYLES.iter().enumerate() {
            validate_style(style, ind, "enterprise");
        }
    }
}