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, Layout,
86    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, text, Cookie, CookieOptions, FormRequest, FromParam, FromRequest, HttpResponse,
107    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, 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, MailConfig, MailDriver, MailMessage, Notifiable, Notification,
193    NotificationConfig, NotificationDispatcher, ResendConfig, SlackAttachment, SlackField,
194    SlackMessage, SmtpConfig, StoredNotification,
195};
196
197// Re-export ferro-broadcast for real-time WebSocket channels
198pub use ferro_broadcast::{
199    AuthData, Broadcast, BroadcastBuilder, BroadcastConfig, BroadcastMessage, Broadcaster,
200    ChannelAuthorizer, ChannelInfo, ChannelType, Client as BroadcastClient, ClientMessage,
201    Error as BroadcastError, PresenceMember, ServerMessage,
202};
203
204// Re-export broadcasting auth handler
205pub use broadcast::broadcasting_auth;
206
207// Re-export ferro-storage for file storage abstraction
208pub use ferro_storage::{
209    Disk, DiskConfig, DiskDriver, Error as StorageError, FileMetadata, LocalDriver,
210    MemoryDriver as StorageMemoryDriver, PutOptions, Storage, StorageDriver, Visibility,
211};
212
213// Re-export ferro-cache for caching with tags
214pub use ferro_cache::{
215    Cache as TaggableCache, CacheConfig as TaggableCacheConfig, CacheStore as TaggableCacheStore,
216    Error as TaggableCacheError, MemoryStore as TaggableCacheMemoryStore, TaggedCache,
217};
218
219// Re-export ferro-lang for localization
220pub use ferro_lang::{LangError, Translator};
221
222// Re-export ferro-ai for AI classification and confirmation primitives
223#[cfg(feature = "ai")]
224pub use ferro_ai::{
225    AnthropicProvider, ClassificationProvider, ClassificationResult, Classifier, ClassifierConfig,
226    ConfirmationExpired, ConfirmationStore, Error as AiError, InMemoryConfirmationStore,
227    PendingActionInfo,
228};
229
230// Re-export ferro-whatsapp for WhatsApp Business Cloud API integration
231#[cfg(feature = "whatsapp")]
232pub use ferro_whatsapp::{
233    verify_whatsapp_webhook, DeduplicationStore, DeliveryStatus, Error as WhatsAppError,
234    InMemoryDeduplicationStore, Message as WhatsAppMessage, ProcessWhatsAppWebhook,
235    SendResult as WhatsAppSendResult, SenderIdentity, WhatsApp, WhatsAppConfig,
236    WhatsAppStatusUpdate, WhatsAppTextReceived,
237};
238
239// Re-export ferro-projections for service projection definitions
240#[cfg(feature = "projections")]
241pub use ferro_projections::{
242    derive_intents, infer_meaning, ActionDef, Cardinality, DataType, Error as ProjectionsError,
243    FieldDef, FieldMeaning, GuardDef, InputDef, Intent, IntentHint, IntentScore, NavigationHint,
244    RelationshipDef, Renderer, ServiceDef, StateDef, StateMachine, Transition,
245    Warning as ProjectionsWarning,
246};
247// Re-export visual renderer types from ferro-json-ui
248#[cfg(feature = "projections")]
249pub use ferro_json_ui::{JsonUiRenderer, RenderMode, VisualContext};
250
251// Re-export async_trait for middleware implementations
252pub use async_trait::async_trait;
253
254// Re-export inventory for #[service(ConcreteType)] macro
255#[doc(hidden)]
256pub use inventory;
257
258// Re-export for macro usage
259#[doc(hidden)]
260pub use serde_json;
261
262// Re-export serde for InertiaProps derive macro
263pub use serde;
264
265// Re-export validator crate for derive-based validation
266pub use validator;
267pub use validator::Validate;
268
269// Re-export our Laravel-style validation module
270pub use validation::{
271    // Rules
272    accepted,
273    alpha,
274    alpha_dash,
275    alpha_num,
276    array,
277    between,
278    boolean,
279    confirmed,
280    date,
281    different,
282    email,
283    in_array,
284    integer,
285    max,
286    min,
287    not_in,
288    nullable,
289    numeric,
290    regex,
291    // Bridge
292    register_validation_translator,
293    required,
294    required_if,
295    same,
296    string,
297    url,
298    validate,
299    Rule,
300    TranslatorFn,
301    Validatable,
302    ValidationError,
303    Validator,
304};
305
306// Re-export the proc-macros for compile-time component validation and type safety
307pub use ferro_macros::domain_error;
308pub use ferro_macros::ferro_test;
309pub use ferro_macros::handler;
310pub use ferro_macros::inertia_response;
311pub use ferro_macros::injectable;
312pub use ferro_macros::redirect;
313pub use ferro_macros::request;
314pub use ferro_macros::service;
315pub use ferro_macros::ApiResource;
316pub use ferro_macros::FerroModel;
317pub use ferro_macros::FormRequest as FormRequestDerive;
318pub use ferro_macros::InertiaProps;
319pub use ferro_macros::ValidateRules;
320
321// Re-export Jest-like testing macros
322pub use ferro_macros::describe;
323pub use ferro_macros::test;
324
325// Re-export testing utilities
326pub use testing::{
327    Factory, FactoryBuilder, Fake, Sequence, TestClient, TestContainer, TestContainerGuard,
328    TestDatabase, TestRequestBuilder, TestResponse,
329};
330
331/// Return a JSON response from a handler using `serde_json::json!` syntax.
332///
333/// # Example
334///
335/// ```rust,ignore
336/// use ferro_rs::json_response;
337///
338/// pub async fn index() -> Response {
339///     json_response!({ "status": "ok" })
340/// }
341/// ```
342#[macro_export]
343macro_rules! json_response {
344    ($($json:tt)+) => {
345        Ok($crate::HttpResponse::json($crate::serde_json::json!($($json)+)))
346    };
347}
348
349/// Return a plain-text response from a handler.
350///
351/// # Example
352///
353/// ```rust,ignore
354/// use ferro_rs::text_response;
355///
356/// pub async fn ping() -> Response {
357///     text_response!("pong")
358/// }
359/// ```
360#[macro_export]
361macro_rules! text_response {
362    ($text:expr) => {
363        Ok($crate::HttpResponse::text($text))
364    };
365}
366
367/// Register global middleware that runs on every request
368///
369/// Global middleware is registered in `bootstrap.rs` and runs in registration order,
370/// before any route-specific middleware.
371///
372/// # Example
373///
374/// ```rust,ignore
375/// // In bootstrap.rs
376/// use ferro_rs::global_middleware;
377/// use ferro_rs::middleware;
378///
379/// pub fn register() {
380///     global_middleware!(middleware::LoggingMiddleware);
381///     global_middleware!(middleware::CorsMiddleware);
382/// }
383/// ```
384#[macro_export]
385macro_rules! global_middleware {
386    ($middleware:expr) => {
387        $crate::register_global_middleware($middleware)
388    };
389}
390
391/// Register a pre-route middleware that runs before route matching on every request.
392///
393/// Pre-route middleware can call `request.set_path(new_path)` to rewrite the path
394/// before the router selects a handler. Use this for host-based routing, path
395/// aliasing, or any rewrite that must influence which route is matched.
396///
397/// Pre-route middleware runs in registration order, before standard global middleware.
398///
399/// # Example
400///
401/// ```rust,ignore
402/// // In bootstrap.rs
403/// use ferro_rs::pre_route_middleware;
404///
405/// pub fn register() {
406///     pre_route_middleware!(HostMiddleware::new());
407/// }
408/// ```
409#[macro_export]
410macro_rules! pre_route_middleware {
411    ($middleware:expr) => {
412        $crate::register_pre_route_middleware($middleware)
413    };
414}
415
416/// Create an expectation for fluent assertions
417///
418/// # Example
419///
420/// ```rust,ignore
421/// use ferro_rs::expect;
422///
423/// expect!(actual).to_equal(expected);
424/// expect!(result).to_be_ok();
425/// expect!(vec).to_have_length(3);
426/// ```
427///
428/// On failure, shows clear output:
429/// ```text
430/// Test: "returns all todos"
431///   at src/actions/todo_action.rs:25
432///
433///   expect!(actual).to_equal(expected)
434///
435///   Expected: 0
436///   Received: 3
437/// ```
438#[macro_export]
439macro_rules! expect {
440    ($value:expr) => {
441        $crate::testing::Expect::new($value, concat!(file!(), ":", line!()))
442    };
443}