Skip to main content

alun_web/
resources.rs

1//! 全局资源注册表
2//!
3//! 所有资源统一通过全局单例访问,代码更简洁。
4//!
5//! # 示例
6//!
7//! ```ignore
8//! use alun::{db, cfg, render_template};
9//!
10//! async fn query_user(id: &str) -> Result<Option<Row>, String> {
11//!     let users = db().find_by_id("user", id).await?;
12//!     Ok(users)
13//! }
14//!
15//! async fn page_home() -> Result<Html<String>, String> {
16//!     let content = render_template("home.html", &json!({"title": cfg().app_name}))?;
17//!     Ok(Html(content))
18//! }
19//! ```
20
21use std::sync::OnceLock;
22use std::sync::Arc;
23
24#[cfg(feature = "db")]
25use alun_db::Db;
26#[cfg(feature = "cache")]
27use alun_cache::SharedCache;
28use alun_config::ConfigManager;
29#[cfg(feature = "template")]
30use alun_template::TemplateEngine;
31
32// 全局资源实例(单例,标准库 OnceLock)
33#[cfg(feature = "db")]
34static DB: OnceLock<Db> = OnceLock::new();
35#[cfg(feature = "cache")]
36static CACHE: OnceLock<SharedCache> = OnceLock::new();
37static CONFIG: OnceLock<Arc<ConfigManager>> = OnceLock::new();
38#[cfg(feature = "template")]
39static TEMPLATE: OnceLock<TemplateEngine> = OnceLock::new();
40static UPLOAD_PATH: OnceLock<String> = OnceLock::new();
41static DOWNLOAD_PATH: OnceLock<String> = OnceLock::new();
42
43// ── 设置资源(框架启动时调用)───────────────────────────────────────
44
45/// 初始化数据库
46#[cfg(feature = "db")]
47pub fn set_db(db: Db) -> Result<(), &'static str> {
48    DB.set(db).map_err(|_| "数据库资源已初始化")
49}
50
51/// 初始化缓存
52#[cfg(feature = "cache")]
53pub fn set_cache(cache: SharedCache) -> Result<(), &'static str> {
54    CACHE.set(cache).map_err(|_| "缓存资源已初始化")
55}
56
57/// 初始化配置
58pub fn set_config(config: Arc<ConfigManager>) -> Result<(), &'static str> {
59    CONFIG.set(config).map_err(|_| "配置资源已初始化")
60}
61
62/// 初始化模板引擎
63#[cfg(feature = "template")]
64pub fn set_template(template: TemplateEngine) -> Result<(), &'static str> {
65    TEMPLATE.set(template).map_err(|_| "模板引擎已初始化")
66}
67
68// ── 获取资源(业务代码调用)────────────────────────────────────────
69
70/// 获取全局数据库实例
71#[cfg(feature = "db")]
72pub fn db() -> &'static Db {
73    DB.get().expect("数据库未初始化,请先调用 set_db()")
74}
75
76/// 安全获取数据库(返回 Option)
77#[cfg(feature = "db")]
78pub fn try_db() -> Option<&'static Db> {
79    DB.get()
80}
81
82/// 获取全局缓存实例
83#[cfg(feature = "cache")]
84pub fn cache() -> &'static SharedCache {
85    CACHE.get().expect("缓存未初始化,请先调用 set_cache()")
86}
87
88/// 安全获取缓存(返回 Option)
89#[cfg(feature = "cache")]
90pub fn try_cache() -> Option<&'static SharedCache> {
91    CACHE.get()
92}
93
94/// 获取全局配置管理器
95pub fn config() -> &'static Arc<ConfigManager> {
96    CONFIG.get().expect("配置未初始化,请先调用 set_config()")
97}
98
99/// 安全获取配置(返回 Option)
100pub fn try_config() -> Option<&'static Arc<ConfigManager>> {
101    CONFIG.get()
102}
103
104/// 获取全局配置(快捷方式)
105pub fn cfg() -> &'static alun_config::AppConfig {
106    config().get()
107}
108
109/// 渲染模板(便捷函数)
110#[cfg(feature = "template")]
111pub fn render_template<T: serde::Serialize>(
112    name: &str,
113    context: &T,
114) -> alun_core::Result<String> {
115    TEMPLATE.get()
116        .ok_or_else(|| alun_core::Error::Template("模板引擎未初始化".into()))
117        .and_then(|t| t.render(name, context))
118}
119
120/// 安全获取模板引擎(返回 Option)
121#[cfg(feature = "template")]
122pub fn try_template() -> Option<&'static TemplateEngine> {
123    TEMPLATE.get()
124}
125
126/// 初始化上传文件存储路径
127pub fn set_upload_path(path: String) -> Result<(), &'static str> {
128    UPLOAD_PATH.set(path).map_err(|_| "上传路径已初始化")
129}
130
131/// 获取上传文件存储路径
132pub fn upload_path() -> &'static str {
133    UPLOAD_PATH.get().map(|s| s.as_str()).unwrap_or("uploads")
134}
135
136/// 安全获取上传路径(返回 Option)
137pub fn try_upload_path() -> Option<&'static str> {
138    UPLOAD_PATH.get().map(|s| s.as_str())
139}
140
141/// 初始化下载文件存储路径
142pub fn set_download_path(path: String) -> Result<(), &'static str> {
143    DOWNLOAD_PATH.set(path).map_err(|_| "下载路径已初始化")
144}
145
146/// 获取下载文件存储路径
147pub fn download_path() -> &'static str {
148    DOWNLOAD_PATH.get().map(|s| s.as_str()).unwrap_or("downloads")
149}
150
151/// 安全获取下载路径(返回 Option)
152pub fn try_download_path() -> Option<&'static str> {
153    DOWNLOAD_PATH.get().map(|s| s.as_str())
154}