1pub mod chunk_handler;
6pub mod llm;
7pub use chunk_handler::*;
8pub use llm::*;
9
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap as StdHashMap;
12use sugars_collections::{OneOrMany, ZeroOneOrMany};
13
14pub use hashbrown::HashMap;
16
17pub trait ConfigBuilder<T> {
19 type Error;
21
22 fn build(self) -> Result<T, Self::Error>;
24
25 fn validate(&self) -> Result<(), Self::Error>;
27}
28
29pub trait JsonConfig: Serialize + for<'de> Deserialize<'de> {
31 fn to_json(&self) -> Result<String, serde_json::Error> {
33 serde_json::to_string_pretty(self)
34 }
35
36 fn from_json(json: &str) -> Result<Self, serde_json::Error> {
38 serde_json::from_str(json)
39 }
40}
41
42impl<T> JsonConfig for T where T: Serialize + for<'de> Deserialize<'de> {}
44
45pub trait ObjectLiteral<K, V> {
47 fn from_pairs<I: IntoIterator<Item = (K, V)>>(pairs: I) -> Self;
49
50 fn empty() -> Self;
52}
53
54impl<K, V> ObjectLiteral<K, V> for HashMap<K, V>
55where
56 K: std::hash::Hash + Eq,
57{
58 fn from_pairs<I: IntoIterator<Item = (K, V)>>(pairs: I) -> Self {
59 pairs.into_iter().collect()
60 }
61
62 fn empty() -> Self {
63 HashMap::new()
64 }
65}
66
67impl<K, V> ObjectLiteral<K, V> for StdHashMap<K, V>
68where
69 K: std::hash::Hash + Eq,
70{
71 fn from_pairs<I: IntoIterator<Item = (K, V)>>(pairs: I) -> Self {
72 pairs.into_iter().collect()
73 }
74
75 fn empty() -> Self {
76 StdHashMap::new()
77 }
78}
79
80pub mod state {
82 use std::marker::PhantomData;
83
84 pub trait BuilderState {}
86
87 pub struct Incomplete;
89 impl BuilderState for Incomplete {}
90
91 pub struct Complete;
93 impl BuilderState for Complete {}
94
95 pub struct Validated<T>(PhantomData<T>);
97 impl<T> BuilderState for Validated<T> {}
98
99 pub trait StateTransition<To: BuilderState> {
101 type Output;
103 fn transition(self) -> Self::Output;
105 }
106}
107
108pub mod patterns {
110 use super::*;
111
112 #[derive(Serialize, Deserialize, Debug, Clone)]
114 pub struct AuthConfig {
115 pub methods: OneOrMany<String>,
117 pub token_ttl: u64,
119 pub providers: ZeroOneOrMany<String>,
121 pub settings: HashMap<String, serde_json::Value>,
123 }
124
125 impl AuthConfig {
126 pub fn jwt(secret: &str) -> Self {
128 Self {
129 methods: OneOrMany::one("jwt".to_string()),
130 token_ttl: 3600,
131 providers: ZeroOneOrMany::none(),
132 settings: {
133 let mut settings = HashMap::new();
134 settings.insert("secret".to_string(), secret.into());
135 settings.insert("algorithm".to_string(), "HS256".into());
136 settings
137 },
138 }
139 }
140
141 pub fn oauth<P: Into<ZeroOneOrMany<String>>>(providers: P) -> Self {
143 Self {
144 methods: OneOrMany::many(vec!["oauth".to_string(), "jwt".to_string()])
145 .unwrap_or_else(|_| OneOrMany::one("oauth".to_string())),
146 token_ttl: 7200,
147 providers: providers.into(),
148 settings: HashMap::new(),
149 }
150 }
151 }
152
153 #[derive(Serialize, Deserialize, Debug, Clone)]
155 pub struct RateLimitConfig {
156 pub requests_per_minute: u32,
158 pub burst_size: u32,
160 pub exclude_paths: ZeroOneOrMany<String>,
162 pub custom_rules: HashMap<String, u32>,
164 }
165
166 impl RateLimitConfig {
167 pub fn simple(rpm: u32) -> Self {
169 Self {
170 requests_per_minute: rpm,
171 burst_size: rpm / 10,
172 exclude_paths: ZeroOneOrMany::many(vec![
173 "/health".to_string(),
174 "/metrics".to_string(),
175 ]),
176 custom_rules: HashMap::new(),
177 }
178 }
179
180 pub fn with_rules(rpm: u32, rules: HashMap<String, u32>) -> Self {
182 Self {
183 requests_per_minute: rpm,
184 burst_size: rpm / 10,
185 exclude_paths: ZeroOneOrMany::many(vec![
186 "/health".to_string(),
187 "/metrics".to_string(),
188 ]),
189 custom_rules: rules,
190 }
191 }
192 }
193
194 #[derive(Serialize, Deserialize, Debug, Clone)]
196 pub struct CorsConfig {
197 pub allowed_origins: ZeroOneOrMany<String>,
199 pub allowed_methods: OneOrMany<String>,
201 pub allowed_headers: ZeroOneOrMany<String>,
203 pub max_age: u64,
205 pub credentials: bool,
207 }
208
209 impl CorsConfig {
210 pub fn permissive() -> Self {
212 Self {
213 allowed_origins: ZeroOneOrMany::one("*".to_string()),
214 allowed_methods: OneOrMany::many(vec![
215 "GET".to_string(),
216 "POST".to_string(),
217 "PUT".to_string(),
218 "DELETE".to_string(),
219 "OPTIONS".to_string(),
220 ])
221 .unwrap_or_else(|_| OneOrMany::one("GET".to_string())),
222 allowed_headers: ZeroOneOrMany::one("*".to_string()),
223 max_age: 86400,
224 credentials: false,
225 }
226 }
227
228 pub fn strict<O, M, H>(origins: O, methods: M, headers: H) -> Self
230 where
231 O: Into<ZeroOneOrMany<String>>,
232 M: Into<OneOrMany<String>>,
233 H: Into<ZeroOneOrMany<String>>,
234 {
235 Self {
236 allowed_origins: origins.into(),
237 allowed_methods: methods.into(),
238 allowed_headers: headers.into(),
239 max_age: 86400,
240 credentials: true,
241 }
242 }
243 }
244}
245
246pub mod async_support {
248 use sugars_async_task::{AsyncTask, NotResult};
249
250 pub trait AsyncBuilder<T>
252 where
253 T: NotResult,
254 {
255 type Error;
257
258 fn execute(self) -> AsyncTask<T>;
260
261 fn execute_validated(self) -> AsyncTask<T>
263 where
264 Self: Sized,
265 {
266 self.execute()
267 }
268 }
269}
270
271pub mod macros {
273 #[macro_export]
275 macro_rules! object {
276 () => {
277 $crate::builders::HashMap::new()
278 };
279 ($($key:expr => $value:expr),+ $(,)?) => {
280 {
281 let mut map = $crate::builders::HashMap::new();
282 $(
283 map.insert($key, $value);
284 )+
285 map
286 }
287 };
288 }
289
290 #[macro_export]
292 macro_rules! config_builder {
293 ($builder_type:ty) => {
294 <$builder_type>::new()
295 };
296 ($builder_type:ty, $($method:ident($($arg:expr),*)),+ $(,)?) => {
297 {
298 let builder = <$builder_type>::new();
299 $(
300 let builder = builder.$method($($arg),*);
301 )+
302 builder
303 }
304 };
305 }
306
307 pub use config_builder;
308 pub use object;
309}
310
311pub mod closure_macros {
313 #[macro_export]
318 macro_rules! validate_config {
319 ($config:expr, $($field:ident => $validation:expr),+ $(,)?) => {
320 {
321 let mut errors = Vec::new();
322 $(
323 if !($validation) {
324 errors.push(format!("Validation failed for field: {}", stringify!($field)));
325 }
326 )+
327 if errors.is_empty() {
328 Ok($config)
329 } else {
330 Err(errors.join(", "))
331 }
332 }
333 };
334 }
335
336 pub use validate_config;
337}
338
339pub use patterns::*;
340pub use state::*;
342
343pub use async_support::*;