use anyhow::Result;
use minijinja::{value::Kwargs, Environment, Error, State};
use rand::{seq::IndexedRandom, Rng};
use regex::Regex;
use serde::Serialize;
use std::collections::BTreeMap;
use crate::{
config::Config,
garglebargle::GargleBargle,
gobbledygook::GobbledyGook,
qr_journey,
wurstsalat_generator_pro::{join_words, WurstsalatGeneratorPro},
};
#[derive(Debug)]
pub struct AssembledStatisticalSequences {
pub config: Config,
pub engine: Environment<'static>,
}
#[derive(Debug, Serialize)]
struct PageContext<'a> {
pub content_type: &'a str,
pub static_seed: String,
pub request_host: &'a str,
pub request_uri: &'a str,
pub params: BTreeMap<String, String>,
}
impl AssembledStatisticalSequences {
pub fn new(config: &Config) -> Self {
tracing::info!("Loading the markov chain corpus");
let chain = WurstsalatGeneratorPro::learn_from_files(&config.sources.markov).unwrap();
tracing::info!("Corpus loaded");
tracing::info!("Loading word salad");
let words = GargleBargle::load_words(&config.sources.words).unwrap();
tracing::info!("Word salad loaded");
let mut engine = Environment::new();
if let Some(dir) = &config.templates.directory {
engine.set_loader(minijinja::path_loader(dir));
} else if let Some(main) = &config.templates.main {
engine
.add_template_owned("main.jinja", main.clone())
.unwrap();
} else {
minijinja_embed::load_templates!(&mut engine);
}
let rand = move |state: &State, options: Kwargs| -> Result<u32, Error> {
let mut rng = GobbledyGook::from(state, &options, "default")?;
let result = rng.gen.random_range(rng.min..=rng.max);
Ok(result)
};
engine.add_function("rand", rand);
let regex_gen =
move |state: &State, pattern: &str, options: Kwargs| -> Result<String, Error> {
let mut rng = GobbledyGook::from(state, &options, "regex")?;
let gen = rand_regex::Regex::compile(pattern, options.get("max")?).unwrap();
Ok(rng.gen.sample(&gen))
};
engine.add_function("regex_gen", regex_gen);
let markov_gen = move |state: &State, options: Kwargs| -> Result<String, Error> {
let mut rng = GobbledyGook::from(state, &options, "markov")?;
let words = options
.get::<Option<usize>>("words")?
.unwrap_or_else(|| rng.gen.random_range(rng.min..=rng.max) as usize);
let result = chain.generate(rng.gen).take(words);
Ok(join_words(result))
};
engine.add_function("markov_gen", markov_gen);
let href_gen = move |state: &State, options: Kwargs| -> Result<String, Error> {
let mut rng = GobbledyGook::from(state, &options, "href")?;
let count = options
.get::<Option<usize>>("words")?
.unwrap_or_else(|| rng.gen.random_range(rng.min..=rng.max) as usize);
let result = (1..=count)
.map(|_| words.0.choose(&mut rng.gen).unwrap().as_str())
.collect::<Vec<_>>()
.join("-");
Ok(result)
};
engine.add_function("href_gen", href_gen);
engine.add_function(
"qr",
move |state: &State, content: String, options: Kwargs| -> Result<String, Error> {
qr_journey::generate(state, content, options)
},
);
engine.add_filter(
"matches",
move |haystack: String, needle: String| -> Result<bool, Error> {
let re = Regex::new(&needle).unwrap();
Ok(re.is_match(&haystack))
},
);
engine.add_filter(
"sanitize_filename",
|filename: String, replacement: Option<&str>| -> String {
sanitize_filename::sanitize_with_options(
filename,
sanitize_filename::Options {
replacement: replacement.unwrap_or(""),
..Default::default()
},
)
},
);
Self {
config: config.clone(),
engine,
}
}
pub fn generate(
&self,
host: &str,
path: &str,
params: &BTreeMap<String, String>,
) -> Result<(String, String)> {
let initial_seed = &self.config.generator.initial_seed;
let serialized_params = params
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<_>>()
.join("-");
let static_seed = format!("{}/{}#{}{}", host, path, initial_seed, serialized_params);
let template = self.engine.get_template("main.jinja")?;
let context = PageContext {
content_type: "text/html",
static_seed,
request_host: host,
request_uri: path,
params: params.clone(),
};
let (garbage, state) = template.render_and_return_state(context)?;
let content_type = state.lookup("content_type").unwrap_or("text/html".into());
Ok((content_type.to_string(), garbage))
}
}