iocaine 2.4.1

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

use serde::Deserialize;
use std::collections::HashMap;
use std::fmt::Display;
use std::path::Path;

use crate::tenx_programmer::TenXProgrammerCounters;

mod elegant_weapons;
mod howl;
pub mod means_of_production;
mod outcome;
mod request;

pub use elegant_weapons::ElegantWeapons;
pub use howl::Howl;
pub use means_of_production::MeansOfProduction;
pub use outcome::Outcome;
pub use request::Request;

pub type NPC = Box<dyn SexDungeon + Send + Sync + 'static>;

pub trait SexDungeon {
    fn new<P: AsRef<Path>>(
        path: P,
        counters: Option<&TenXProgrammerCounters>,
        options: &LanguageOptions,
    ) -> anyhow::Result<Self>
    where
        Self: Sized + Send + Sync + 'static;

    fn decide(&self, request: Request) -> Result<Outcome, Outcome>;

    fn run_tests(&mut self) -> anyhow::Result<()>;
}

#[derive(Clone, Copy, Debug, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum Language {
    #[default]
    Roto,
    Lua,
    Fennel,
}

impl Display for Language {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let lang = match self {
            Self::Roto => "roto",
            Self::Lua => "lua",
            Self::Fennel => "fennel",
        };
        write!(f, "{lang}")
    }
}

pub type LanguageOptions = HashMap<String, String>;

pub fn create<P: AsRef<Path>>(
    language: Language,
    options: &LanguageOptions,
    path: P,
    counters: Option<&TenXProgrammerCounters>,
) -> anyhow::Result<NPC> {
    let dungeon: NPC = match language {
        Language::Roto => Box::new(MeansOfProduction::new(path, counters, options)?),
        Language::Lua => Box::new(Howl::new(path, counters, options)?),
        Language::Fennel => Box::new(ElegantWeapons::new(path, counters, options)?),
    };
    Ok(dungeon)
}