sugars_builders/
lib.rs

1//! Builder pattern primitives and utilities
2//!
3//! This module provides reusable components for creating fluent typestate builders
4//! that leverage all cyrup_sugars features seamlessly.
5pub 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
14/// Re-export hashbrown for builder convenience
15pub use hashbrown::HashMap;
16
17/// Trait for building configuration objects with validation
18pub trait ConfigBuilder<T> {
19    /// The error type returned when building fails.
20    type Error;
21
22    /// Build the final configuration
23    fn build(self) -> Result<T, Self::Error>;
24
25    /// Validate the current state
26    fn validate(&self) -> Result<(), Self::Error>;
27}
28
29/// Trait for JSON serializable configurations
30pub trait JsonConfig: Serialize + for<'de> Deserialize<'de> {
31    /// Serialize to pretty JSON string
32    fn to_json(&self) -> Result<String, serde_json::Error> {
33        serde_json::to_string_pretty(self)
34    }
35
36    /// Deserialize from JSON string
37    fn from_json(json: &str) -> Result<Self, serde_json::Error> {
38        serde_json::from_str(json)
39    }
40}
41
42/// Automatic implementation for all Serialize + Deserialize types
43impl<T> JsonConfig for T where T: Serialize + for<'de> Deserialize<'de> {}
44
45/// Helper trait for creating object literal syntax
46pub trait ObjectLiteral<K, V> {
47    /// Create from key-value pairs
48    fn from_pairs<I: IntoIterator<Item = (K, V)>>(pairs: I) -> Self;
49
50    /// Create empty object
51    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
80/// Builder state management
81pub mod state {
82    use std::marker::PhantomData;
83
84    /// Marker trait for builder states
85    pub trait BuilderState {}
86
87    /// Builder is incomplete and missing required fields
88    pub struct Incomplete;
89    impl BuilderState for Incomplete {}
90
91    /// Builder has all required fields
92    pub struct Complete;
93    impl BuilderState for Complete {}
94
95    /// Builder with custom validation state
96    pub struct Validated<T>(PhantomData<T>);
97    impl<T> BuilderState for Validated<T> {}
98
99    /// State transition helpers
100    pub trait StateTransition<To: BuilderState> {
101        /// The output type after transition.
102        type Output;
103        /// Performs the state transition.
104        fn transition(self) -> Self::Output;
105    }
106}
107
108/// Common configuration patterns
109pub mod patterns {
110    use super::*;
111
112    /// Authentication configuration pattern
113    #[derive(Serialize, Deserialize, Debug, Clone)]
114    pub struct AuthConfig {
115        /// Authentication methods (e.g., "jwt", "oauth").
116        pub methods: OneOrMany<String>,
117        /// Token time-to-live in seconds.
118        pub token_ttl: u64,
119        /// Authentication providers (e.g., "google", "github").
120        pub providers: ZeroOneOrMany<String>,
121        /// Additional provider-specific settings.
122        pub settings: HashMap<String, serde_json::Value>,
123    }
124
125    impl AuthConfig {
126        /// Creates a JWT authentication configuration.
127        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        /// Creates an OAuth authentication configuration.
142        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    /// Rate limiting configuration pattern
154    #[derive(Serialize, Deserialize, Debug, Clone)]
155    pub struct RateLimitConfig {
156        /// Maximum requests per minute.
157        pub requests_per_minute: u32,
158        /// Burst size for rate limiting.
159        pub burst_size: u32,
160        /// Paths to exclude from rate limiting.
161        pub exclude_paths: ZeroOneOrMany<String>,
162        /// Custom rate limit rules per path.
163        pub custom_rules: HashMap<String, u32>,
164    }
165
166    impl RateLimitConfig {
167        /// Creates a simple rate limit configuration.
168        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        /// Creates a rate limit configuration with custom rules.
181        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    /// CORS configuration pattern
195    #[derive(Serialize, Deserialize, Debug, Clone)]
196    pub struct CorsConfig {
197        /// Allowed origins for CORS requests.
198        pub allowed_origins: ZeroOneOrMany<String>,
199        /// Allowed HTTP methods.
200        pub allowed_methods: OneOrMany<String>,
201        /// Allowed headers in requests.
202        pub allowed_headers: ZeroOneOrMany<String>,
203        /// Maximum age for preflight cache.
204        pub max_age: u64,
205        /// Whether to allow credentials.
206        pub credentials: bool,
207    }
208
209    impl CorsConfig {
210        /// Creates a permissive CORS configuration.
211        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        /// Creates a strict CORS configuration with specific allowed values.
229        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
246/// Async builder support
247pub mod async_support {
248    use sugars_async_task::{AsyncTask, NotResult};
249
250    /// Trait for builders that can deploy/execute asynchronously
251    pub trait AsyncBuilder<T>
252    where
253        T: NotResult,
254    {
255        /// The error type returned when async building fails.
256        type Error;
257
258        /// Execute the builder asynchronously
259        fn execute(self) -> AsyncTask<T>;
260
261        /// Execute with validation
262        fn execute_validated(self) -> AsyncTask<T>
263        where
264            Self: Sized,
265        {
266            self.execute()
267        }
268    }
269}
270
271/// Macro helpers for object literal syntax
272pub mod macros {
273    /// Create HashMap with object literal syntax
274    #[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    /// Create configuration builder with fluent syntax
291    #[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
311/// Feature-gated closure macros for builders
312pub mod closure_macros {
313    // Re-export closure macros for builder usage
314    // Macros are automatically re-exported by macro_export attribute
315
316    /// Builder validation macro
317    #[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::*;
340/// Re-export everything for convenience
341pub use state::*;
342
343pub use async_support::*;