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    ConfirmDialog, DashboardLayout, DashboardLayoutConfig, DescriptionItem, DescriptionListProps,
84    DialogVariant, Element, ElementBuilder, FormProps, HeaderProps, HttpMethod, IconPosition,
85    ImageProps, InputProps, InputType, JsonUiConfig, Layout, LayoutContext, LayoutRegistry,
86    ModalProps, NavItem, NotificationDropdownProps, NotificationItem, NotifyVariant, Orientation,
87    PaginationProps, ProgressProps, SelectOption, SelectProps, SeparatorProps, SidebarGroup,
88    SidebarNavItem, SidebarProps, SidebarSection, Size, SkeletonProps, SortDirection, Spec,
89    SpecBuilder, SpecError, StatCardProps, SwitchProps, Tab, TableProps, TabsProps, TextElement,
90    TextProps, ToastProps, ToastVariant, Visibility as JsonUiVisibility, VisibilityCondition,
91    VisibilityOperator, MAX_NESTING_DEPTH, 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, validate_mime, validate_size, Cookie, CookieOptions,
107    FormRequest, FromParam, FromRequest, HttpResponse, InertiaRedirect, MultipartForm,
108    PaginationLinks, PaginationMeta, Redirect, Request, Resource, ResourceCollection, ResourceMap,
109    Response, ResponseExt, SameSite, UploadedFile,
110};
111#[cfg(feature = "inertia")]
112pub use inertia::{Inertia, InertiaConfig, InertiaResponse, InertiaShared, SavedInertiaContext};
113#[cfg(feature = "json-ui")]
114pub use json_ui::JsonUi;
115pub use lang::{lang_choice, lang_init, locale, set_locale, t, trans, LangMiddleware};
116pub use sea_orm::{
117    ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, ModelTrait, PaginatorTrait,
118    QueryFilter, QueryOrder, QuerySelect,
119};
120pub use session::{
121    invalidate_all_for_user, session, session_mut, DatabaseSessionDriver, SessionConfig,
122    SessionData, SessionMiddleware, SessionStore,
123};
124#[cfg(feature = "stripe")]
125pub use tenant::RequiresPlan;
126pub use tenant::{
127    current_tenant, DbTenantLookup, FrameworkTenantScopeProvider, HeaderResolver, JwtClaimResolver,
128    PathResolver, SubdomainResolver, TenantContext, TenantFailureMode, TenantLookup,
129    TenantMiddleware, TenantResolver, TenantScope,
130};
131#[cfg(feature = "theme")]
132pub use theme::{
133    current_theme, DefaultResolver, HeaderThemeResolver, TenantThemeResolver, ThemeMiddleware,
134    ThemeResolver,
135};
136// Deprecated - kept for backward compatibility
137#[cfg(feature = "inertia")]
138#[allow(deprecated)]
139pub use inertia::InertiaContext;
140pub use metrics::{get_metrics, MetricsSnapshot, RouteMetrics, RouteMetricsView};
141pub use middleware::{
142    get_pre_route_middleware, register_global_middleware, register_pre_route_middleware,
143    rewrite_request_path, Cors, Limit, LimiterResponse, MetricsMiddleware, Middleware,
144    MiddlewareFuture, MiddlewareRegistry, Next, PreRouteMiddleware, PreRouteResult, RateLimiter,
145    SecurityHeaders, Throttle,
146};
147pub use routing::{
148    // Internal functions used by macros (hidden from docs)
149    __box_handler,
150    __delete_impl,
151    __fallback_impl,
152    __get_impl,
153    __patch_impl,
154    __post_impl,
155    __put_impl,
156    get_registered_routes,
157    route,
158    validate_route_path,
159    FallbackDefBuilder,
160    GroupBuilder,
161    GroupDef,
162    GroupItem,
163    GroupRoute,
164    GroupRouter,
165    IntoGroupItem,
166    ResourceAction,
167    ResourceDef,
168    ResourceRoute,
169    RouteBuilder,
170    RouteDefBuilder,
171    RouteInfo,
172    Router,
173};
174pub use schedule::{CronExpression, DayOfWeek, Schedule, Task, TaskBuilder, TaskEntry, TaskResult};
175pub use seeder::{DatabaseSeeder, Seeder, SeederRegistry};
176pub use server::Server;
177
178// Re-export ferro-events for event-driven architecture
179pub use ferro_events::{
180    dispatch as dispatch_event, dispatch_sync, Error as EventError, Event, EventDispatcher,
181    Listener, ShouldQueue,
182};
183
184// Re-export ferro-queue for background job processing
185pub use ferro_queue::{
186    dispatch as queue_dispatch, dispatch_later, dispatch_to, register_tenant_capture_hook,
187    Error as QueueError, Job, JobPayload, PendingDispatch, Queue, QueueConfig, QueueConnection,
188    Queueable, TenantScopeProvider, Worker, WorkerConfig,
189};
190
191// Re-export ferro-notifications for multi-channel notifications
192pub use ferro_notifications::{
193    Channel as NotificationChannel, ChannelResult, DatabaseMessage, DatabaseNotificationStore,
194    Error as NotificationError, InAppConfig, InAppMessage, InAppSeverity, MailAttachment,
195    MailConfig, MailDriver, MailMessage, Notifiable, Notification, NotificationConfig,
196    NotificationDispatcher, PushMessage, ResendConfig, SlackAttachment, SlackField, SlackMessage,
197    SmsMessage, SmtpConfig, StoredNotification, WhatsAppMessage,
198};
199
200// Re-export ferro-broadcast for real-time WebSocket channels
201pub use ferro_broadcast::{
202    AuthData, Broadcast, BroadcastBuilder, BroadcastConfig, BroadcastMessage, Broadcaster,
203    ChannelAuthorizer, ChannelInfo, ChannelType, Client as BroadcastClient, ClientMessage,
204    Error as BroadcastError, PresenceMember, ServerMessage,
205};
206
207// Re-export broadcasting auth handler
208pub use broadcast::broadcasting_auth;
209
210// Re-export ferro-storage for file storage abstraction
211pub use ferro_storage::{
212    Disk, DiskConfig, DiskDriver, Error as StorageError, FileMetadata, LocalDriver,
213    MemoryDriver as StorageMemoryDriver, PutOptions, Storage, StorageDriver, Visibility,
214};
215
216// Re-export ferro-cache for caching with tags
217pub use ferro_cache::{
218    Cache as TaggableCache, CacheConfig as TaggableCacheConfig, CacheStore as TaggableCacheStore,
219    Error as TaggableCacheError, MemoryStore as TaggableCacheMemoryStore, TaggedCache,
220};
221
222// Re-export ferro-lang for localization
223pub use ferro_lang::{LangError, Translator};
224
225// Re-export ferro-ai for AI classification and confirmation primitives
226#[cfg(feature = "ai")]
227pub use ferro_ai::{
228    AnthropicProvider, ClassificationProvider, ClassificationResult, Classifier, ClassifierConfig,
229    ConfirmationExpired, ConfirmationStore, Error as AiError, InMemoryConfirmationStore,
230    PendingActionInfo,
231};
232
233// Re-export ferro-whatsapp for WhatsApp Business Cloud API integration
234#[cfg(feature = "whatsapp")]
235pub use ferro_whatsapp::{
236    verify_whatsapp_webhook, DeduplicationStore, DeliveryStatus, Error as WhatsAppError,
237    InMemoryDeduplicationStore, Message as WhatsAppRawMessage, ProcessWhatsAppWebhook,
238    SendResult as WhatsAppSendResult, SenderIdentity, WhatsApp, WhatsAppConfig,
239    WhatsAppStatusUpdate, WhatsAppTextReceived,
240};
241
242// Re-export ferro-projections for service projection definitions
243#[cfg(feature = "projections")]
244pub use ferro_projections::{
245    derive_intents, infer_meaning, ActionDef, Cardinality, DataType, Error as ProjectionsError,
246    FieldDef, FieldMeaning, GuardDef, InputDef, Intent, IntentHint, IntentScore, NavigationHint,
247    RelationshipDef, Renderer, ServiceDef, StateDef, StateMachine, Transition,
248    Warning as ProjectionsWarning,
249};
250// Re-export visual renderer types from ferro-json-ui
251#[cfg(feature = "projections")]
252pub use ferro_json_ui::{JsonUiRenderer, RenderMode, VisualContext};
253
254// Re-export async_trait for middleware implementations
255pub use async_trait::async_trait;
256
257// Re-export inventory for #[service(ConcreteType)] macro
258#[doc(hidden)]
259pub use inventory;
260
261// Re-export for macro usage
262#[doc(hidden)]
263pub use serde_json;
264
265// Re-export serde for InertiaProps derive macro
266pub use serde;
267
268// Re-export validator crate for derive-based validation
269pub use validator;
270pub use validator::Validate;
271
272// Re-export our Laravel-style validation module
273pub use validation::{
274    // Rules
275    accepted,
276    alpha,
277    alpha_dash,
278    alpha_num,
279    array,
280    between,
281    boolean,
282    confirmed,
283    date,
284    different,
285    email,
286    in_array,
287    integer,
288    max,
289    min,
290    not_in,
291    nullable,
292    numeric,
293    regex,
294    // Bridge
295    register_validation_translator,
296    required,
297    required_if,
298    same,
299    string,
300    url,
301    validate,
302    Rule,
303    TranslatorFn,
304    Validatable,
305    ValidationError,
306    Validator,
307};
308
309// Re-export the proc-macros for compile-time component validation and type safety
310pub use ferro_macros::domain_error;
311pub use ferro_macros::ferro_test;
312pub use ferro_macros::handler;
313pub use ferro_macros::inertia_response;
314pub use ferro_macros::injectable;
315pub use ferro_macros::redirect;
316pub use ferro_macros::request;
317pub use ferro_macros::service;
318pub use ferro_macros::ApiResource;
319pub use ferro_macros::FerroModel;
320pub use ferro_macros::FormRequest as FormRequestDerive;
321pub use ferro_macros::InertiaProps;
322pub use ferro_macros::ValidateRules;
323
324// Re-export Jest-like testing macros
325pub use ferro_macros::describe;
326pub use ferro_macros::test;
327
328// Re-export testing utilities
329pub use testing::{
330    Factory, FactoryBuilder, Fake, Sequence, TestClient, TestContainer, TestContainerGuard,
331    TestDatabase, TestRequestBuilder, TestResponse,
332};
333
334/// Return a JSON response from a handler using `serde_json::json!` syntax.
335///
336/// # Example
337///
338/// ```rust,ignore
339/// use ferro_rs::json_response;
340///
341/// pub async fn index() -> Response {
342///     json_response!({ "status": "ok" })
343/// }
344/// ```
345#[macro_export]
346macro_rules! json_response {
347    ($($json:tt)+) => {
348        Ok($crate::HttpResponse::json($crate::serde_json::json!($($json)+)))
349    };
350}
351
352/// Return a plain-text response from a handler.
353///
354/// # Example
355///
356/// ```rust,ignore
357/// use ferro_rs::text_response;
358///
359/// pub async fn ping() -> Response {
360///     text_response!("pong")
361/// }
362/// ```
363#[macro_export]
364macro_rules! text_response {
365    ($text:expr) => {
366        Ok($crate::HttpResponse::text($text))
367    };
368}
369
370/// Register global middleware that runs on every request
371///
372/// Global middleware is registered in `bootstrap.rs` and runs in registration order,
373/// before any route-specific middleware.
374///
375/// # Example
376///
377/// ```rust,ignore
378/// // In bootstrap.rs
379/// use ferro_rs::global_middleware;
380/// use ferro_rs::middleware;
381///
382/// pub fn register() {
383///     global_middleware!(middleware::LoggingMiddleware);
384///     global_middleware!(middleware::CorsMiddleware);
385/// }
386/// ```
387#[macro_export]
388macro_rules! global_middleware {
389    ($middleware:expr) => {
390        $crate::register_global_middleware($middleware)
391    };
392}
393
394/// Register a pre-route middleware that runs before path extraction and route matching.
395///
396/// Pre-route middleware operates on the raw hyper request and can rewrite the path
397/// (via `rewrite_request_path`) before the router selects a handler. Use this for
398/// host-based routing, path aliasing, or any rewrite that must influence which
399/// route is matched. Runs in registration order, before standard global middleware.
400///
401/// # Example
402///
403/// ```rust,ignore
404/// // In bootstrap.rs
405/// pre_route_middleware!(middleware::host::HostMiddleware::new());
406/// ```
407#[macro_export]
408macro_rules! pre_route_middleware {
409    ($middleware:expr) => {
410        $crate::register_pre_route_middleware($middleware)
411    };
412}
413
414/// Create an expectation for fluent assertions
415///
416/// # Example
417///
418/// ```rust,ignore
419/// use ferro_rs::expect;
420///
421/// expect!(actual).to_equal(expected);
422/// expect!(result).to_be_ok();
423/// expect!(vec).to_have_length(3);
424/// ```
425///
426/// On failure, shows clear output:
427/// ```text
428/// Test: "returns all todos"
429///   at src/actions/todo_action.rs:25
430///
431///   expect!(actual).to_equal(expected)
432///
433///   Expected: 0
434///   Received: 3
435/// ```
436#[macro_export]
437macro_rules! expect {
438    ($value:expr) => {
439        $crate::testing::Expect::new($value, concat!(file!(), ":", line!()))
440    };
441}