Skip to main content

ferro_rs/
lib.rs

1//! Ferro — a full-stack web framework for Rust.
2//!
3//! Provides routing, database access, validation, authentication, queues,
4//! events, notifications, broadcasting, storage, caching, and Inertia.js
5//! integration in a single cohesive package.
6#![warn(missing_docs)]
7
8/// API key management and OpenAPI specification generation.
9pub mod api;
10pub mod app;
11pub mod auth;
12pub mod authorization;
13pub mod broadcast;
14pub mod cache;
15pub mod config;
16pub mod container;
17pub mod csrf;
18pub mod database;
19pub mod debug;
20pub mod error;
21pub mod hashing;
22/// HTTP request, response, cookie, and resource types.
23pub mod http;
24#[cfg(feature = "inertia")]
25pub mod inertia;
26#[cfg(feature = "json-ui")]
27pub mod json_ui;
28pub mod lang;
29pub mod metrics;
30pub mod middleware;
31/// Route definition and registration.
32pub mod routing;
33pub mod schedule;
34pub mod seeder;
35/// HTTP server builder and runner.
36pub mod server;
37pub mod session;
38pub(crate) mod static_files;
39pub mod tenant;
40pub mod testing;
41#[cfg(feature = "theme")]
42pub mod theme;
43pub mod validation;
44mod websocket;
45
46pub use api::api_key::{
47    generate_api_key, hash_api_key, verify_api_key_hash, ApiKeyInfo, ApiKeyMiddleware,
48    ApiKeyProvider, GeneratedApiKey,
49};
50pub use api::openapi::{
51    build_openapi_spec, openapi_docs_response, openapi_json_response, OpenApiConfig,
52};
53
54pub use app::Application;
55pub use auth::{
56    Auth, AuthMiddleware, AuthUser, Authenticatable, GuestMiddleware, OptionalUser, UserProvider,
57};
58pub use authorization::{AuthResponse, Authorizable, AuthorizationError, Authorize, Gate, Policy};
59pub use cache::{Cache, CacheConfig, CacheStore, InMemoryCache, RedisCache};
60pub use config::{
61    env, env_optional, env_required, AppConfig, Config, Environment, LangConfig, LangConfigBuilder,
62    ServerConfig,
63};
64pub use container::{App, Container};
65pub use csrf::{csrf_field, csrf_meta_tag, csrf_token, CsrfMiddleware};
66pub use database::{
67    AutoRouteBinding, Database, DatabaseConfig, DatabaseType, DbConnection, Model, ModelMut,
68    RouteBinding, DB,
69};
70// Re-export utoipa and utoipa-redoc for advanced OpenAPI customization
71pub use utoipa;
72pub use utoipa_redoc;
73
74// Re-export commonly used SeaORM traits for convenience
75// This saves users from having to add `use sea_orm::*` imports
76pub use error::{AppError, FrameworkError, HttpError, ValidationErrors};
77#[cfg(feature = "json-ui")]
78pub use ferro_json_ui::{
79    resolve_actions, resolve_actions_strict, resolve_errors, resolve_errors_all, Action,
80    ActionCardProps, ActionCardVariant, ActionOutcome, AlertProps, AlertVariant, AvatarProps,
81    BadgeProps, BadgeVariant, BreadcrumbItem, BreadcrumbProps, ButtonProps, ButtonType,
82    ButtonVariant, CardProps, CheckboxProps, ChecklistItem, ChecklistProps, Column, ColumnFormat,
83    Component, ComponentNode, ConfirmDialog, DashboardLayout, DashboardLayoutConfig,
84    DescriptionItem, DescriptionListProps, DialogVariant, FormProps, HeaderProps, HttpMethod,
85    IconPosition, ImageProps, InputProps, InputType, JsonUiConfig, JsonUiView, KeyValueEditorProps,
86    Layout, LayoutContext, LayoutRegistry, ModalProps, NavItem, NotificationDropdownProps,
87    NotificationItem, NotifyVariant, Orientation, PaginationProps, ProgressProps, SelectOption,
88    SelectProps, SeparatorProps, SidebarGroup, SidebarNavItem, SidebarProps, SidebarSection, Size,
89    SkeletonProps, SortDirection, StatCardProps, SwitchProps, Tab, TableProps, TabsProps,
90    TextElement, TextProps, ToastProps, ToastVariant, Visibility as JsonUiVisibility,
91    VisibilityCondition, VisibilityOperator, SCHEMA_VERSION,
92};
93#[cfg(feature = "stripe")]
94pub use ferro_stripe::{
95    account, checkout, refund, verify_webhook, CheckoutBuilder, CheckoutIntent,
96    Error as StripeError, LineItem, MemoryProcessedLog, Mode, ProcessStripeWebhook,
97    ProcessedEventLog, Stripe, StripeChargeDisputeCreated, StripeChargeRefunded,
98    StripeCheckoutCompleted, StripeCheckoutExpired, StripeConfig, StripeConnectAccountUpdated,
99    StripeConnectPaymentSucceeded, StripeEvent, StripeInvoicePaid, StripePaymentIntentFailed,
100    StripeSubscriptionDeleted, StripeSubscriptionUpdated, SyncDispatcher,
101};
102#[cfg(feature = "theme")]
103pub use ferro_theme::{IntentModeTemplates, IntentSlotTemplate, Theme, ThemeError, ThemeTemplates};
104pub use hashing::{hash, needs_rehash, verify, DEFAULT_COST as HASH_DEFAULT_COST};
105pub use http::{
106    bytes, json, request_host, text, Cookie, CookieOptions, FormRequest, FromParam, FromRequest,
107    HttpResponse, InertiaRedirect, PaginationLinks, PaginationMeta, Redirect, Request, Resource,
108    ResourceCollection, ResourceMap, Response, ResponseExt, SameSite,
109};
110#[cfg(feature = "inertia")]
111pub use inertia::{Inertia, InertiaConfig, InertiaResponse, InertiaShared, SavedInertiaContext};
112#[cfg(feature = "json-ui")]
113pub use json_ui::JsonUi;
114pub use lang::{lang_choice, lang_init, locale, set_locale, t, trans, LangMiddleware};
115pub use sea_orm::{
116    ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, ModelTrait, PaginatorTrait,
117    QueryFilter, QueryOrder, QuerySelect,
118};
119pub use session::{
120    invalidate_all_for_user, session, session_mut, DatabaseSessionDriver, SessionConfig,
121    SessionData, SessionMiddleware, SessionStore,
122};
123#[cfg(feature = "stripe")]
124pub use tenant::RequiresPlan;
125pub use tenant::{
126    current_tenant, DbTenantLookup, FrameworkTenantScopeProvider, HeaderResolver, JwtClaimResolver,
127    PathResolver, SubdomainResolver, TenantContext, TenantFailureMode, TenantLookup,
128    TenantMiddleware, TenantResolver, TenantScope,
129};
130#[cfg(feature = "theme")]
131pub use theme::{
132    current_theme, DefaultResolver, HeaderThemeResolver, TenantThemeResolver, ThemeMiddleware,
133    ThemeResolver,
134};
135// Deprecated - kept for backward compatibility
136#[cfg(feature = "inertia")]
137#[allow(deprecated)]
138pub use inertia::InertiaContext;
139pub use metrics::{get_metrics, MetricsSnapshot, RouteMetrics, RouteMetricsView};
140pub use middleware::{
141    register_global_middleware, register_pre_route_middleware, Cors, Limit, LimiterResponse,
142    MetricsMiddleware, Middleware, MiddlewareFuture, MiddlewareRegistry, Next, PreRouteMiddleware,
143    RateLimiter, SecurityHeaders, Throttle,
144};
145pub use routing::{
146    // Internal functions used by macros (hidden from docs)
147    __box_handler,
148    __delete_impl,
149    __fallback_impl,
150    __get_impl,
151    __patch_impl,
152    __post_impl,
153    __put_impl,
154    get_registered_routes,
155    route,
156    validate_route_path,
157    FallbackDefBuilder,
158    GroupBuilder,
159    GroupDef,
160    GroupItem,
161    GroupRoute,
162    GroupRouter,
163    IntoGroupItem,
164    ResourceAction,
165    ResourceDef,
166    ResourceRoute,
167    RouteBuilder,
168    RouteDefBuilder,
169    RouteInfo,
170    Router,
171};
172pub use schedule::{CronExpression, DayOfWeek, Schedule, Task, TaskBuilder, TaskEntry, TaskResult};
173pub use seeder::{DatabaseSeeder, Seeder, SeederRegistry};
174pub use server::Server;
175
176// Re-export ferro-events for event-driven architecture
177pub use ferro_events::{
178    dispatch as dispatch_event, dispatch_sync, Error as EventError, Event, EventDispatcher,
179    Listener, ShouldQueue,
180};
181
182// Re-export ferro-queue for background job processing
183pub use ferro_queue::{
184    dispatch as queue_dispatch, dispatch_later, dispatch_to, register_tenant_capture_hook,
185    Error as QueueError, Job, JobPayload, PendingDispatch, Queue, QueueConfig, QueueConnection,
186    Queueable, TenantScopeProvider, Worker, WorkerConfig,
187};
188
189// Re-export ferro-notifications for multi-channel notifications
190pub use ferro_notifications::{
191    Channel as NotificationChannel, ChannelResult, DatabaseMessage, DatabaseNotificationStore,
192    Error as NotificationError, InAppConfig, InAppMessage, InAppSeverity, MailAttachment,
193    MailConfig, MailDriver, MailMessage, Notifiable, Notification, NotificationConfig,
194    NotificationDispatcher, PushMessage, ResendConfig, SlackAttachment, SlackField, SlackMessage,
195    SmsMessage, SmtpConfig, StoredNotification, WhatsAppMessage,
196};
197
198// Re-export ferro-broadcast for real-time WebSocket channels
199pub use ferro_broadcast::{
200    AuthData, Broadcast, BroadcastBuilder, BroadcastConfig, BroadcastMessage, Broadcaster,
201    ChannelAuthorizer, ChannelInfo, ChannelType, Client as BroadcastClient, ClientMessage,
202    Error as BroadcastError, PresenceMember, ServerMessage,
203};
204
205// Re-export broadcasting auth handler
206pub use broadcast::broadcasting_auth;
207
208// Re-export ferro-storage for file storage abstraction
209pub use ferro_storage::{
210    Disk, DiskConfig, DiskDriver, Error as StorageError, FileMetadata, LocalDriver,
211    MemoryDriver as StorageMemoryDriver, PutOptions, Storage, StorageDriver, Visibility,
212};
213
214// Re-export ferro-cache for caching with tags
215pub use ferro_cache::{
216    Cache as TaggableCache, CacheConfig as TaggableCacheConfig, CacheStore as TaggableCacheStore,
217    Error as TaggableCacheError, MemoryStore as TaggableCacheMemoryStore, TaggedCache,
218};
219
220// Re-export ferro-lang for localization
221pub use ferro_lang::{LangError, Translator};
222
223// Re-export ferro-ai for AI classification and confirmation primitives
224#[cfg(feature = "ai")]
225pub use ferro_ai::{
226    AnthropicProvider, ClassificationProvider, ClassificationResult, Classifier, ClassifierConfig,
227    ConfirmationExpired, ConfirmationStore, Error as AiError, InMemoryConfirmationStore,
228    PendingActionInfo,
229};
230
231// Re-export ferro-whatsapp for WhatsApp Business Cloud API integration
232#[cfg(feature = "whatsapp")]
233pub use ferro_whatsapp::{
234    verify_whatsapp_webhook, DeduplicationStore, DeliveryStatus, Error as WhatsAppError,
235    InMemoryDeduplicationStore, Message as WhatsAppRawMessage, ProcessWhatsAppWebhook,
236    SendResult as WhatsAppSendResult, SenderIdentity, WhatsApp, WhatsAppConfig,
237    WhatsAppStatusUpdate, WhatsAppTextReceived,
238};
239
240// Re-export ferro-projections for service projection definitions
241#[cfg(feature = "projections")]
242pub use ferro_projections::{
243    derive_intents, infer_meaning, ActionDef, Cardinality, DataType, Error as ProjectionsError,
244    FieldDef, FieldMeaning, GuardDef, InputDef, Intent, IntentHint, IntentScore, NavigationHint,
245    RelationshipDef, Renderer, ServiceDef, StateDef, StateMachine, Transition,
246    Warning as ProjectionsWarning,
247};
248// Re-export visual renderer types from ferro-json-ui
249#[cfg(feature = "projections")]
250pub use ferro_json_ui::{JsonUiRenderer, RenderMode, VisualContext};
251
252// Re-export async_trait for middleware implementations
253pub use async_trait::async_trait;
254
255// Re-export inventory for #[service(ConcreteType)] macro
256#[doc(hidden)]
257pub use inventory;
258
259// Re-export for macro usage
260#[doc(hidden)]
261pub use serde_json;
262
263// Re-export serde for InertiaProps derive macro
264pub use serde;
265
266// Re-export validator crate for derive-based validation
267pub use validator;
268pub use validator::Validate;
269
270// Re-export our Laravel-style validation module
271pub use validation::{
272    // Rules
273    accepted,
274    alpha,
275    alpha_dash,
276    alpha_num,
277    array,
278    between,
279    boolean,
280    confirmed,
281    date,
282    different,
283    email,
284    in_array,
285    integer,
286    max,
287    min,
288    not_in,
289    nullable,
290    numeric,
291    regex,
292    // Bridge
293    register_validation_translator,
294    required,
295    required_if,
296    same,
297    string,
298    url,
299    validate,
300    Rule,
301    TranslatorFn,
302    Validatable,
303    ValidationError,
304    Validator,
305};
306
307// Re-export the proc-macros for compile-time component validation and type safety
308pub use ferro_macros::domain_error;
309pub use ferro_macros::ferro_test;
310pub use ferro_macros::handler;
311pub use ferro_macros::inertia_response;
312pub use ferro_macros::injectable;
313pub use ferro_macros::redirect;
314pub use ferro_macros::request;
315pub use ferro_macros::service;
316pub use ferro_macros::ApiResource;
317pub use ferro_macros::FerroModel;
318pub use ferro_macros::FormRequest as FormRequestDerive;
319pub use ferro_macros::InertiaProps;
320pub use ferro_macros::ValidateRules;
321
322// Re-export Jest-like testing macros
323pub use ferro_macros::describe;
324pub use ferro_macros::test;
325
326// Re-export testing utilities
327pub use testing::{
328    Factory, FactoryBuilder, Fake, Sequence, TestClient, TestContainer, TestContainerGuard,
329    TestDatabase, TestRequestBuilder, TestResponse,
330};
331
332/// Return a JSON response from a handler using `serde_json::json!` syntax.
333///
334/// # Example
335///
336/// ```rust,ignore
337/// use ferro_rs::json_response;
338///
339/// pub async fn index() -> Response {
340///     json_response!({ "status": "ok" })
341/// }
342/// ```
343#[macro_export]
344macro_rules! json_response {
345    ($($json:tt)+) => {
346        Ok($crate::HttpResponse::json($crate::serde_json::json!($($json)+)))
347    };
348}
349
350/// Return a plain-text response from a handler.
351///
352/// # Example
353///
354/// ```rust,ignore
355/// use ferro_rs::text_response;
356///
357/// pub async fn ping() -> Response {
358///     text_response!("pong")
359/// }
360/// ```
361#[macro_export]
362macro_rules! text_response {
363    ($text:expr) => {
364        Ok($crate::HttpResponse::text($text))
365    };
366}
367
368/// Register global middleware that runs on every request
369///
370/// Global middleware is registered in `bootstrap.rs` and runs in registration order,
371/// before any route-specific middleware.
372///
373/// # Example
374///
375/// ```rust,ignore
376/// // In bootstrap.rs
377/// use ferro_rs::global_middleware;
378/// use ferro_rs::middleware;
379///
380/// pub fn register() {
381///     global_middleware!(middleware::LoggingMiddleware);
382///     global_middleware!(middleware::CorsMiddleware);
383/// }
384/// ```
385#[macro_export]
386macro_rules! global_middleware {
387    ($middleware:expr) => {
388        $crate::register_global_middleware($middleware)
389    };
390}
391
392/// Register a pre-route middleware that runs before route matching on every request.
393///
394/// Pre-route middleware can call `request.set_path(new_path)` to rewrite the path
395/// before the router selects a handler. Use this for host-based routing, path
396/// aliasing, or any rewrite that must influence which route is matched.
397///
398/// Pre-route middleware runs in registration order, before standard global middleware.
399///
400/// # Example
401///
402/// ```rust,ignore
403/// // In bootstrap.rs
404/// use ferro_rs::pre_route_middleware;
405///
406/// pub fn register() {
407///     pre_route_middleware!(HostMiddleware::new());
408/// }
409/// ```
410#[macro_export]
411macro_rules! pre_route_middleware {
412    ($middleware:expr) => {
413        $crate::register_pre_route_middleware($middleware)
414    };
415}
416
417/// Create an expectation for fluent assertions
418///
419/// # Example
420///
421/// ```rust,ignore
422/// use ferro_rs::expect;
423///
424/// expect!(actual).to_equal(expected);
425/// expect!(result).to_be_ok();
426/// expect!(vec).to_have_length(3);
427/// ```
428///
429/// On failure, shows clear output:
430/// ```text
431/// Test: "returns all todos"
432///   at src/actions/todo_action.rs:25
433///
434///   expect!(actual).to_equal(expected)
435///
436///   Expected: 0
437///   Received: 3
438/// ```
439#[macro_export]
440macro_rules! expect {
441    ($value:expr) => {
442        $crate::testing::Expect::new($value, concat!(file!(), ":", line!()))
443    };
444}