Skip to main content

sz_rust_state_facade/
env.rs

1//! Env 模块 — 对齐 PHP `think\facade\Env`
2//!
3//! 本模块实现环境变量管理,对齐 PHP `think\facade\Env` 的核心 API。
4//!
5//! ## PHP 对齐
6//!
7//! ### 核心 API 映射
8//!
9//! | PHP 方法 | Rust 方法 | 说明 |
10//! |---------|-----------|------|
11//! | `Env::get($name, $default = null)` | [`Env::get`] / [`Env::get_with_default`] | 获取环境变量 |
12//! | `Env::set($name, $value)` | [`Env::set`] | 设置环境变量(仅写入内部存储) |
13//! | `Env::has($name)` | [`Env::has`] | 检查环境变量是否存在 |
14//! | `Env::load($file)` | [`Env::load_from_file`] | 从 `.env` 文件加载 |
15//!
16//! ### PHP 行为对齐
17//!
18//! - **优先级**:PHP `Env::get()` 优先返回真实环境变量(`$_SERVER` / `getenv()`),
19//!   其次返回 `.env` 文件加载的值。Rust 同样优先 `std::env::var()`,其次查内部存储。
20//! - **点分隔访问**:PHP `.env` 文件支持 `[section]` 段,通过 `section.key` 访问。
21//!   Rust 通过 [`Env::load_from_file`] 解析 INI 风格 section,存储为 `section.key` 形式。
22//! - **不污染进程环境**:PHP `Env::set()` 仅修改内部数组,不调用 `putenv()`。
23//!   Rust `set()` 同样仅写入内部 `HashMap`,避免 `std::env::set_var` 的线程安全问题。
24//!
25//! ## .env 文件格式
26//!
27//! 支持 INI 风格的 section 嵌套:
28//!
29//! ```ini
30//! APP_DEBUG = true
31//! APP_KEY = base64:xxxxxx
32//!
33//! [database]
34//! hostname = localhost
35//! port = 3306
36//! ```
37//!
38//! 访问方式:
39//! - `APP_DEBUG` → 顶层键
40//! - `database.hostname` → section 内键
41//!
42//! ## 架构说明
43//!
44//! - **无外部依赖**:不依赖 `dotenv` / `dotenvy` crate,自行实现 INI 解析
45//! - **线程安全**:通过 `Arc<RwLock<HashMap>>` 提供并发读、互斥写
46//! - **不修改进程环境变量**:所有 `set()` 仅写入内部存储
47
48use parking_lot::RwLock;
49use std::collections::HashMap;
50use std::path::Path;
51use std::sync::Arc;
52use thiserror::Error;
53use tokio::fs;
54
55// ============================================================================
56// 错误类型
57// ============================================================================
58
59/// Env 错误
60#[derive(Debug, Error)]
61pub enum EnvError {
62    /// `.env` 文件读取失败
63    #[error(".env 文件读取失败: {path} — {source}")]
64    FileRead {
65        /// 文件路径
66        path: String,
67        /// 底层 IO 错误
68        #[source]
69        source: std::io::Error,
70    },
71    /// `.env` 文件解析失败
72    #[error(".env 文件解析失败: {path} — 行 {line}: {message}")]
73    Parse {
74        /// 文件路径
75        path: String,
76        /// 出错的行号(从 1 开始)
77        line: usize,
78        /// 错误描述
79        message: String,
80    },
81}
82
83// ============================================================================
84// Env 主体
85// ============================================================================
86
87/// 环境变量管理器 — 对齐 PHP `think\facade\Env`
88///
89/// 通过 `.env` 文件加载配置,同时支持读取真实进程环境变量。
90///
91/// # 优先级
92///
93/// `get()` 查找顺序:
94/// 1. 真实进程环境变量 `std::env::var(name)`
95/// 2. 内部存储(`.env` 文件加载或 `set()` 写入的值)
96///
97/// # 线程安全
98///
99/// 内部存储通过 `Arc<RwLock<HashMap>>` 保护,支持并发读、互斥写。
100/// 不调用 `std::env::set_var`,避免 Rust 2024 edition 的线程安全警告。
101///
102/// # PHP 对齐
103///
104/// ```php
105/// // PHP think\facade\Env
106/// Env::load('.env');          // 加载 .env 文件
107/// Env::set('APP_KEY', 'xxx'); // 设置内部变量
108/// Env::get('APP_KEY');        // 获取(优先真实环境变量)
109/// Env::has('APP_KEY');        // 检查存在
110/// ```
111#[derive(Debug, Clone, Default)]
112pub struct Env {
113    /// 内部存储(.env 文件加载 + set() 写入)
114    data: Arc<RwLock<HashMap<String, String>>>,
115}
116
117impl Env {
118    /// 创建空的 Env 实例
119    pub fn new() -> Self {
120        Self::default()
121    }
122
123    /// 从 `.env` 文件加载配置
124    ///
125    /// 支持 INI 风格的 `[section]` 段,section 内的键会以 `section.key` 形式存储。
126    ///
127    /// # 参数
128    ///
129    /// - `path`: `.env` 文件路径
130    ///
131    /// # 返回
132    ///
133    /// 成功返回 `Ok(())`,失败返回 [`EnvError`]。
134    ///
135    /// # PHP 对齐
136    ///
137    /// ```php
138    /// Env::load('.env');
139    /// ```
140    ///
141    /// # 错误
142    ///
143    /// - [`EnvError::FileRead`][]: 文件读取失败
144    /// - [`EnvError::Parse`][]: 文件解析失败(格式错误)
145    pub async fn load_from_file(&self, path: impl AsRef<Path>) -> Result<(), EnvError> {
146        let path_ref = path.as_ref();
147        let content = fs::read_to_string(path_ref)
148            .await
149            .map_err(|e| EnvError::FileRead {
150                path: path_ref.display().to_string(),
151                source: e,
152            })?;
153
154        self.parse_ini_content(&content, &path_ref.display().to_string())
155    }
156
157    /// 解析 INI 风格内容并写入内部存储
158    ///
159    /// # 格式规则
160    ///
161    /// - `key = value` → 顶层键值对
162    /// - `[section]` → 后续键值对存储为 `section.key`
163    /// - `#` 或 `;` 开头的行 → 注释,忽略
164    /// - 空行 → 忽略
165    /// - 引号包裹的值会去除引号(`"value"` → `value`)
166    fn parse_ini_content(&self, content: &str, path: &str) -> Result<(), EnvError> {
167        let mut data = self.data.write();
168        let mut current_section: String = String::new();
169
170        for (line_idx, raw_line) in content.lines().enumerate() {
171            let line_no = line_idx + 1;
172            let line = raw_line.trim();
173
174            // 空行跳过
175            if line.is_empty() {
176                continue;
177            }
178
179            // 注释行跳过(# 或 ; 开头)
180            if line.starts_with('#') || line.starts_with(';') {
181                continue;
182            }
183
184            // section 头:[section]
185            if line.starts_with('[') {
186                if let Some(end) = line.find(']') {
187                    current_section = line[1..end].trim().to_string();
188                } else {
189                    return Err(EnvError::Parse {
190                        path: path.to_string(),
191                        line: line_no,
192                        message: "section 头缺少闭合的 ']'".to_string(),
193                    });
194                }
195                continue;
196            }
197
198            // 键值对:key = value
199            if let Some(eq_pos) = line.find('=') {
200                let key = line[..eq_pos].trim().to_string();
201                let mut value = line[eq_pos + 1..].trim().to_string();
202
203                if key.is_empty() {
204                    return Err(EnvError::Parse {
205                        path: path.to_string(),
206                        line: line_no,
207                        message: "键为空".to_string(),
208                    });
209                }
210
211                // 去除引号包裹
212                if value.len() >= 2 {
213                    let first = value.chars().next().expect("已检查 value.len() >= 2");
214                    let last = value.chars().last().expect("已检查 value.len() >= 2");
215                    if (first == '"' && last == '"') || (first == '\'' && last == '\'') {
216                        value = value[1..value.len() - 1].to_string();
217                    }
218                }
219
220                // 拼接完整键名(section.key 或顶层 key)
221                let full_key = if current_section.is_empty() {
222                    key
223                } else {
224                    format!("{}.{}", current_section, key)
225                };
226
227                data.insert(full_key, value);
228            } else {
229                return Err(EnvError::Parse {
230                    path: path.to_string(),
231                    line: line_no,
232                    message: "缺少 '=' 分隔符".to_string(),
233                });
234            }
235        }
236
237        Ok(())
238    }
239
240    /// 获取环境变量值
241    ///
242    /// # 优先级
243    ///
244    /// 1. 真实进程环境变量 `std::env::var(name)`
245    /// 2. 内部存储(`.env` 文件加载或 `set()` 写入的值)
246    ///
247    /// # 参数
248    ///
249    /// - `name`: 环境变量名(支持点分隔,如 `database.hostname`)
250    ///
251    /// # 返回
252    ///
253    /// 存在返回 `Some(value)`,不存在返回 `None`。
254    ///
255    /// # PHP 对齐
256    ///
257    /// ```php
258    /// Env::get('APP_KEY');  // 无默认值,不存在返回 null
259    /// ```
260    pub fn get(&self, name: &str) -> Option<String> {
261        // 优先真实进程环境变量
262        if let Ok(value) = std::env::var(name) {
263            if !value.is_empty() {
264                return Some(value);
265            }
266        }
267
268        // 其次内部存储
269        let data = self.data.read();
270        data.get(name).cloned()
271    }
272
273    /// 获取环境变量值,不存在时返回默认值
274    ///
275    /// # 参数
276    ///
277    /// - `name`: 环境变量名
278    /// - `default`: 默认值
279    ///
280    /// # 返回
281    ///
282    /// 存在返回实际值,不存在返回 `default`。
283    ///
284    /// # PHP 对齐
285    ///
286    /// ```php
287    /// Env::get('APP_DEBUG', false);  // 不存在时返回 false
288    /// ```
289    pub fn get_with_default(&self, name: &str, default: &str) -> String {
290        self.get(name).unwrap_or_else(|| default.to_string())
291    }
292
293    /// 检查环境变量是否存在
294    ///
295    /// # 优先级
296    ///
297    /// 同 [`Env::get`]:真实进程环境变量优先于内部存储。
298    ///
299    /// # 参数
300    ///
301    /// - `name`: 环境变量名
302    ///
303    /// # 返回
304    ///
305    /// 存在返回 `true`,否则返回 `false`。
306    ///
307    /// # PHP 对齐
308    ///
309    /// ```php
310    /// Env::has('APP_KEY');
311    /// ```
312    pub fn has(&self, name: &str) -> bool {
313        // 优先真实进程环境变量
314        if let Ok(value) = std::env::var(name) {
315            if !value.is_empty() {
316                return true;
317            }
318        }
319
320        // 其次内部存储
321        let data = self.data.read();
322        data.contains_key(name)
323    }
324
325    /// 设置环境变量(仅写入内部存储)
326    ///
327    /// # 注意
328    ///
329    /// 本方法**不调用** `std::env::set_var`,仅修改内部 `HashMap`。
330    /// 这样做的原因:
331    /// 1. 避免 Rust 2024 edition 中 `set_var` 的线程安全警告
332    /// 2. 对齐 PHP `think\facade\Env::set()` 的行为(仅修改内部数组,不调用 `putenv()`)
333    ///
334    /// # 参数
335    ///
336    /// - `name`: 环境变量名
337    /// - `value`: 环境变量值
338    ///
339    /// # PHP 对齐
340    ///
341    /// ```php
342    /// Env::set('APP_KEY', 'base64:xxxxxx');
343    /// ```
344    pub fn set(&self, name: &str, value: &str) {
345        let mut data = self.data.write();
346        data.insert(name.to_string(), value.to_string());
347    }
348
349    /// 删除内部存储中的环境变量
350    ///
351    /// # 注意
352    ///
353    /// 本方法仅删除内部存储中的值,**不影响**真实进程环境变量。
354    ///
355    /// # 参数
356    ///
357    /// - `name`: 环境变量名
358    ///
359    /// # 返回
360    ///
361    /// 如果内部存储中存在该键并已删除,返回 `true`;否则返回 `false`。
362    pub fn remove(&self, name: &str) -> bool {
363        let mut data = self.data.write();
364        data.remove(name).is_some()
365    }
366
367    /// 获取内部存储的所有键值对(快照)
368    ///
369    /// # 注意
370    ///
371    /// 返回的是内部存储的副本,**不包含**真实进程环境变量。
372    /// 主要用于调试和测试。
373    ///
374    /// # 返回
375    ///
376    /// 所有内部存储键值对的 `HashMap`。
377    pub fn all(&self) -> HashMap<String, String> {
378        let data = self.data.read();
379        data.clone()
380    }
381
382    /// 清空内部存储
383    ///
384    /// # 注意
385    ///
386    /// 仅清空内部存储,**不影响**真实进程环境变量。
387    pub fn clear(&self) {
388        let mut data = self.data.write();
389        data.clear();
390    }
391}
392
393// ============================================================================
394// 单元测试
395// ============================================================================
396
397#[cfg(test)]
398mod tests {
399    static ENV_TEST_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
400    use super::*;
401    use std::io::Write;
402
403    /// 测试空 Env 实例
404    #[test]
405    fn test_new_env_is_empty() {
406        let env = Env::new();
407        assert!(env.all().is_empty());
408        assert!(!env.has("NON_EXISTENT_KEY"));
409        assert_eq!(env.get("NON_EXISTENT_KEY"), None);
410    }
411
412    /// 测试 set/get/remove 基本流程
413    #[test]
414    fn test_set_get_remove() {
415        let env = Env::new();
416
417        env.set("APP_KEY", "base64:xxxxxx");
418        assert!(env.has("APP_KEY"));
419        assert_eq!(env.get("APP_KEY"), Some("base64:xxxxxx".to_string()));
420
421        assert!(env.remove("APP_KEY"));
422        assert!(!env.has("APP_KEY"));
423        assert_eq!(env.get("APP_KEY"), None);
424    }
425
426    /// 测试 get_with_default
427    #[test]
428    fn test_get_with_default() {
429        let env = Env::new();
430
431        // 不存在时返回默认值
432        assert_eq!(env.get_with_default("MISSING", "fallback"), "fallback");
433
434        // 存在时返回实际值
435        env.set("EXISTING", "actual");
436        assert_eq!(env.get_with_default("EXISTING", "fallback"), "actual");
437    }
438
439    /// 测试从 INI 格式字符串加载(含 section)
440    #[test]
441    fn test_load_from_ini_content_with_section() {
442        let env = Env::new();
443        let content = r#"
444# 顶层配置
445APP_DEBUG = true
446APP_KEY = "base64:secret"
447
448[database]
449hostname = localhost
450port = 3306
451
452[redis]
453host = "127.0.0.1"
454"#;
455        env.parse_ini_content(content, "<test>").unwrap();
456
457        // 验证顶层键
458        assert_eq!(env.get("APP_DEBUG"), Some("true".to_string()));
459        assert_eq!(env.get("APP_KEY"), Some("base64:secret".to_string()));
460
461        // 验证 section 内键
462        assert_eq!(env.get("database.hostname"), Some("localhost".to_string()));
463        assert_eq!(env.get("database.port"), Some("3306".to_string()));
464        assert_eq!(env.get("redis.host"), Some("127.0.0.1".to_string()));
465    }
466
467    /// 测试引号去除(双引号和单引号)
468    #[test]
469    fn test_quote_stripping() {
470        let env = Env::new();
471        let content = r#"
472DOUBLE = "value with spaces"
473SINGLE = 'another value'
474NO_QUOTE = plain
475EMPTY = ""
476"#;
477        env.parse_ini_content(content, "<test>").unwrap();
478
479        assert_eq!(env.get("DOUBLE"), Some("value with spaces".to_string()));
480        assert_eq!(env.get("SINGLE"), Some("another value".to_string()));
481        assert_eq!(env.get("NO_QUOTE"), Some("plain".to_string()));
482        assert_eq!(env.get("EMPTY"), Some("".to_string()));
483    }
484
485    /// 测试注释行跳过(# 和 ;)
486    #[test]
487    fn test_comment_lines_skipped() {
488        let env = Env::new();
489        let content = r#"
490# 这是注释
491APP_KEY = value1
492; 这也是注释
493APP_DEBUG = value2
494"#;
495        env.parse_ini_content(content, "<test>").unwrap();
496
497        assert_eq!(env.get("APP_KEY"), Some("value1".to_string()));
498        assert_eq!(env.get("APP_DEBUG"), Some("value2".to_string()));
499    }
500
501    /// 测试从真实文件加载
502    #[tokio::test]
503    async fn test_load_from_file() {
504        // 创建临时 .env 文件
505        let temp_dir = std::env::temp_dir().join("sz_rust_env_test");
506        let _ = std::fs::create_dir_all(&temp_dir);
507        let env_file = temp_dir.join(".env");
508
509        let mut file = std::fs::File::create(&env_file).unwrap();
510        writeln!(file, "TEST_KEY = test_value").unwrap();
511        writeln!(file).unwrap();
512        writeln!(file, "[section]").unwrap();
513        writeln!(file, "inner = inner_value").unwrap();
514        drop(file);
515
516        let env = Env::new();
517        env.load_from_file(&env_file).await.unwrap();
518
519        assert_eq!(env.get("TEST_KEY"), Some("test_value".to_string()));
520        assert_eq!(env.get("section.inner"), Some("inner_value".to_string()));
521
522        let _ = std::fs::remove_dir_all(&temp_dir);
523    }
524
525    /// 测试文件不存在时返回错误
526    #[tokio::test]
527    async fn test_load_nonexistent_file_errors() {
528        let env = Env::new();
529        let result = env.load_from_file("/nonexistent/path/.env").await;
530        assert!(result.is_err());
531        match result {
532            Err(EnvError::FileRead { .. }) => {}
533            _ => panic!("期望 FileRead 错误"),
534        }
535    }
536
537    /// 测试解析错误:section 头未闭合
538    #[test]
539    fn test_parse_unclosed_section_errors() {
540        let env = Env::new();
541        let content = "[unclosed_section\nkey = value";
542        let result = env.parse_ini_content(content, "<test>");
543        assert!(result.is_err());
544        match result {
545            Err(EnvError::Parse { line, .. }) => {
546                assert_eq!(line, 1);
547            }
548            _ => panic!("期望 Parse 错误"),
549        }
550    }
551
552    /// 测试解析错误:缺少等号
553    #[test]
554    fn test_parse_missing_equals_errors() {
555        let env = Env::new();
556        let content = "this_is_not_a_key_value_pair";
557        let result = env.parse_ini_content(content, "<test>");
558        assert!(result.is_err());
559        match result {
560            Err(EnvError::Parse { line, .. }) => {
561                assert_eq!(line, 1);
562            }
563            _ => panic!("期望 Parse 错误"),
564        }
565    }
566
567    /// 测试解析错误:键为空
568    #[test]
569    fn test_parse_empty_key_errors() {
570        let env = Env::new();
571        let content = " = value";
572        let result = env.parse_ini_content(content, "<test>");
573        assert!(result.is_err());
574        match result {
575            Err(EnvError::Parse { line, .. }) => {
576                assert_eq!(line, 1);
577            }
578            _ => panic!("期望 Parse 错误"),
579        }
580    }
581
582    /// 测试真实进程环境变量优先于内部存储
583    ///
584    /// 验证:当 `std::env::var(name)` 返回非空值时,`get()` 返回进程环境变量值,
585    /// 而非内部存储的值。
586    #[test]
587    fn test_process_env_takes_priority() {
588        let _guard = ENV_TEST_LOCK.lock();
589        let env = Env::new();
590
591        // 内部存储设置一个值
592        env.set("SZ_RUST_TEST_ENV_PRIORITY", "internal_value");
593
594        // 同时设置进程环境变量
595        std::env::set_var("SZ_RUST_TEST_ENV_PRIORITY", "process_value");
596
597        // get() 应返回进程环境变量值
598        assert_eq!(
599            env.get("SZ_RUST_TEST_ENV_PRIORITY"),
600            Some("process_value".to_string())
601        );
602
603        std::env::remove_var("SZ_RUST_TEST_ENV_PRIORITY");
604    }
605
606    /// 测试进程环境变量为空字符串时回退到内部存储
607    ///
608    /// 验证:当 `std::env::var(name)` 返回空字符串时,`get()` 回退到内部存储。
609    #[test]
610    fn test_empty_process_env_falls_back_to_internal() {
611        let _guard = ENV_TEST_LOCK.lock();
612        let env = Env::new();
613
614        // 内部存储设置一个值
615        env.set("SZ_RUST_TEST_EMPTY_FALLBACK", "internal_value");
616
617        // 设置进程环境变量为空字符串
618        std::env::set_var("SZ_RUST_TEST_EMPTY_FALLBACK", "");
619
620        // get() 应回退到内部存储
621        assert_eq!(
622            env.get("SZ_RUST_TEST_EMPTY_FALLBACK"),
623            Some("internal_value".to_string())
624        );
625
626        std::env::remove_var("SZ_RUST_TEST_EMPTY_FALLBACK");
627    }
628
629    /// 测试 clear 清空内部存储
630    #[test]
631    fn test_clear() {
632        let env = Env::new();
633        env.set("KEY1", "value1");
634        env.set("KEY2", "value2");
635        assert_eq!(env.all().len(), 2);
636
637        env.clear();
638        assert!(env.all().is_empty());
639    }
640
641    /// 测试 all() 返回内部存储快照
642    #[test]
643    fn test_all_returns_snapshot() {
644        let env = Env::new();
645        env.set("KEY1", "value1");
646        env.set("KEY2", "value2");
647
648        let snapshot = env.all();
649        assert_eq!(snapshot.len(), 2);
650        assert_eq!(snapshot.get("KEY1"), Some(&"value1".to_string()));
651        assert_eq!(snapshot.get("KEY2"), Some(&"value2".to_string()));
652
653        // 修改内部存储不影响快照
654        env.set("KEY3", "value3");
655        assert_eq!(snapshot.len(), 2);
656    }
657
658    /// 测试 remove 不存在的键返回 false
659    #[test]
660    fn test_remove_nonexistent_returns_false() {
661        let env = Env::new();
662        assert!(!env.remove("NON_EXISTENT"));
663    }
664
665    /// 测试跨 section 重复加载(覆盖语义)
666    ///
667    /// 验证:同一键名在不同 section 下是独立的(`section1.key` vs `section2.key`),
668    /// 但同 section 内的同名键会被覆盖。
669    #[test]
670    fn test_section_isolation() {
671        let env = Env::new();
672        let content = r#"
673[section1]
674key = value1
675
676[section2]
677key = value2
678"#;
679        env.parse_ini_content(content, "<test>").unwrap();
680
681        assert_eq!(env.get("section1.key"), Some("value1".to_string()));
682        assert_eq!(env.get("section2.key"), Some("value2".to_string()));
683    }
684
685    /// 测试多次 load_from_file 累加而非覆盖
686    ///
687    /// 验证:连续调用 `load_from_file` 会累加键值对,而非清空后重新加载。
688    /// 这对齐 PHP `think\facade\Env::load()` 的行为。
689    #[test]
690    fn test_multiple_load_accumulates() {
691        let env = Env::new();
692        let content1 = "KEY1 = value1";
693        let content2 = "KEY2 = value2";
694
695        env.parse_ini_content(content1, "<test1>").unwrap();
696        env.parse_ini_content(content2, "<test2>").unwrap();
697
698        assert_eq!(env.get("KEY1"), Some("value1".to_string()));
699        assert_eq!(env.get("KEY2"), Some("value2".to_string()));
700    }
701
702    // ========================================================================
703    // 并发边界测试(P3-CONC-01 ~ P3-CONC-04)
704    // ========================================================================
705
706    /// P3-CONC-01:Env 高并发读写一致性
707    ///
708    /// 验证 `Arc<RwLock<HashMap>>` 在高并发场景下:
709    /// - 多读者并发读取不阻塞彼此
710    /// - 写者与读者并发时数据不丢失
711    /// - 最终一致性:所有写入最终可见
712    #[tokio::test]
713    async fn test_env_concurrent_read_write_consistency() {
714        let env = Env::default();
715        let num_writers = 8;
716        let num_reads = 100;
717
718        // 预置数据
719        for i in 0..num_writers {
720            env.set(&format!("key{}", i), &format!("initial{}", i));
721        }
722
723        let env = std::sync::Arc::new(env);
724        let mut handles = Vec::new();
725
726        // 写者:每个写者写入 50 个值
727        for w in 0..num_writers {
728            let env = std::sync::Arc::clone(&env);
729            handles.push(tokio::spawn(async move {
730                for r in 0..num_reads {
731                    env.set(&format!("w{}_r{}", w, r), &format!("v{}_{}", w, r));
732                }
733            }));
734        }
735
736        // 读者:并发读取已预置的 key
737        for i in 0..num_writers {
738            let env = std::sync::Arc::clone(&env);
739            handles.push(tokio::spawn(async move {
740                for _ in 0..num_reads {
741                    let _ = env.get(&format!("key{}", i));
742                }
743            }));
744        }
745
746        futures::future::join_all(handles).await;
747
748        // 验证:所有写入最终可见
749        for w in 0..num_writers {
750            for r in 0..num_reads {
751                let val = env.get(&format!("w{}_r{}", w, r));
752                assert_eq!(
753                    val,
754                    Some(format!("v{}_{}", w, r)),
755                    "P3-CONC-01: 写者 {} 记录 r={} 应可见",
756                    w,
757                    r
758                );
759            }
760        }
761    }
762
763    /// P3-CONC-02:Env 无死锁 — 高并发 set/get 压力测试
764    ///
765    /// 使用超时包裹并发操作,验证不会发生死锁。
766    /// 若 `parking_lot::RwLock` 在极端并发下出现死锁,
767    /// `tokio::time::timeout` 将触发超时失败。
768    #[tokio::test]
769    async fn test_env_no_deadlock_under_contention() {
770        let env = std::sync::Arc::new(Env::default());
771        let num_tasks = 32;
772        let ops_per_task = 200;
773
774        let result = tokio::time::timeout(std::time::Duration::from_secs(10), async {
775            let mut handles = Vec::new();
776            for t in 0..num_tasks {
777                let env = std::sync::Arc::clone(&env);
778                handles.push(tokio::spawn(async move {
779                    for i in 0..ops_per_task {
780                        let key = format!("k_{}_{}", t, i % 10);
781                        if i % 3 == 0 {
782                            env.set(&key, &format!("v_{}_{}", t, i));
783                        } else {
784                            let _ = env.get(&key);
785                        }
786                    }
787                }));
788            }
789            futures::future::join_all(handles).await;
790        })
791        .await;
792
793        assert!(
794            result.is_ok(),
795            "P3-CONC-02: Env 高并发操作超时(可能存在死锁)"
796        );
797    }
798
799    /// P3-CONC-03:parking_lot RwLock 不跨 .await 持有
800    ///
801    /// 验证 Env 的 get/set 方法不会在持有锁的状态下穿越 await 点。
802    /// 测试方法:4 个任务通过 Barrier 同步后并发操作,
803    /// 若锁跨 await 持有则会出现任务饥饿,timeout 将捕获。
804    #[tokio::test]
805    async fn test_env_lock_never_held_across_await() {
806        let env = std::sync::Arc::new(Env::default());
807        let barrier = std::sync::Arc::new(tokio::sync::Barrier::new(4));
808        let mut handles = Vec::new();
809
810        for t in 0..4 {
811            let env = std::sync::Arc::clone(&env);
812            let barrier = std::sync::Arc::clone(&barrier);
813            handles.push(tokio::spawn(async move {
814                barrier.wait().await; // 所有任务同步到同一时刻
815                for i in 0..50 {
816                    let key = format!("concurrent_key_{}", i);
817                    env.set(&key, &format!("task{}_val{}", t, i));
818                    // 此处无 await 在锁内
819                    let _ = env.get(&key);
820                }
821            }));
822        }
823
824        let result = tokio::time::timeout(
825            std::time::Duration::from_secs(5),
826            futures::future::join_all(handles),
827        )
828        .await;
829
830        assert!(
831            result.is_ok(),
832            "P3-CONC-03: 任务饥饿(锁可能跨 await 持有)"
833        );
834    }
835
836    /// P3-CONC-04:Env 内存边界 — 大量 key 写入不 OOM
837    ///
838    /// 验证 Env 在写入大量 key 时内存增长可控,
839    /// 不会因 HashMap 扩容策略导致内存尖峰。
840    #[tokio::test]
841    async fn test_env_memory_bounded_under_load() {
842        let env = Env::default();
843        let num_keys = 10_000;
844
845        // 写入大量 key
846        for i in 0..num_keys {
847            env.set(&format!("stress_key_{}", i), &"x".repeat(100));
848        }
849
850        // 验证所有 key 可读
851        assert_eq!(env.get("stress_key_0"), Some("x".repeat(100)));
852        assert_eq!(env.get("stress_key_9999"), Some("x".repeat(100)));
853
854        // 删除一半(触发 HashMap 缩容路径)
855        for i in 0..num_keys / 2 {
856            env.remove(&format!("stress_key_{}", i));
857        }
858
859        // 验证删除后仍可正常操作
860        assert_eq!(env.get("stress_key_0"), None);
861        assert_eq!(env.get("stress_key_9999"), Some("x".repeat(100)));
862
863        // 重新写入被删除的 key(触发 HashMap rehash)
864        for i in 0..num_keys / 2 {
865            env.set(&format!("stress_key_{}", i), &"y".repeat(50));
866        }
867        assert_eq!(env.get("stress_key_0"), Some("y".repeat(50)));
868    }
869}