iocaine 1.0.0

The deadliest poison known to AI
Documentation
// SPDX-FileCopyrightText: 2025 Gergely Nagy
// SPDX-FileContributor: Gergely Nagy
//
// SPDX-License-Identifier: MIT

use anyhow::Result;
use handlebars::{Handlebars, RenderErrorReason};
use rand::{seq::IndexedRandom, Rng};
use rust_embed::Embed;
use serde::Serialize;

use crate::{app::Iocaine, gobbledygook::GobbledyGook, wurstsalat_generator_pro::join_words};

#[derive(Embed)]
#[folder = "templates/"]
#[include = "*.hbs"]
struct Asset;

#[derive(Serialize, Debug)]
struct Enumerator<T> {
    index: u32,
    value: T,
}

#[derive(Serialize, Debug)]
struct Link {
    href_words: u32,
    title_words: u32,
}

#[derive(Serialize, Debug)]
struct Page<'a> {
    pub request_uri: &'a str,
    pub paragraphs: Vec<Enumerator<u32>>,
    pub links: Vec<Enumerator<Link>>,
}

pub struct AssembledStatisticalSequences;

impl AssembledStatisticalSequences {
    pub fn generate(iocaine: &Iocaine, host: &str, path: &str) -> Result<String> {
        let initial_seed = &iocaine.config.generator.initial_seed;
        let static_seed = format!("{}/{}#{}", host, path, initial_seed);
        let markov_gen = |h: &handlebars::Helper,
                          _: &Handlebars,
                          _: &handlebars::Context,
                          _: &mut handlebars::RenderContext,
                          out: &mut dyn handlebars::Output|
         -> handlebars::HelperResult {
            let group = h
                .param(0)
                .ok_or(RenderErrorReason::ParamNotFoundForIndex("group", 0))?
                .value()
                .as_str()
                .ok_or(RenderErrorReason::InvalidParamType("string"))?;
            let index = h
                .param(1)
                .ok_or(RenderErrorReason::ParamNotFoundForIndex("index", 1))?
                .value()
                .as_i64()
                .ok_or(RenderErrorReason::InvalidParamType("i64"))?;
            let words = h
                .param(2)
                .ok_or(RenderErrorReason::ParamNotFoundForIndex("words", 2))?
                .value()
                .as_i64()
                .ok_or(RenderErrorReason::InvalidParamType("i64"))?;

            let rng = GobbledyGook::for_url(format!("{}://{}/{}", group, &static_seed, index));
            let chain = iocaine.chain.generate(rng).take(words as usize);
            out.write(&join_words(chain))?;

            Ok(())
        };
        let href_gen = |h: &handlebars::Helper,
                        _: &Handlebars,
                        _: &handlebars::Context,
                        _: &mut handlebars::RenderContext,
                        out: &mut dyn handlebars::Output|
         -> handlebars::HelperResult {
            let group = h
                .param(0)
                .ok_or(RenderErrorReason::ParamNotFoundForIndex("group", 0))?
                .value()
                .as_str()
                .ok_or(RenderErrorReason::InvalidParamType("string"))?;
            let index = h
                .param(1)
                .ok_or(RenderErrorReason::ParamNotFoundForIndex("index", 1))?
                .value()
                .as_i64()
                .ok_or(RenderErrorReason::InvalidParamType("i64"))?;
            let count = h
                .param(2)
                .ok_or(RenderErrorReason::ParamNotFoundForIndex("count", 2))?
                .value()
                .as_i64()
                .ok_or(RenderErrorReason::InvalidParamType("i64"))?;

            let mut rng = GobbledyGook::for_url(format!("{}://{}/{}", group, &static_seed, index));
            let words = (1..=count)
                .map(|_| iocaine.words.0.choose(&mut rng).unwrap().as_str())
                .collect::<Vec<_>>()
                .join("-");
            out.write(&words)?;

            Ok(())
        };
        let isroot = |_: &handlebars::Helper,
                      _: &Handlebars,
                      _: &handlebars::Context,
                      _: &mut handlebars::RenderContext,
                      out: &mut dyn handlebars::Output|
         -> handlebars::HelperResult {
            if path == "/" {
                out.write("true")?;
            }
            Ok(())
        };

        let mut rng = GobbledyGook::for_url(format!("iocaine://{}", static_seed));

        // markov

        let paragraph_count = rng.random_range(
            iocaine.config.generator.markov.paragraphs.min
                ..=iocaine.config.generator.markov.paragraphs.max,
        );
        let paragraphs: Vec<Enumerator<u32>> = (0..paragraph_count)
            .map(|idx| Enumerator::<u32> {
                index: idx,
                value: rng.random_range(
                    iocaine.config.generator.markov.words.min
                        ..=iocaine.config.generator.markov.words.max,
                ),
            })
            .collect();

        let link_count = rng.random_range(
            iocaine.config.generator.links.min..=(iocaine.config.generator.links.max),
        );
        let links: Vec<Enumerator<Link>> = (0..link_count)
            .map(|idx| Enumerator::<Link> {
                index: idx,
                value: Link {
                    href_words: rng.random_range(
                        iocaine.config.generator.links.href_words.min
                            ..=iocaine.config.generator.links.href_words.max,
                    ),
                    title_words: rng.random_range(
                        iocaine.config.generator.links.title_words.min
                            ..=iocaine.config.generator.links.title_words.max,
                    ),
                },
            })
            .collect();

        let mut handlebars = Handlebars::new();
        if let Some(dir) = &iocaine.config.templates.directory {
            handlebars
                .register_templates_directory(dir, handlebars::DirectorySourceOptions::default())?;
        } else {
            handlebars.register_embed_templates_with_extension::<Asset>(".hbs")?;
        }
        handlebars.register_helper("markov-gen", Box::new(markov_gen));
        handlebars.register_helper("href-gen", Box::new(href_gen));
        handlebars.register_helper("is-root", Box::new(isroot));

        let data = Page {
            request_uri: path,
            paragraphs,
            links,
        };

        let host_template = &format!("hosts/{}", host);
        let rendered = if handlebars.has_template(host_template) {
            handlebars.render(host_template, &data)?
        } else {
            handlebars.render("main", &data)?
        };
        Ok(rendered)
    }
}