iocaine 3.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 axum::{
    extract::{Query, Request, State},
    handler::Handler,
    http::StatusCode,
    response::IntoResponse,
};
use serde::Serialize;
use std::collections::BTreeMap;
use std::path;
use std::sync::Arc;
use tokio_listener::Listener;

use crate::{
    morgue::{Decay, StateOfDecay, shutdown_signal},
    sex_dungeon::{self, Language, Request as SDRequest, SharedRequest},
    tenx_programmer::TenXProgrammer,
};

#[derive(Clone)]
pub struct Bruce {
    pub state: StateOfDecay,
}

impl Bruce {
    pub fn new<P: AsRef<path::Path>, C: AsRef<path::Path>, S: Serialize>(
        language: Language,
        compiler: Option<C>,
        path: &Option<P>,
        initial_seed: &str,
        metrics: &TenXProgrammer,
        config: Option<S>,
    ) -> Result<Self> {
        let request_handler = Arc::new(sex_dungeon::create(
            language,
            compiler,
            path.as_ref(),
            initial_seed,
            metrics,
            config,
        )?);

        if !request_handler.can_output() {
            let path = path
                .as_ref()
                .map_or_else(|| "(default)".into(), |p| p.as_ref().display().to_string());
            tracing::error!({ path }, "At least an output() function is required");
            anyhow::bail!("Requires at least output()");
        }

        let state = Decay {
            metrics: metrics.clone(),
            request_handler,
        }
        .into();

        Ok(Self { state })
    }

    #[allow(clippy::significant_drop_tightening)]
    pub fn run_tests(self) -> anyhow::Result<()> {
        let mut state = self.state.write().unwrap();
        let handler = Arc::get_mut(&mut state.request_handler).unwrap();
        handler.run_tests()
    }

    pub async fn serve(self, listener: Listener) -> Result<()> {
        let app = handler
            .layer(tower_http::trace::TraceLayer::new_for_http())
            .with_state(self.state);

        Ok(axum::serve(listener, app.into_make_service())
            .with_graceful_shutdown(shutdown_signal(None))
            .await?)
    }
}

async fn handler(
    State(state): State<StateOfDecay>,
    Query(params): Query<BTreeMap<String, String>>,
    request: Request,
) -> impl IntoResponse {
    let request: SharedRequest = SDRequest {
        method: request.method().to_string(),
        path: request.uri().path().to_owned(),
        headers: request.headers().clone(),
        params,
    }
    .into();

    let state = state.read().unwrap();
    let decision = if state.request_handler.can_decide() {
        match state.request_handler.decide(request.clone()) {
            Ok(v) => Some(v),
            Err(e) => {
                tracing::error!("Error running decide(): {e}");
                return server_error().into_response();
            }
        }
    } else {
        None
    };
    state.request_handler.output(request, decision).map_or_else(
        |e| {
            tracing::error!("Error running output(): {e}");
            server_error().into_response()
        },
        IntoResponse::into_response,
    )
}

fn server_error() -> impl IntoResponse {
    (StatusCode::INTERNAL_SERVER_ERROR, "")
}