reinhardt_core/lib.rs
1#![warn(missing_docs)]
2//! # Reinhardt Core
3//!
4//! Core components for the Reinhardt framework, providing fundamental types,
5//! exception handling, signals, macros, security, and validation utilities.
6//!
7//! ## Available Validators
8//!
9//! The validators crate provides comprehensive validation utilities:
10//! - **IPAddressValidator**: IPv4/IPv6 address validation
11//! - **PhoneNumberValidator**: International phone number validation (E.164)
12//! - **CreditCardValidator**: Credit card validation with Luhn algorithm
13//! - **IBANValidator**: International bank account number validation
14//! - **ColorValidator**: Hex, RGB, HSL color validation
15//! - **FileTypeValidator**: MIME type and extension validation
16//! - **CustomRegexValidator**: User-defined regex pattern validation
17//!
18//! ## Related Backend Crates
19//!
20//! Backend integrations live in focused crates rather than in `reinhardt-core`.
21//! See `reinhardt-db`, `reinhardt-auth`, `reinhardt-mail`,
22//! `reinhardt-tasks`, and `reinhardt-utils` for database, auth, mail,
23//! queue, cache, and storage integrations.
24//!
25//! ## Quick Start
26//!
27//! ```rust
28//! use reinhardt_core::exception::{Error, ErrorKind};
29//!
30//! // Create a typed application error
31//! let err = Error::NotFound("Resource not found".to_string());
32//! assert_eq!(err.kind(), ErrorKind::NotFound);
33//! ```
34//!
35//! ## Architecture
36//!
37//! Key modules in this crate:
38//!
39//! - [`exception`]: Typed error hierarchy for HTTP and application-level errors
40//! - [`types`]: Fundamental types (URL, money, phone number, color, coordinates)
41//! - [`signals`]: Django-style signal/slot system for decoupled event handling
42//! - [`security`]: CSRF, XSS prevention, security headers, HSTS, IP filtering, redirect validation, and resource limits
43//! - [`validators`]: Comprehensive input validation (IP, IBAN, phone, credit card)
44//! - [`serializers`]: Data serialization and deserialization framework
45//! - `pagination`: Cursor, page number, and limit-offset pagination strategies
46//! - [`parsers`]: Request body parsing (JSON, form, multipart)
47//! - `negotiation`: HTTP content negotiation utilities
48//!
49//! ## Feature Flags
50//!
51//! | Feature | Default | Description |
52//! |---------|---------|-------------|
53//! | `types` | enabled | Core type definitions |
54//! | `exception` | enabled | Error hierarchy and HTTP status mapping |
55//! | `signals` | enabled | Async signal/slot system |
56//! | `macros` | enabled | Procedural macros re-export |
57//! | `security` | enabled | CSRF, XSS prevention, headers, HSTS, IP filtering, redirects, and resource limits |
58//! | `validators` | enabled | Comprehensive input validation |
59//! | `serializers` | enabled | Data serialization framework |
60//! | `parsers` | disabled | Request body parsers |
61//! | `pagination` | disabled | Pagination strategies |
62//! | `negotiation` | disabled | HTTP content negotiation |
63//! | `messages` | disabled | Flash message storage |
64//! | `page` | disabled | Server-side page rendering types |
65//! | `reactive` | disabled | Reactive state management |
66//! | `serde` | disabled | Serde serialization support |
67//! | `json` | disabled | JSON serialization support |
68//! | `xml` | disabled | XML serialization support |
69//! | `yaml` | disabled | YAML serialization support |
70//! | `parallel` | disabled | Parallel processing with Rayon |
71//! | `i18n` | disabled | Internationalization with Fluent |
72
73pub mod apply_update;
74pub use apply_update::ApplyUpdate;
75/// HTTP endpoint routing and handler registration.
76#[cfg(native)]
77pub mod endpoint;
78/// Error types and exception handling.
79#[cfg(feature = "exception")]
80pub mod exception;
81/// Flash message storage framework.
82#[cfg(feature = "messages")]
83pub mod messages;
84/// Target-neutral metadata traits emitted by model macros.
85pub mod model_info {
86 use std::marker::PhantomData;
87
88 /// Minimal model identity needed by generated `{Model}Info` companion types.
89 ///
90 /// Unlike the ORM `Model` trait, this trait is available on WASM and only
91 /// exposes the primary-key type required for generated foreign-key `*_id`
92 /// fields.
93 pub trait InfoModel {
94 /// Primary-key type used by generated DTO companion fields.
95 type PrimaryKey;
96 }
97
98 /// Lightweight relationship reference used by generated `{Model}Info` fields (Issue #5272).
99 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
100 #[cfg_attr(
101 feature = "serde",
102 serde(bound(
103 serialize = "T::PrimaryKey: serde::Serialize",
104 deserialize = "T::PrimaryKey: serde::Deserialize<'de>"
105 ))
106 )]
107 pub struct RelationInfo<T: InfoModel> {
108 /// Primary key of the related model.
109 pub id: T::PrimaryKey,
110 #[cfg_attr(feature = "serde", serde(skip))]
111 _model: PhantomData<T>,
112 }
113
114 impl<T: InfoModel> RelationInfo<T> {
115 /// Creates a relationship reference from a related model primary key.
116 pub const fn new(id: T::PrimaryKey) -> Self {
117 Self {
118 id,
119 _model: PhantomData,
120 }
121 }
122
123 /// Returns the related model primary key.
124 pub const fn id(&self) -> &T::PrimaryKey {
125 &self.id
126 }
127
128 /// Converts this relationship reference into its primary key.
129 pub fn into_id(self) -> T::PrimaryKey {
130 self.id
131 }
132 }
133
134 impl<T> std::fmt::Debug for RelationInfo<T>
135 where
136 T: InfoModel,
137 T::PrimaryKey: std::fmt::Debug,
138 {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 f.debug_struct("RelationInfo")
141 .field("id", &self.id)
142 .finish()
143 }
144 }
145
146 impl<T> Clone for RelationInfo<T>
147 where
148 T: InfoModel,
149 T::PrimaryKey: Clone,
150 {
151 fn clone(&self) -> Self {
152 Self::new(self.id.clone())
153 }
154 }
155
156 impl<T> PartialEq for RelationInfo<T>
157 where
158 T: InfoModel,
159 T::PrimaryKey: PartialEq,
160 {
161 fn eq(&self, other: &Self) -> bool {
162 self.id == other.id
163 }
164 }
165
166 /// Lightweight many-to-many relationship payload for generated `{Model}Info` (Issue #5272).
167 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
168 #[cfg_attr(
169 feature = "serde",
170 serde(bound(
171 serialize = "Target::PrimaryKey: serde::Serialize",
172 deserialize = "Target::PrimaryKey: serde::Deserialize<'de>"
173 ))
174 )]
175 pub struct ManyToManyInfo<Source, Target: InfoModel> {
176 /// Primary keys of related target models.
177 pub target_ids: Vec<Target::PrimaryKey>,
178 #[cfg_attr(feature = "serde", serde(skip))]
179 _source: PhantomData<Source>,
180 }
181
182 impl<Source, Target> ManyToManyInfo<Source, Target>
183 where
184 Target: InfoModel,
185 {
186 /// Creates a many-to-many payload from target primary keys.
187 pub fn new<I>(target_ids: I) -> Self
188 where
189 I: IntoIterator<Item = Target::PrimaryKey>,
190 {
191 Self {
192 target_ids: target_ids.into_iter().collect(),
193 _source: PhantomData,
194 }
195 }
196
197 /// Creates an empty many-to-many payload.
198 pub const fn empty() -> Self {
199 Self {
200 target_ids: Vec::new(),
201 _source: PhantomData,
202 }
203 }
204
205 /// Returns the target model primary keys.
206 pub fn target_ids(&self) -> &[Target::PrimaryKey] {
207 &self.target_ids
208 }
209
210 /// Converts this payload into the target primary-key list.
211 pub fn into_target_ids(self) -> Vec<Target::PrimaryKey> {
212 self.target_ids
213 }
214 }
215
216 impl<Source, Target> Default for ManyToManyInfo<Source, Target>
217 where
218 Target: InfoModel,
219 {
220 fn default() -> Self {
221 Self::empty()
222 }
223 }
224
225 impl<Source, Target> std::fmt::Debug for ManyToManyInfo<Source, Target>
226 where
227 Target: InfoModel,
228 Target::PrimaryKey: std::fmt::Debug,
229 {
230 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231 f.debug_struct("ManyToManyInfo")
232 .field("target_ids", &self.target_ids)
233 .finish()
234 }
235 }
236
237 impl<Source, Target> Clone for ManyToManyInfo<Source, Target>
238 where
239 Target: InfoModel,
240 Target::PrimaryKey: Clone,
241 {
242 fn clone(&self) -> Self {
243 Self::new(self.target_ids.clone())
244 }
245 }
246
247 impl<Source, Target> PartialEq for ManyToManyInfo<Source, Target>
248 where
249 Target: InfoModel,
250 Target::PrimaryKey: PartialEq,
251 {
252 fn eq(&self, other: &Self) -> bool {
253 self.target_ids == other.target_ids
254 }
255 }
256
257 #[cfg(test)]
258 mod tests {
259 use super::{InfoModel, ManyToManyInfo, RelationInfo};
260
261 #[derive(Debug)]
262 struct Post;
263
264 impl InfoModel for Post {
265 type PrimaryKey = i64;
266 }
267
268 #[test]
269 fn relation_info_preserves_primary_key() {
270 let relation = RelationInfo::<Post>::new(7);
271 assert_eq!(*relation.id(), 7);
272 assert_eq!(relation.into_id(), 7);
273 }
274
275 #[test]
276 fn many_to_many_info_preserves_target_ids() {
277 let info = ManyToManyInfo::<(), Post>::new([1, 2, 3]);
278 assert_eq!(info.target_ids(), &[1, 2, 3]);
279 }
280 }
281}
282/// Content negotiation for request/response formats.
283#[cfg(feature = "negotiation")]
284pub mod negotiation;
285/// Pagination strategies (page-based, cursor, limit-offset).
286#[cfg(feature = "pagination")]
287pub mod pagination;
288/// Request body parsers (JSON, form, multipart, etc.).
289#[cfg(feature = "parsers")]
290pub mod parsers;
291/// Rate limiting strategies.
292pub mod rate_limit;
293/// Reactive state management primitives.
294#[cfg(feature = "reactive")]
295pub mod reactive;
296/// Security utilities (CSRF, XSS prevention, headers, HSTS, IP filtering, redirects, and resource limits).
297#[cfg(feature = "security")]
298pub mod security;
299/// Data serialization framework.
300#[cfg(feature = "serializers")]
301pub mod serializers;
302/// Signal/event dispatch system.
303#[cfg(feature = "signals")]
304pub mod signals;
305/// Core type definitions.
306#[cfg(feature = "types")]
307pub mod types;
308/// Field and data validators.
309#[cfg(feature = "validators")]
310pub mod validators;
311/// WebSocket routing primitives shared across reinhardt crates.
312#[cfg(native)]
313pub mod ws;
314
315// Re-export Page types when page feature is enabled
316// This provides Page, PageElement, IntoPage, Head, EventType, etc.
317#[cfg(all(feature = "types", feature = "page"))]
318pub use crate::types::page;
319
320#[cfg(feature = "macros")]
321pub use reinhardt_macros as macros;
322
323// Re-export rate limiting types
324pub use crate::rate_limit::RateLimitStrategy;
325
326// Re-export common external dependencies
327pub use async_trait::async_trait;
328
329// Re-export tokio only on non-WASM targets
330#[cfg(native)]
331pub use tokio;
332
333/// Re-export of serde serialization types and serde_json.
334#[cfg(feature = "serde")]
335pub mod serde {
336 pub use ::serde::{Deserialize, Deserializer, Serialize, Serializer, de, ser};
337 pub use ::serde_json as json;
338}