wordval 1.0.4

Wordval: A fast, simple, and productive Rust web framework - config-driven, plugin-extensible, batteries included
//! # Wordval —— 快、简、美的 Rust Web 框架
//!
//! 配置驱动,一行启动,零成本抽象。基于 axum 构建,提供路由宏、
//! 中间件、数据库 CRUD、缓存、文件存储、插件系统与 Agent 能力。
//!
//! # 快速开始
//!
//! ```ignore
//! use wordval::prelude::*;
//!
//! #[tokio::main]
//! async fn main() -> wordval::Result<()> {
//!     App::from_config()?   // 从 config/config.toml 自动加载
//!         .get("/", || async { Res::ok("Hello, Wordval!") })
//!         .parse_cli()      // 支持 gen-config 生成配置文件
//!         .serve("8080")
//!         .await
//! }
//! ```
//!
//! # 功能总览
//!
//! - **路由与处理器**:`#[get]` / `#[post]` / `#[put]` / `#[delete]` 属性宏,
//!   配合 `Path` / `Query` / `Json` / `ValidatedJson` 提取器;
//! - **统一响应**:`Res<T>` + `ResResult<T>`,业务码与 HTTP 状态码解耦;
//! - **会话认证**:基于 session 的登录态,`CurrentUser` / `OptionalUser` 提取器,
//!   内置 `nonce`(防重放)、限流、安全响应头等中间件;
//! - **数据库**:Row 式 CRUD 查询构造器(`Q::table`)、事务、钩子、SQL 模板、迁移;
//! - **缓存**:本地内存缓存 + Redis 统一抽象(`Cache` trait / `SharedCache`),
//!   配合 `#[cached]` 宏与 `#[idempotent]` 幂等宏;
//! - **文件存储**:`LocalFs` 本地沙箱存储(自动 MIME 推断);
//! - **插件系统**:配置驱动的插件生命周期,内置缓存、调度器;
//! - **Agent**:ReasonAct 循环、工具系统、MCP、内存、沙箱执行(feature 启用);
//!   `#[tools]` 宏把普通 API handler 注册为 MCP 工具(`McpServer` 端点暴露);
//! - **WebSocket / IoT / Actor / 队列 / Foxglove**:由独立 crate 提供(见下表)。
//!
//! # 特性开关(feature)
//!
//! | feature | 启用内容 |
//! |---|---|
//! | `db` | 数据库层(`wordval-db`) |
//! | `cache` | 缓存层(`wordval-cache`)与幂等/防重放中间件 |
//! | `fs` | 文件存储(`wordval-fs`) |
//! | `plugin` | 内置插件(scheduler、cache) |
//! | `agent` | Agent 框架(`wordval-agent`) |
//! | `template` | Tera 模板引擎 |
//! | `config` | TOML 配置加载(默认随 `full` 启用) |
//!
//! # 模块导览
//!
//! 框架拆分为多个独立 crate,可按需依赖:
//!
//! - [wordval-core](https://docs.rs/wordval-core):应用生命周期、插件系统、错误与响应类型
//! - [wordval-web](https://docs.rs/wordval-web):路由、中间件、提取器、会话认证
//! - [wordval-macros](https://docs.rs/wordval-macros):路由 / 控制器 / 插件属性宏
//! - [wordval-db](https://docs.rs/wordval-db):Row 式 CRUD 与事务
//! - [wordval-cache](https://docs.rs/wordval-cache):本地内存 + Redis 缓存抽象
//! - [wordval-config](https://docs.rs/wordval-config):TOML 配置、多环境 profile
//! - [wordval-utils](https://docs.rs/wordval-utils):字符串、日期、加密、校验等工具
//! - [wordval-scheduler](https://docs.rs/wordval-scheduler):定时任务插件
//! - [wordval-fs](https://docs.rs/wordval-fs):文件存储后端
//! - [wordval-ws](https://docs.rs/wordval-ws):WebSocket 房间/会话,
//!   内建 Foxglove WebSocket 服务端(feature `foxglove`)
//! - [wordval-queue](https://docs.rs/wordval-queue):本地 / NATS 队列
//! - [wordval-nats](https://docs.rs/wordval-nats):NATS 集成
//! - [wordval-actor](https://docs.rs/wordval-actor):Actor 模型
//! - [wordval-iot](https://docs.rs/wordval-iot):MQTT / TCP / Modbus 采集插件
//! - [wordval-foxglove](https://docs.rs/wordval-foxglove):Foxglove WebSocket 协议客户端
//! - [wordval-foxglove-protocol](https://docs.rs/wordval-foxglove-protocol):
//!   Foxglove 协议编解码层(客户端 / 服务端共享)
//! - [wordval-agent](https://docs.rs/wordval-agent):Agent 框架

pub mod prelude {
    pub use wordval_core::{
        codes, ApiError, Error, PageData, PageQuery, Plugin, PluginManager, Res, ResResult, Result,
    };
    pub use wordval_web::resources::{cfg, config, set_config, try_config};

    #[cfg(feature = "db")]
    pub use wordval_web::resources::{
        db, db_default, db_names, register_db, remove_db, set_db, set_dbs, try_db, try_using,
        DbHandle, DEFAULT_DB_NAME,
    };

    #[cfg(feature = "cache")]
    pub use wordval_web::resources::{cache, set_cache, try_cache};

    #[cfg(feature = "template")]
    pub use wordval_web::resources::{render_template, set_template, try_template};
    #[cfg(feature = "template")]
    pub use wordval_web::TemplateEngine;

    pub use wordval_config::AppConfig;
    pub use wordval_web::middleware::{create_session, destroy_session, refresh_session};
    #[cfg(feature = "cache")]
    pub use wordval_web::middleware::{nonce, NonceState};
    pub use wordval_web::{
        current_tenant_id, require_tenant_id, resolve_tenant, with_tenant, App, CurrentUser,
        FileSender, OptionalUser, TenantId, ValidatedJson, WordvalRouter,
    };

    #[cfg(feature = "db")]
    pub use wordval_db::{
        factory, ActiveTx, Db, DbExecutor, Hook, HookChain, Isolation, Row, RowExt, TryFromRow,
    };

    pub use serde_json::{json, Value as JsonValue};

    #[cfg(feature = "cache")]
    pub use wordval_cache::{Cache, CacheStats, LocalCache};

    #[cfg(feature = "plugin")]
    pub use wordval_scheduler::SchedulerPlugin;

    #[cfg(all(feature = "plugin", feature = "cache"))]
    pub use wordval_cache::CachePlugin;

    #[cfg(all(feature = "plugin", feature = "fs"))]
    pub use wordval_fs::FsPlugin;

    #[cfg(feature = "plugin")]
    pub use crate::create_plugins_from_config;

    #[cfg(all(feature = "plugin", feature = "agent"))]
    pub use crate::extend_plugins_from_config;

    #[cfg(feature = "agent")]
    pub use wordval_agent::AgentPlugin;

    #[cfg(feature = "fs")]
    pub use wordval_fs::{FileMeta, LocalFs};

    pub use axum::extract::{Path, Query};
    pub use axum::response::Json as AxumJson;
    pub use axum::response::Json;
    pub use axum::Extension;
}

pub use wordval_core::{trace_error, ApiError, Error, Res, ResResult, Result};
pub use wordval_utils::serde_utils;
pub use wordval_utils::sid;

pub use wordval_cache_macros::{cache_evict, cache_evict_pattern, cached};
pub use wordval_macros::{delete, get, idempotent, no_auth, permission, post, put, tools, Model};
pub use wordval_web::resources::{cfg, config, set_config, try_config};

#[cfg(feature = "db")]
pub use wordval_web::resources::{
    db, db_default, db_names, register_db, remove_db, set_db, set_dbs, try_db, try_using, DbHandle,
    DEFAULT_DB_NAME,
};

#[cfg(feature = "cache")]
pub use wordval_web::resources::{cache, set_cache, try_cache};

#[cfg(feature = "fs")]
pub use wordval_web::resources::{fs, set_fs, try_fs};

#[cfg(feature = "template")]
pub use wordval_web::resources::{render_template, set_template, try_template};
#[cfg(feature = "template")]
pub use wordval_web::TemplateEngine;

pub use wordval_config::AppConfig;
#[cfg(feature = "db")]
pub use wordval_db::{Db, Row, RowExt, Q};
pub use wordval_web::{
    current_tenant_id, require_tenant_id, resolve_tenant, with_tenant, App, CurrentUser,
    FileSender, NoAuthDef, OptionalUser, PermissionDef, RouteEntry, TenantId, ValidatedJson,
    WordvalRouter, NO_AUTH_ROUTES, PERMISSION_ROUTES, ROUTES,
};

pub use linkme::distributed_slice;
pub use uuid;

/// axum re-export(`#[wordval::idempotent]` 宏展开路径使用)
pub use axum;

/// serde_json re-export(`#[wordval::tools]` 宏展开路径使用,业务代码无需直接依赖)
pub use serde_json;

/// Web 子模块(完整导出)
pub use wordval_web as web;

#[cfg(feature = "plugin")]
pub use wordval_scheduler;

#[cfg(feature = "agent")]
pub use wordval_agent;

#[cfg(feature = "fs")]
pub use wordval_fs;

// ──── 插件组装(umbrella crate 职责) ────────────────

/// 根据配置创建所有启用的内置插件,返回 PluginManager
///
/// # Panics
///
/// `config.plugins.enabled` 中出现未知插件名时直接 panic(fail-fast):
/// 配置笔误应在启动期暴露,而非静默跳过导致功能缺失。
#[cfg(feature = "plugin")]
pub fn create_plugins_from_config(config: &AppConfig) -> wordval_core::PluginManager {
    let mut mgr = wordval_core::PluginManager::new();

    for name in &config.plugins.enabled {
        match name.as_str() {
            "scheduler" => {
                mgr = mgr.add(wordval_scheduler::SchedulerPlugin::new());
                tracing::info!("插件: scheduler 已注册");
            }
            "cache" => {
                mgr = mgr.add(wordval_cache::CachePlugin::new(
                    &config.cache,
                    &config.redis,
                ));
                tracing::info!("插件: cache 已注册");
            }
            "fs" => {
                #[cfg(feature = "fs")]
                {
                    mgr = mgr.add(wordval_fs::FsPlugin::new(&config.fs));
                    tracing::info!("插件: fs 已注册");
                }
            }
            "sid" => {
                // 向后兼容:短 ID 生成已回归 wordval_utils::Sid(无状态工具,无需插件注册),
                // 直接使用 Sid::uuid() / Sid::uuid7() / Sid::biz_id() 等静态方法即可
                tracing::warn!(
                    "配置中的 'sid' 插件已废弃:短 ID 生成是无状态工具,请改用 wordval_utils::Sid"
                );
            }
            "agent" => {
                // agent 插件由 extend_plugins_from_config 处理(需要 agent feature)
                #[cfg(feature = "agent")]
                {
                    mgr = mgr.add(wordval_agent::AgentPlugin::new());
                    tracing::info!("插件: agent 已注册");
                }
            }
            other => {
                #[cfg(feature = "agent")]
                let opts = "scheduler, cache, fs, agent";
                #[cfg(not(feature = "agent"))]
                let opts = "scheduler, cache, fs";
                panic!("未知插件名: {other}(可选: {opts})");
            }
        }
    }

    mgr
}

/// 兼容保留:在 [`create_plugins_from_config`] 基础上追加 agent 插件
///
/// 注意:[`create_plugins_from_config`] 已内置处理 `"agent"` 条目(需 `agent` feature),
/// 本函数仅在外部调用方自行构建 `PluginManager` 且遗漏 agent 注册时使用。
/// 若对同一配置先后调用两个函数,agent 插件会被重复注册。
///
/// # 用法
///
/// ```ignore
/// let mgr = wordval::create_plugins_from_config(&config);
/// // 通常无需再调用;仅当自行组装 PluginManager 时才需要:
/// // let mgr = wordval::extend_plugins_from_config(mgr, &config);
/// ```
#[cfg(all(feature = "plugin", feature = "agent"))]
pub fn extend_plugins_from_config(
    mgr: wordval_core::PluginManager,
    config: &AppConfig,
) -> wordval_core::PluginManager {
    if config.plugins.enabled.iter().any(|n| n == "agent") {
        mgr.add(wordval_agent::AgentPlugin::new())
    } else {
        mgr
    }
}