A pit full of rusty nails
1use std::{convert::Infallible, ops::Deref, sync::Arc};
2
3use axum::extract::{FromRef, FromRequestParts};
4use nailconfig::NailConfig;
5use nailgen::{GeneratedTemplate, MarkovGen, Template, WarningTemplate};
6use nailrng::FastRng;
7use nailspicy::SpicyPayloads;
8use rand::seq::IndexedRandom;
9
10pub struct Templates {
11 pub warning: WarningTemplate,
12 pub generated: GeneratedTemplate,
13}
14
15#[derive(Clone)]
16pub struct NailPayloads {
17 spicy_payloads: Option<Arc<SpicyPayloads>>,
18}
19
20impl core::fmt::Debug for NailPayloads {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 f.debug_struct("NailPayloads").finish_non_exhaustive()
23 }
24}
25
26impl NailPayloads {
27 pub fn get(&self) -> Option<Arc<SpicyPayloads>> {
28 self.spicy_payloads.clone()
29 }
30}
31
32/// Smart pointer for all available Markov chains.
33#[derive(Clone)]
34pub struct NailInputs {
35 chains: Arc<[MarkovGen]>,
36 templates: Arc<Templates>,
37}
38
39impl NailInputs {
40 pub fn new(chains: Arc<[MarkovGen]>, templates: Arc<Templates>) -> Self {
41 Self { chains, templates }
42 }
43
44 /// Pulls a random markov chain from the available list. Returns a cloned
45 /// pointer to the selected chain.
46 #[inline]
47 pub fn get_random_input(&self, rng: &mut FastRng) -> MarkovGen {
48 assert!(!self.chains.is_empty());
49
50 if self.chains.len() == 1 {
51 self.chains[0].clone()
52 } else {
53 self.chains.choose(rng).unwrap().clone()
54 }
55 }
56
57 #[inline]
58 pub fn get_warning_template(&self) -> Template {
59 Template::from(self.templates.warning.clone())
60 }
61
62 #[inline]
63 pub fn get_generated_template(&self) -> Template {
64 Template::from(self.templates.generated.clone())
65 }
66}
67
68impl std::fmt::Debug for NailInputs {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 f.debug_tuple("NailInputs").finish_non_exhaustive()
71 }
72}
73
74#[derive(Debug, Clone)]
75pub struct AppConfig(Arc<NailConfig>);
76
77impl AppConfig {
78 pub fn clone_inner(&self) -> Arc<NailConfig> {
79 self.0.clone()
80 }
81}
82
83impl From<Arc<NailConfig>> for AppConfig {
84 #[inline]
85 fn from(value: Arc<NailConfig>) -> Self {
86 Self(value)
87 }
88}
89
90impl Deref for AppConfig {
91 type Target = NailConfig;
92
93 fn deref(&self) -> &Self::Target {
94 self.0.as_ref()
95 }
96}
97
98#[derive(Debug, Clone)]
99pub struct ServerState {
100 pub config: AppConfig,
101 pub inputs: NailInputs,
102 pub spicy_payloads: NailPayloads,
103}
104
105impl ServerState {
106 pub fn new(
107 config: impl Into<AppConfig>,
108 chains: Arc<[MarkovGen]>,
109 templates: Arc<Templates>,
110 spicy_payloads: Option<Arc<SpicyPayloads>>,
111 ) -> Self {
112 let config = config.into();
113
114 Self {
115 config,
116 inputs: NailInputs { chains, templates },
117 spicy_payloads: NailPayloads { spicy_payloads },
118 }
119 }
120}
121
122impl FromRef<ServerState> for AppConfig {
123 #[inline]
124 fn from_ref(input: &ServerState) -> Self {
125 input.config.clone()
126 }
127}
128
129impl FromRef<ServerState> for NailInputs {
130 #[inline]
131 fn from_ref(input: &ServerState) -> Self {
132 input.inputs.clone()
133 }
134}
135
136impl<S> FromRequestParts<S> for AppConfig
137where
138 AppConfig: FromRef<S>,
139 S: Send + Sync,
140{
141 type Rejection = Infallible;
142
143 async fn from_request_parts(
144 _parts: &mut axum::http::request::Parts,
145 state: &S,
146 ) -> Result<Self, Self::Rejection> {
147 Ok(AppConfig::from_ref(state))
148 }
149}
150
151impl<S> FromRequestParts<S> for NailInputs
152where
153 NailInputs: FromRef<S>,
154 S: Send + Sync,
155{
156 type Rejection = Infallible;
157
158 async fn from_request_parts(
159 _parts: &mut axum::http::request::Parts,
160 state: &S,
161 ) -> Result<Self, Self::Rejection> {
162 Ok(NailInputs::from_ref(state))
163 }
164}