use std::{convert::Infallible, ops::Deref, sync::Arc}; use axum::extract::{FromRef, FromRequestParts}; use nailconfig::NailConfig; use nailgen::{GeneratedTemplate, MarkovGen, Template, WarningTemplate}; use nailrng::FastRng; use nailspicy::SpicyPayloads; use rand::seq::IndexedRandom; pub struct Templates { pub warning: WarningTemplate, pub generated: GeneratedTemplate, } #[derive(Clone)] pub struct NailPayloads { spicy_payloads: Option>, } impl core::fmt::Debug for NailPayloads { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("NailPayloads").finish_non_exhaustive() } } impl NailPayloads { pub fn get(&self) -> Option> { self.spicy_payloads.clone() } } /// Smart pointer for all available Markov chains. #[derive(Clone)] pub struct NailInputs { chains: Arc<[MarkovGen]>, templates: Arc, } impl NailInputs { pub fn new(chains: Arc<[MarkovGen]>, templates: Arc) -> Self { Self { chains, templates } } /// Pulls a random markov chain from the available list. Returns a cloned /// pointer to the selected chain. #[inline] pub fn get_random_input(&self, rng: &mut FastRng) -> MarkovGen { assert!(!self.chains.is_empty()); if self.chains.len() == 1 { self.chains[0].clone() } else { self.chains.choose(rng).unwrap().clone() } } #[inline] pub fn get_warning_template(&self) -> Template { Template::from(self.templates.warning.clone()) } #[inline] pub fn get_generated_template(&self) -> Template { Template::from(self.templates.generated.clone()) } } impl std::fmt::Debug for NailInputs { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("NailInputs").finish_non_exhaustive() } } #[derive(Debug, Clone)] pub struct AppConfig(Arc); impl AppConfig { pub fn clone_inner(&self) -> Arc { self.0.clone() } } impl From> for AppConfig { #[inline] fn from(value: Arc) -> Self { Self(value) } } impl Deref for AppConfig { type Target = NailConfig; fn deref(&self) -> &Self::Target { self.0.as_ref() } } #[derive(Debug, Clone)] pub struct ServerState { pub config: AppConfig, pub inputs: NailInputs, pub spicy_payloads: NailPayloads, } impl ServerState { pub fn new( config: impl Into, chains: Arc<[MarkovGen]>, templates: Arc, spicy_payloads: Option>, ) -> Self { let config = config.into(); Self { config, inputs: NailInputs { chains, templates }, spicy_payloads: NailPayloads { spicy_payloads }, } } } impl FromRef for AppConfig { #[inline] fn from_ref(input: &ServerState) -> Self { input.config.clone() } } impl FromRef for NailInputs { #[inline] fn from_ref(input: &ServerState) -> Self { input.inputs.clone() } } impl FromRequestParts for AppConfig where AppConfig: FromRef, S: Send + Sync, { type Rejection = Infallible; async fn from_request_parts( _parts: &mut axum::http::request::Parts, state: &S, ) -> Result { Ok(AppConfig::from_ref(state)) } } impl FromRequestParts for NailInputs where NailInputs: FromRef, S: Send + Sync, { type Rejection = Infallible; async fn from_request_parts( _parts: &mut axum::http::request::Parts, state: &S, ) -> Result { Ok(NailInputs::from_ref(state)) } }