open_lark/core/
query_params.rs

1//! 查询参数常量定义模块
2//!
3//! 本模块定义了飞书API中常用的查询参数名称常量,旨在:
4//! 1. 减少字符串分配开销 - 使用&'static str替代重复的.to_string()调用
5//! 2. 提高代码一致性 - 统一参数名称,避免拼写错误  
6//! 3. 便于维护管理 - 集中管理所有查询参数常量
7//!
8//! # 性能影响
9//!
10//! 使用本模块常量可以显著减少内存分配:
11//! ```rust
12//! use open_lark::core::query_params::QueryParams;
13//! use std::collections::HashMap;
14//!
15//! # fn main() {
16//! let mut params = HashMap::new();
17//! let value = "20".to_string();
18//! // 优化前: 每次调用都分配新字符串
19//! params.insert("page_size", value.clone());
20//!
21//! // 优化后: 使用静态字符串常量
22//! params.insert(QueryParams::PAGE_SIZE, value);
23//! # }
24//! ```
25//!
26//! # 使用示例
27//!
28//! ```rust
29//! use open_lark::core::query_params::QueryParams;
30//! use std::collections::HashMap;
31//!
32//! # fn main() {
33//! let mut params = HashMap::new();
34//! params.insert(QueryParams::PAGE_SIZE, "20".to_string());
35//! let token = "next-page-token".to_string();
36//! params.insert(QueryParams::PAGE_TOKEN, token);
37//! # }
38//! ```
39
40use std::collections::HashMap;
41
42/// 飞书API查询参数常量定义
43///
44/// 提供所有常用查询参数的静态字符串常量,避免运行时字符串分配开销。
45/// 按功能分组组织,便于查找和维护。
46pub struct QueryParams;
47
48impl QueryParams {
49    // ==================== 分页参数 ====================
50    /// 分页大小参数 - 指定每页返回的项目数量
51    pub const PAGE_SIZE: &'static str = "page_size";
52
53    /// 分页令牌参数 - 用于获取下一页数据的令牌
54    pub const PAGE_TOKEN: &'static str = "page_token";
55
56    /// 页码参数 - 指定要获取的页面编号(某些API使用)
57    pub const PAGE: &'static str = "page";
58
59    /// 偏移量参数 - 指定从第几条记录开始返回
60    pub const OFFSET: &'static str = "offset";
61
62    /// 限制参数 - 限制返回的最大记录数
63    pub const LIMIT: &'static str = "limit";
64
65    // ==================== 时间范围参数 ====================
66    /// 开始时间参数 - 查询范围的起始时间
67    pub const START_TIME: &'static str = "start_time";
68
69    /// 结束时间参数 - 查询范围的结束时间  
70    pub const END_TIME: &'static str = "end_time";
71
72    /// 创建时间开始
73    pub const CREATE_TIME_START: &'static str = "create_time_start";
74
75    /// 创建时间结束
76    pub const CREATE_TIME_END: &'static str = "create_time_end";
77
78    /// 更新时间开始
79    pub const UPDATE_TIME_START: &'static str = "update_time_start";
80
81    /// 更新时间结束
82    pub const UPDATE_TIME_END: &'static str = "update_time_end";
83
84    // ==================== 用户和身份参数 ====================
85    /// 用户ID参数 - 指定目标用户的唯一标识
86    pub const USER_ID: &'static str = "user_id";
87
88    /// 用户ID类型参数 - 指定用户ID的类型格式
89    pub const USER_ID_TYPE: &'static str = "user_id_type";
90
91    /// 部门ID参数 - 指定目标部门的唯一标识
92    pub const DEPARTMENT_ID: &'static str = "department_id";
93
94    /// 部门ID类型参数 - 指定部门ID的类型格式
95    pub const DEPARTMENT_ID_TYPE: &'static str = "department_id_type";
96
97    /// 组织ID参数 - 指定组织的唯一标识
98    pub const ORG_ID: &'static str = "org_id";
99
100    /// 员工ID参数 - 指定员工的唯一标识
101    pub const EMPLOYEE_ID: &'static str = "employee_id";
102
103    // ==================== 状态和类型参数 ====================
104    /// 状态参数 - 指定资源的状态筛选条件
105    pub const STATUS: &'static str = "status";
106
107    /// 类型参数 - 指定资源的类型筛选条件
108    pub const TYPE: &'static str = "type";
109
110    /// 规则类型参数 - 指定规则的类型
111    pub const RULE_TYPE: &'static str = "rule_type";
112
113    /// 访问类型参数 - 指定访问的类型
114    pub const ACCESS_TYPE: &'static str = "access_type";
115
116    /// 排序参数 - 指定结果的排序方式
117    pub const SORT: &'static str = "sort";
118
119    /// 排序顺序参数 - 指定排序的方向(asc/desc)
120    pub const ORDER: &'static str = "order";
121
122    // ==================== 内容和搜索参数 ====================
123    /// 查询关键词参数 - 用于搜索的关键词
124    pub const QUERY: &'static str = "query";
125
126    /// 搜索关键词参数 - 另一种搜索关键词字段名
127    pub const KEYWORD: &'static str = "keyword";
128
129    /// 名称参数 - 按名称筛选
130    pub const NAME: &'static str = "name";
131
132    /// 标题参数 - 按标题筛选
133    pub const TITLE: &'static str = "title";
134
135    /// 语言参数 - 指定语言偏好
136    pub const LANGUAGE: &'static str = "language";
137
138    /// 地区参数 - 指定地区设置
139    pub const LOCALE: &'static str = "locale";
140
141    // ==================== 会议和VC参数 ====================
142    /// 会议ID参数 - 会议的唯一标识
143    pub const MEETING_ID: &'static str = "meeting_id";
144
145    /// 房间ID参数 - 会议室的唯一标识
146    pub const ROOM_ID: &'static str = "room_id";
147
148    /// 录制ID参数 - 录制文件的唯一标识
149    pub const RECORDING_ID: &'static str = "recording_id";
150
151    // ==================== 工作台和应用参数 ====================
152    /// 自定义工作台ID参数
153    pub const CUSTOM_WORKPLACE_ID: &'static str = "custom_workplace_id";
154
155    /// 小部件ID参数  
156    pub const WIDGET_ID: &'static str = "widget_id";
157
158    /// 应用ID参数
159    pub const APP_ID: &'static str = "app_id";
160
161    // ==================== 招聘相关参数 ====================
162    /// 收入类型参数
163    pub const INCOME_TYPE: &'static str = "income_type";
164    /// 开始日期参数
165    pub const START_DATE: &'static str = "start_date";
166    /// 结束日期参数
167    pub const END_DATE: &'static str = "end_date";
168
169    // ==================== 文件和文档参数 ====================
170    /// 文件ID参数 - 文件的唯一标识
171    pub const FILE_ID: &'static str = "file_id";
172
173    /// 文档ID参数 - 文档的唯一标识
174    pub const DOC_ID: &'static str = "doc_id";
175
176    /// 文件夹ID参数 - 文件夹的唯一标识
177    pub const FOLDER_ID: &'static str = "folder_id";
178
179    /// 文件类型参数 - 文件类型筛选
180    pub const FILE_TYPE: &'static str = "file_type";
181
182    // ==================== 消息和IM参数 ====================
183    /// 消息ID参数 - 消息的唯一标识
184    pub const MESSAGE_ID: &'static str = "message_id";
185
186    /// 聊天ID参数 - 聊天会话的唯一标识
187    pub const CHAT_ID: &'static str = "chat_id";
188
189    /// 频道ID参数 - 频道的唯一标识
190    pub const CHANNEL_ID: &'static str = "channel_id";
191
192    // ==================== 设备和访问参数 ====================
193    /// 设备ID参数 - 设备的唯一标识
194    pub const DEVICE_ID: &'static str = "device_id";
195
196    /// 访问方法参数 - 访问方式筛选
197    pub const ACCESS_METHOD: &'static str = "access_method";
198
199    /// 结果参数 - 访问结果筛选
200    pub const RESULT: &'static str = "result";
201
202    // ==================== 勋章相关参数 ====================
203    /// 勋章ID参数 - 勋章的唯一标识
204    pub const BADGE_ID: &'static str = "badge_id";
205}
206
207/// 查询参数构建器
208///
209/// 提供类型安全的查询参数构建接口,支持链式调用,
210/// 内部使用静态字符串常量避免不必要的字符串分配。
211///
212/// # 示例
213///
214/// ```rust
215/// use open_lark::core::query_params::{QueryParams, QueryParamsBuilder};
216///
217/// let params = QueryParamsBuilder::new()
218///     .page_size(20)
219///     .page_token("token_123")
220///     .start_time("2024-01-01T00:00:00Z")
221///     .build();
222///
223/// assert_eq!(params.get(QueryParams::PAGE_SIZE), Some(&"20".to_string()));
224/// ```
225#[derive(Debug, Default)]
226pub struct QueryParamsBuilder {
227    params: HashMap<&'static str, String>,
228}
229
230impl QueryParamsBuilder {
231    /// 创建新的查询参数构建器
232    pub fn new() -> Self {
233        Self {
234            params: HashMap::new(),
235        }
236    }
237
238    /// 预分配HashMap容量,适用于已知参数数量的场景
239    pub fn with_capacity(capacity: usize) -> Self {
240        Self {
241            params: HashMap::with_capacity(capacity),
242        }
243    }
244
245    // ==================== 分页参数方法 ====================
246
247    /// 设置分页大小
248    pub fn page_size(mut self, size: i32) -> Self {
249        self.params.insert(QueryParams::PAGE_SIZE, size.to_string());
250        self
251    }
252
253    /// 设置分页令牌
254    pub fn page_token<S: ToString>(mut self, token: S) -> Self {
255        self.params
256            .insert(QueryParams::PAGE_TOKEN, token.to_string());
257        self
258    }
259
260    /// 设置页码
261    pub fn page(mut self, page: i32) -> Self {
262        self.params.insert(QueryParams::PAGE, page.to_string());
263        self
264    }
265
266    /// 设置偏移量
267    pub fn offset(mut self, offset: i32) -> Self {
268        self.params.insert(QueryParams::OFFSET, offset.to_string());
269        self
270    }
271
272    /// 设置限制数量
273    pub fn limit(mut self, limit: i32) -> Self {
274        self.params.insert(QueryParams::LIMIT, limit.to_string());
275        self
276    }
277
278    // ==================== 时间参数方法 ====================
279
280    /// 设置开始时间
281    pub fn start_time<S: ToString>(mut self, time: S) -> Self {
282        self.params
283            .insert(QueryParams::START_TIME, time.to_string());
284        self
285    }
286
287    /// 设置结束时间
288    pub fn end_time<S: ToString>(mut self, time: S) -> Self {
289        self.params.insert(QueryParams::END_TIME, time.to_string());
290        self
291    }
292
293    // ==================== 用户参数方法 ====================
294
295    /// 设置用户ID
296    pub fn user_id<S: ToString>(mut self, id: S) -> Self {
297        self.params.insert(QueryParams::USER_ID, id.to_string());
298        self
299    }
300
301    /// 设置用户ID类型
302    pub fn user_id_type<S: ToString>(mut self, id_type: S) -> Self {
303        self.params
304            .insert(QueryParams::USER_ID_TYPE, id_type.to_string());
305        self
306    }
307
308    /// 设置部门ID
309    pub fn department_id<S: ToString>(mut self, id: S) -> Self {
310        self.params
311            .insert(QueryParams::DEPARTMENT_ID, id.to_string());
312        self
313    }
314
315    /// 设置组织ID
316    pub fn org_id<S: ToString>(mut self, id: S) -> Self {
317        self.params.insert(QueryParams::ORG_ID, id.to_string());
318        self
319    }
320
321    // ==================== 状态参数方法 ====================
322
323    /// 设置状态
324    pub fn status<S: ToString>(mut self, status: S) -> Self {
325        self.params.insert(QueryParams::STATUS, status.to_string());
326        self
327    }
328
329    /// 设置类型
330    pub fn r#type<S: ToString>(mut self, type_value: S) -> Self {
331        self.params
332            .insert(QueryParams::TYPE, type_value.to_string());
333        self
334    }
335
336    /// 设置规则类型
337    pub fn rule_type<S: ToString>(mut self, rule_type: S) -> Self {
338        self.params
339            .insert(QueryParams::RULE_TYPE, rule_type.to_string());
340        self
341    }
342
343    /// 设置访问类型
344    pub fn access_type<S: ToString>(mut self, access_type: S) -> Self {
345        self.params
346            .insert(QueryParams::ACCESS_TYPE, access_type.to_string());
347        self
348    }
349
350    // ==================== 自定义参数方法 ====================
351
352    /// 添加自定义参数,使用静态字符串作为key
353    pub fn custom_static(mut self, key: &'static str, value: String) -> Self {
354        self.params.insert(key, value);
355        self
356    }
357
358    /// 添加可选参数,仅当值存在时添加
359    pub fn optional<S: ToString>(mut self, key: &'static str, value: Option<S>) -> Self {
360        if let Some(v) = value {
361            self.params.insert(key, v.to_string());
362        }
363        self
364    }
365
366    /// 构建最终的查询参数HashMap
367    pub fn build(self) -> HashMap<String, String> {
368        // 转换为HashMap<String, String>以兼容现有API
369        self.params
370            .into_iter()
371            .map(|(k, v)| (k.to_string(), v))
372            .collect()
373    }
374
375    /// 构建为引用形式,避免额外的String分配
376    /// 注意:返回的引用仅在构建器实例存在期间有效
377    pub fn build_ref(&self) -> &HashMap<&'static str, String> {
378        &self.params
379    }
380
381    /// 获取参数数量
382    pub fn len(&self) -> usize {
383        self.params.len()
384    }
385
386    /// 判断是否为空
387    pub fn is_empty(&self) -> bool {
388        self.params.is_empty()
389    }
390}
391
392#[cfg(test)]
393mod tests {
394    use super::*;
395
396    // ==================== Constants Testing ====================
397
398    #[test]
399    fn test_pagination_constants() {
400        assert_eq!(QueryParams::PAGE_SIZE, "page_size");
401        assert_eq!(QueryParams::PAGE_TOKEN, "page_token");
402        assert_eq!(QueryParams::PAGE, "page");
403        assert_eq!(QueryParams::OFFSET, "offset");
404        assert_eq!(QueryParams::LIMIT, "limit");
405    }
406
407    #[test]
408    fn test_time_range_constants() {
409        assert_eq!(QueryParams::START_TIME, "start_time");
410        assert_eq!(QueryParams::END_TIME, "end_time");
411        assert_eq!(QueryParams::CREATE_TIME_START, "create_time_start");
412        assert_eq!(QueryParams::CREATE_TIME_END, "create_time_end");
413        assert_eq!(QueryParams::UPDATE_TIME_START, "update_time_start");
414        assert_eq!(QueryParams::UPDATE_TIME_END, "update_time_end");
415    }
416
417    #[test]
418    fn test_user_identity_constants() {
419        assert_eq!(QueryParams::USER_ID, "user_id");
420        assert_eq!(QueryParams::USER_ID_TYPE, "user_id_type");
421        assert_eq!(QueryParams::DEPARTMENT_ID, "department_id");
422        assert_eq!(QueryParams::DEPARTMENT_ID_TYPE, "department_id_type");
423        assert_eq!(QueryParams::ORG_ID, "org_id");
424        assert_eq!(QueryParams::EMPLOYEE_ID, "employee_id");
425    }
426
427    #[test]
428    fn test_status_type_constants() {
429        assert_eq!(QueryParams::STATUS, "status");
430        assert_eq!(QueryParams::TYPE, "type");
431        assert_eq!(QueryParams::RULE_TYPE, "rule_type");
432        assert_eq!(QueryParams::ACCESS_TYPE, "access_type");
433        assert_eq!(QueryParams::SORT, "sort");
434        assert_eq!(QueryParams::ORDER, "order");
435    }
436
437    #[test]
438    fn test_content_search_constants() {
439        assert_eq!(QueryParams::QUERY, "query");
440        assert_eq!(QueryParams::KEYWORD, "keyword");
441        assert_eq!(QueryParams::NAME, "name");
442        assert_eq!(QueryParams::TITLE, "title");
443        assert_eq!(QueryParams::LANGUAGE, "language");
444        assert_eq!(QueryParams::LOCALE, "locale");
445    }
446
447    #[test]
448    fn test_meeting_vc_constants() {
449        assert_eq!(QueryParams::MEETING_ID, "meeting_id");
450        assert_eq!(QueryParams::ROOM_ID, "room_id");
451        assert_eq!(QueryParams::RECORDING_ID, "recording_id");
452    }
453
454    #[test]
455    fn test_workplace_app_constants() {
456        assert_eq!(QueryParams::CUSTOM_WORKPLACE_ID, "custom_workplace_id");
457        assert_eq!(QueryParams::WIDGET_ID, "widget_id");
458        assert_eq!(QueryParams::APP_ID, "app_id");
459    }
460
461    #[test]
462    fn test_hire_related_constants() {
463        assert_eq!(QueryParams::INCOME_TYPE, "income_type");
464        assert_eq!(QueryParams::START_DATE, "start_date");
465        assert_eq!(QueryParams::END_DATE, "end_date");
466    }
467
468    #[test]
469    fn test_file_document_constants() {
470        assert_eq!(QueryParams::FILE_ID, "file_id");
471        assert_eq!(QueryParams::DOC_ID, "doc_id");
472        assert_eq!(QueryParams::FOLDER_ID, "folder_id");
473        assert_eq!(QueryParams::FILE_TYPE, "file_type");
474    }
475
476    #[test]
477    fn test_message_im_constants() {
478        assert_eq!(QueryParams::MESSAGE_ID, "message_id");
479        assert_eq!(QueryParams::CHAT_ID, "chat_id");
480        assert_eq!(QueryParams::CHANNEL_ID, "channel_id");
481    }
482
483    #[test]
484    fn test_device_access_constants() {
485        assert_eq!(QueryParams::DEVICE_ID, "device_id");
486        assert_eq!(QueryParams::ACCESS_METHOD, "access_method");
487        assert_eq!(QueryParams::RESULT, "result");
488    }
489
490    #[test]
491    fn test_badge_constants() {
492        assert_eq!(QueryParams::BADGE_ID, "badge_id");
493    }
494
495    // ==================== Builder Testing ====================
496
497    #[test]
498    fn test_query_params_builder_creation() {
499        let builder = QueryParamsBuilder::new();
500        assert_eq!(builder.len(), 0);
501        assert!(builder.is_empty());
502    }
503
504    #[test]
505    fn test_query_params_builder_default() {
506        let builder: QueryParamsBuilder = Default::default();
507        assert_eq!(builder.len(), 0);
508        assert!(builder.is_empty());
509    }
510
511    #[test]
512    fn test_builder_with_capacity() {
513        let builder = QueryParamsBuilder::with_capacity(5);
514        assert_eq!(builder.len(), 0);
515        assert!(builder.is_empty());
516        // Capacity doesn't affect length/empty state, just memory allocation
517    }
518
519    #[test]
520    fn test_builder_debug_trait() {
521        let builder = QueryParamsBuilder::new().page_size(10).user_id("test123");
522
523        let debug_str = format!("{:?}", builder);
524        assert!(debug_str.contains("QueryParamsBuilder"));
525        assert!(debug_str.contains("params"));
526    }
527
528    // ==================== Pagination Methods Testing ====================
529
530    #[test]
531    fn test_pagination_methods() {
532        let params = QueryParamsBuilder::new()
533            .page_size(20)
534            .page_token("token_123")
535            .page(1)
536            .offset(100)
537            .limit(50)
538            .build();
539
540        assert_eq!(params.len(), 5);
541        assert_eq!(params.get("page_size"), Some(&"20".to_string()));
542        assert_eq!(params.get("page_token"), Some(&"token_123".to_string()));
543        assert_eq!(params.get("page"), Some(&"1".to_string()));
544        assert_eq!(params.get("offset"), Some(&"100".to_string()));
545        assert_eq!(params.get("limit"), Some(&"50".to_string()));
546    }
547
548    #[test]
549    fn test_page_size_edge_cases() {
550        let params = QueryParamsBuilder::new().page_size(0).build();
551        assert_eq!(params.get("page_size"), Some(&"0".to_string()));
552
553        let params = QueryParamsBuilder::new().page_size(-1).build();
554        assert_eq!(params.get("page_size"), Some(&"-1".to_string()));
555
556        let params = QueryParamsBuilder::new().page_size(i32::MAX).build();
557        assert_eq!(params.get("page_size"), Some(&i32::MAX.to_string()));
558    }
559
560    #[test]
561    fn test_page_token_different_types() {
562        let params = QueryParamsBuilder::new().page_token("string_token").build();
563        assert_eq!(params.get("page_token"), Some(&"string_token".to_string()));
564
565        let params = QueryParamsBuilder::new()
566            .page_token(String::from("owned_string"))
567            .build();
568        assert_eq!(params.get("page_token"), Some(&"owned_string".to_string()));
569
570        let params = QueryParamsBuilder::new().page_token(123).build();
571        assert_eq!(params.get("page_token"), Some(&"123".to_string()));
572    }
573
574    #[test]
575    fn test_offset_limit_combinations() {
576        let params = QueryParamsBuilder::new().offset(0).limit(10).build();
577        assert_eq!(params.get("offset"), Some(&"0".to_string()));
578        assert_eq!(params.get("limit"), Some(&"10".to_string()));
579
580        let params = QueryParamsBuilder::new().offset(100).limit(0).build();
581        assert_eq!(params.get("offset"), Some(&"100".to_string()));
582        assert_eq!(params.get("limit"), Some(&"0".to_string()));
583    }
584
585    // ==================== Time Methods Testing ====================
586
587    #[test]
588    fn test_time_methods() {
589        let params = QueryParamsBuilder::new()
590            .start_time("2024-01-01T00:00:00Z")
591            .end_time("2024-12-31T23:59:59Z")
592            .build();
593
594        assert_eq!(params.len(), 2);
595        assert_eq!(
596            params.get("start_time"),
597            Some(&"2024-01-01T00:00:00Z".to_string())
598        );
599        assert_eq!(
600            params.get("end_time"),
601            Some(&"2024-12-31T23:59:59Z".to_string())
602        );
603    }
604
605    #[test]
606    fn test_time_methods_different_formats() {
607        let params = QueryParamsBuilder::new()
608            .start_time("1640995200") // Unix timestamp
609            .end_time("2024-01-01") // Date only
610            .build();
611
612        assert_eq!(params.get("start_time"), Some(&"1640995200".to_string()));
613        assert_eq!(params.get("end_time"), Some(&"2024-01-01".to_string()));
614    }
615
616    #[test]
617    fn test_time_methods_empty_strings() {
618        let params = QueryParamsBuilder::new()
619            .start_time("")
620            .end_time("")
621            .build();
622
623        assert_eq!(params.get("start_time"), Some(&"".to_string()));
624        assert_eq!(params.get("end_time"), Some(&"".to_string()));
625    }
626
627    // ==================== User Identity Methods Testing ====================
628
629    #[test]
630    fn test_user_identity_methods() {
631        let params = QueryParamsBuilder::new()
632            .user_id("user_123")
633            .user_id_type("open_id")
634            .department_id("dept_456")
635            .org_id("org_789")
636            .build();
637
638        assert_eq!(params.len(), 4);
639        assert_eq!(params.get("user_id"), Some(&"user_123".to_string()));
640        assert_eq!(params.get("user_id_type"), Some(&"open_id".to_string()));
641        assert_eq!(params.get("department_id"), Some(&"dept_456".to_string()));
642        assert_eq!(params.get("org_id"), Some(&"org_789".to_string()));
643    }
644
645    #[test]
646    fn test_user_id_type_variants() {
647        let params = QueryParamsBuilder::new().user_id_type("open_id").build();
648        assert_eq!(params.get("user_id_type"), Some(&"open_id".to_string()));
649
650        let params = QueryParamsBuilder::new().user_id_type("union_id").build();
651        assert_eq!(params.get("user_id_type"), Some(&"union_id".to_string()));
652
653        let params = QueryParamsBuilder::new().user_id_type("user_id").build();
654        assert_eq!(params.get("user_id_type"), Some(&"user_id".to_string()));
655    }
656
657    #[test]
658    fn test_user_methods_with_unicode() {
659        let params = QueryParamsBuilder::new()
660            .user_id("用户_123")
661            .department_id("部门_456")
662            .build();
663
664        assert_eq!(params.get("user_id"), Some(&"用户_123".to_string()));
665        assert_eq!(params.get("department_id"), Some(&"部门_456".to_string()));
666    }
667
668    // ==================== Status and Type Methods Testing ====================
669
670    #[test]
671    fn test_status_type_methods() {
672        let params = QueryParamsBuilder::new()
673            .status("active")
674            .r#type("document")
675            .rule_type("approval")
676            .access_type("read")
677            .build();
678
679        assert_eq!(params.len(), 4);
680        assert_eq!(params.get("status"), Some(&"active".to_string()));
681        assert_eq!(params.get("type"), Some(&"document".to_string()));
682        assert_eq!(params.get("rule_type"), Some(&"approval".to_string()));
683        assert_eq!(params.get("access_type"), Some(&"read".to_string()));
684    }
685
686    #[test]
687    fn test_type_method_with_raw_identifier() {
688        // Test the r#type method specifically since it uses a Rust keyword
689        let params = QueryParamsBuilder::new().r#type("special_type").build();
690
691        assert_eq!(params.get("type"), Some(&"special_type".to_string()));
692    }
693
694    #[test]
695    fn test_status_variations() {
696        let status_values = ["active", "inactive", "pending", "approved", "rejected"];
697
698        for status in &status_values {
699            let params = QueryParamsBuilder::new().status(*status).build();
700            assert_eq!(params.get("status"), Some(&status.to_string()));
701        }
702    }
703
704    // ==================== Custom and Optional Methods Testing ====================
705
706    #[test]
707    fn test_custom_static_method() {
708        let params = QueryParamsBuilder::new()
709            .custom_static("custom_param", "custom_value".to_string())
710            .custom_static("another_param", "another_value".to_string())
711            .build();
712
713        assert_eq!(params.len(), 2);
714        assert_eq!(
715            params.get("custom_param"),
716            Some(&"custom_value".to_string())
717        );
718        assert_eq!(
719            params.get("another_param"),
720            Some(&"another_value".to_string())
721        );
722    }
723
724    #[test]
725    fn test_optional_method_with_some() {
726        let params = QueryParamsBuilder::new()
727            .optional(QueryParams::PAGE_SIZE, Some(10))
728            .optional(QueryParams::USER_ID, Some("user123"))
729            .optional(QueryParams::STATUS, Some(String::from("active")))
730            .build();
731
732        assert_eq!(params.len(), 3);
733        assert_eq!(params.get("page_size"), Some(&"10".to_string()));
734        assert_eq!(params.get("user_id"), Some(&"user123".to_string()));
735        assert_eq!(params.get("status"), Some(&"active".to_string()));
736    }
737
738    #[test]
739    fn test_optional_method_with_none() {
740        let params = QueryParamsBuilder::new()
741            .optional(QueryParams::PAGE_SIZE, None::<i32>)
742            .optional(QueryParams::PAGE_TOKEN, None::<String>)
743            .optional(QueryParams::USER_ID, None::<&str>)
744            .build();
745
746        assert_eq!(params.len(), 0);
747        assert!(!params.contains_key("page_size"));
748        assert!(!params.contains_key("page_token"));
749        assert!(!params.contains_key("user_id"));
750    }
751
752    #[test]
753    fn test_optional_method_mixed() {
754        let params = QueryParamsBuilder::new()
755            .optional(QueryParams::PAGE_SIZE, Some(10))
756            .optional(QueryParams::PAGE_TOKEN, None::<String>)
757            .optional(QueryParams::USER_ID, Some("user123"))
758            .optional(QueryParams::STATUS, None::<&str>)
759            .build();
760
761        assert_eq!(params.len(), 2);
762        assert_eq!(params.get("page_size"), Some(&"10".to_string()));
763        assert_eq!(params.get("user_id"), Some(&"user123".to_string()));
764        assert!(!params.contains_key("page_token"));
765        assert!(!params.contains_key("status"));
766    }
767
768    // ==================== Build Methods Testing ====================
769
770    #[test]
771    fn test_build_method() {
772        let builder = QueryParamsBuilder::new().page_size(20).user_id("test_user");
773
774        let params = builder.build();
775
776        assert_eq!(params.len(), 2);
777        assert_eq!(params.get("page_size"), Some(&"20".to_string()));
778        assert_eq!(params.get("user_id"), Some(&"test_user".to_string()));
779
780        // Verify the HashMap is the expected type
781        let _: HashMap<String, String> = params;
782    }
783
784    #[test]
785    fn test_build_ref_method() {
786        let builder = QueryParamsBuilder::new().page_size(20).user_id("test_user");
787
788        let params_ref = builder.build_ref();
789
790        assert_eq!(params_ref.len(), 2);
791        assert_eq!(
792            params_ref.get(QueryParams::PAGE_SIZE),
793            Some(&"20".to_string())
794        );
795        assert_eq!(
796            params_ref.get(QueryParams::USER_ID),
797            Some(&"test_user".to_string())
798        );
799
800        // Verify the HashMap is the expected type with static str keys
801        let _: &HashMap<&'static str, String> = params_ref;
802    }
803
804    #[test]
805    fn test_build_empty() {
806        let params = QueryParamsBuilder::new().build();
807        assert_eq!(params.len(), 0);
808        assert!(params.is_empty());
809    }
810
811    #[test]
812    fn test_build_ref_empty() {
813        let builder = QueryParamsBuilder::new();
814        let params_ref = builder.build_ref();
815        assert_eq!(params_ref.len(), 0);
816        assert!(params_ref.is_empty());
817    }
818
819    // ==================== Chain Building Testing ====================
820
821    #[test]
822    fn test_comprehensive_chaining() {
823        let params = QueryParamsBuilder::new()
824            .page_size(50)
825            .page_token("next_page_123")
826            .start_time("2024-01-01T00:00:00Z")
827            .end_time("2024-12-31T23:59:59Z")
828            .user_id("user_456")
829            .user_id_type("open_id")
830            .status("active")
831            .r#type("document")
832            .custom_static("custom_field", "custom_value".to_string())
833            .optional(QueryParams::LIMIT, Some(100))
834            .optional(QueryParams::OFFSET, None::<i32>)
835            .build();
836
837        assert_eq!(params.len(), 10); // All non-None values: page_size, page_token, start_time, end_time, user_id, user_id_type, status, type, custom_field, limit
838        assert_eq!(params.get("page_size"), Some(&"50".to_string()));
839        assert_eq!(params.get("page_token"), Some(&"next_page_123".to_string()));
840        assert_eq!(
841            params.get("start_time"),
842            Some(&"2024-01-01T00:00:00Z".to_string())
843        );
844        assert_eq!(
845            params.get("end_time"),
846            Some(&"2024-12-31T23:59:59Z".to_string())
847        );
848        assert_eq!(params.get("user_id"), Some(&"user_456".to_string()));
849        assert_eq!(params.get("user_id_type"), Some(&"open_id".to_string()));
850        assert_eq!(params.get("status"), Some(&"active".to_string()));
851        assert_eq!(params.get("type"), Some(&"document".to_string()));
852        assert_eq!(
853            params.get("custom_field"),
854            Some(&"custom_value".to_string())
855        );
856        assert_eq!(params.get("limit"), Some(&"100".to_string()));
857        assert!(!params.contains_key("offset"));
858    }
859
860    #[test]
861    fn test_method_overwriting() {
862        let params = QueryParamsBuilder::new()
863            .page_size(10)
864            .page_size(20) // Should overwrite the previous value
865            .user_id("user1")
866            .user_id("user2") // Should overwrite the previous value
867            .build();
868
869        assert_eq!(params.len(), 2);
870        assert_eq!(params.get("page_size"), Some(&"20".to_string()));
871        assert_eq!(params.get("user_id"), Some(&"user2".to_string()));
872    }
873
874    #[test]
875    fn test_large_chain_building() {
876        let mut builder = QueryParamsBuilder::new();
877
878        // Chain 20 different parameters
879        builder = builder
880            .page_size(10)
881            .page_token("token")
882            .page(1)
883            .offset(0)
884            .limit(50)
885            .start_time("2024-01-01")
886            .end_time("2024-12-31")
887            .user_id("user123")
888            .user_id_type("open_id")
889            .department_id("dept456")
890            .org_id("org789")
891            .status("active")
892            .r#type("document")
893            .rule_type("approval")
894            .access_type("read")
895            .custom_static("param1", "value1".to_string())
896            .custom_static("param2", "value2".to_string())
897            .optional(QueryParams::LANGUAGE, Some("en"))
898            .optional(QueryParams::LOCALE, Some("en_US"))
899            .optional("extra_param", Some("extra_value"));
900
901        let params = builder.build();
902        assert_eq!(params.len(), 20);
903    }
904
905    // ==================== Memory and Performance Testing ====================
906
907    #[test]
908    fn test_with_capacity_performance() {
909        let large_capacity = 1000;
910        let builder = QueryParamsBuilder::with_capacity(large_capacity);
911
912        // Builder should start empty regardless of capacity
913        assert_eq!(builder.len(), 0);
914        assert!(builder.is_empty());
915    }
916
917    #[test]
918    fn test_memory_efficiency_static_strings() {
919        let params1 = QueryParamsBuilder::new().page_size(10).build();
920
921        let params2 = QueryParamsBuilder::new().page_size(20).build();
922
923        // Both should use the same static string key "page_size"
924        let key1 = params1.keys().next().unwrap();
925        let key2 = params2.keys().next().unwrap();
926
927        assert_eq!(key1, key2);
928        assert_eq!(key1, "page_size");
929    }
930
931    #[test]
932    fn test_string_conversion_consistency() {
933        let builder = QueryParamsBuilder::new()
934            .page_size(42)
935            .user_id("test_user")
936            .status("active");
937
938        let params_owned = builder.build();
939        let params_ref = {
940            let temp_builder = QueryParamsBuilder::new()
941                .page_size(42)
942                .user_id("test_user")
943                .status("active");
944            // Create a temporary reference to test the ref method
945            temp_builder.build_ref().clone()
946        };
947
948        // Values should be the same between build() and build_ref()
949        assert_eq!(params_owned.get("page_size"), Some(&"42".to_string()));
950        assert_eq!(
951            params_ref.get(QueryParams::PAGE_SIZE),
952            Some(&"42".to_string())
953        );
954    }
955
956    // ==================== Edge Cases and Error Conditions ====================
957
958    #[test]
959    fn test_empty_string_values() {
960        let params = QueryParamsBuilder::new()
961            .page_token("")
962            .user_id("")
963            .status("")
964            .build();
965
966        assert_eq!(params.len(), 3);
967        assert_eq!(params.get("page_token"), Some(&"".to_string()));
968        assert_eq!(params.get("user_id"), Some(&"".to_string()));
969        assert_eq!(params.get("status"), Some(&"".to_string()));
970    }
971
972    #[test]
973    fn test_unicode_and_special_characters() {
974        let params = QueryParamsBuilder::new()
975            .user_id("用户_123_éñ")
976            .page_token("🚀🎯📊")
977            .status("状态_active")
978            .custom_static("unicode_key", "测试值_with_emoji_🧪".to_string())
979            .build();
980
981        assert_eq!(params.get("user_id"), Some(&"用户_123_éñ".to_string()));
982        assert_eq!(params.get("page_token"), Some(&"🚀🎯📊".to_string()));
983        assert_eq!(params.get("status"), Some(&"状态_active".to_string()));
984        assert_eq!(
985            params.get("unicode_key"),
986            Some(&"测试值_with_emoji_🧪".to_string())
987        );
988    }
989
990    #[test]
991    fn test_extreme_numeric_values() {
992        let params = QueryParamsBuilder::new()
993            .page_size(i32::MAX)
994            .offset(i32::MIN)
995            .limit(0)
996            .page(-1)
997            .build();
998
999        assert_eq!(params.get("page_size"), Some(&i32::MAX.to_string()));
1000        assert_eq!(params.get("offset"), Some(&i32::MIN.to_string()));
1001        assert_eq!(params.get("limit"), Some(&"0".to_string()));
1002        assert_eq!(params.get("page"), Some(&"-1".to_string()));
1003    }
1004
1005    #[test]
1006    fn test_very_long_strings() {
1007        let long_string = "a".repeat(10000);
1008        let params = QueryParamsBuilder::new()
1009            .page_token(&long_string)
1010            .user_id(&long_string)
1011            .build();
1012
1013        assert_eq!(params.get("page_token"), Some(&long_string));
1014        assert_eq!(params.get("user_id"), Some(&long_string));
1015    }
1016
1017    #[test]
1018    fn test_builder_is_consumed_by_build() {
1019        let builder = QueryParamsBuilder::new().page_size(10).user_id("test");
1020
1021        let _params = builder.build();
1022        // builder is now consumed and cannot be used again
1023        // This test just verifies the consumption pattern compiles correctly
1024    }
1025
1026    #[test]
1027    fn test_optional_with_different_types() {
1028        let params = QueryParamsBuilder::new()
1029            .optional("int_param", Some(42))
1030            .optional("string_param", Some("test"))
1031            .optional("owned_string_param", Some(String::from("owned")))
1032            .optional("none_int", None::<i32>)
1033            .optional("none_string", None::<String>)
1034            .build();
1035
1036        assert_eq!(params.len(), 3);
1037        assert_eq!(params.get("int_param"), Some(&"42".to_string()));
1038        assert_eq!(params.get("string_param"), Some(&"test".to_string()));
1039        assert_eq!(params.get("owned_string_param"), Some(&"owned".to_string()));
1040        assert!(!params.contains_key("none_int"));
1041        assert!(!params.contains_key("none_string"));
1042    }
1043
1044    // ==================== Documentation Example Verification ====================
1045
1046    #[test]
1047    fn test_documentation_example() {
1048        // Verify the example from the module documentation works correctly
1049        let params = QueryParamsBuilder::new()
1050            .page_size(20)
1051            .page_token("token_123")
1052            .start_time("2024-01-01T00:00:00Z")
1053            .build();
1054
1055        assert_eq!(params.get(QueryParams::PAGE_SIZE), Some(&"20".to_string()));
1056        assert_eq!(
1057            params.get(QueryParams::PAGE_TOKEN),
1058            Some(&"token_123".to_string())
1059        );
1060        assert_eq!(
1061            params.get(QueryParams::START_TIME),
1062            Some(&"2024-01-01T00:00:00Z".to_string())
1063        );
1064    }
1065
1066    #[test]
1067    fn test_static_string_memory_optimization() {
1068        // Test that demonstrates the memory optimization of using static strings
1069        let mut params = HashMap::new();
1070        let value = "20".to_string();
1071
1072        // This is the optimized approach using constants
1073        params.insert(QueryParams::PAGE_SIZE, value.clone());
1074        assert_eq!(params.get(QueryParams::PAGE_SIZE), Some(&value));
1075
1076        // Verify the key is indeed a static string
1077        assert_eq!(QueryParams::PAGE_SIZE, "page_size");
1078
1079        // The key should be from static memory (pointer comparison would be same)
1080        let key1 = QueryParams::PAGE_SIZE;
1081        let key2 = QueryParams::PAGE_SIZE;
1082        assert_eq!(key1.as_ptr(), key2.as_ptr()); // Same memory location
1083    }
1084}