wechat_minapp/new_type/
scene.rs

1use std::fmt;
2use std::str::FromStr;
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum ValidationSceneError {
7    #[error("字符串长度超过32个字符限制")]
8    TooLong,
9    #[error("包含非法字符: {0}")]
10    InvalidChar(char),
11}
12
13// 合法的字符集:数字、大小写英文、!#$&'()*+,/:;=?@-._~
14const VALID_CHARS: &str =
15    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$&'()*+,/:;=?@-._~";
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct SceneString(String);
19
20impl SceneString {
21    /// 创建新的 SceneString,进行验证
22    pub fn new(s: &str) -> Result<Self, ValidationSceneError> {
23        if s.len() > 32 {
24            return Err(ValidationSceneError::TooLong);
25        }
26
27        for c in s.chars() {
28            if !VALID_CHARS.contains(c) {
29                return Err(ValidationSceneError::InvalidChar(c));
30            }
31        }
32
33        Ok(SceneString(s.to_string()))
34    }
35
36    /// 获取内部字符串引用
37    pub fn as_str(&self) -> &str {
38        &self.0
39    }
40
41    /// 获取内部字符串
42    pub fn into_inner(self) -> String {
43        self.0
44    }
45
46    /// 检查字符串是否为空
47    pub fn is_empty(&self) -> bool {
48        self.0.is_empty()
49    }
50
51    /// 获取字符串长度
52    pub fn len(&self) -> usize {
53        self.0.len()
54    }
55}
56
57impl FromStr for SceneString {
58    type Err = ValidationSceneError;
59
60    fn from_str(s: &str) -> Result<Self, Self::Err> {
61        SceneString::new(s)
62    }
63}
64
65impl fmt::Display for SceneString {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        write!(f, "{}", self.0)
68    }
69}
70
71impl TryFrom<String> for SceneString {
72    type Error = ValidationSceneError;
73
74    fn try_from(value: String) -> Result<Self, Self::Error> {
75        SceneString::new(&value)
76    }
77}
78
79impl TryFrom<&str> for SceneString {
80    type Error = ValidationSceneError;
81
82    fn try_from(value: &str) -> Result<Self, Self::Error> {
83        SceneString::new(value)
84    }
85}
86
87// 为方便使用,实现 Deref
88impl std::ops::Deref for SceneString {
89    type Target = str;
90
91    fn deref(&self) -> &Self::Target {
92        &self.0
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_valid_string() {
102        // 测试合法字符串
103        assert!(SceneString::new("hello123").is_ok());
104        assert!(SceneString::new("HELLO_WORLD-123").is_ok());
105        assert!(SceneString::new("test!#$&'()*+,/:;=?@-._~").is_ok());
106
107        // 测试长度限制
108        let long_string = "a".repeat(33);
109        assert!(matches!(
110            SceneString::new(&long_string),
111            Err(ValidationSceneError::TooLong)
112        ));
113
114        // 测试边界情况
115        let max_length_string = "a".repeat(32);
116        assert!(SceneString::new(&max_length_string).is_ok());
117
118        // 测试非法字符
119        assert!(matches!(
120            SceneString::new("hello world"), // 包含空格
121            Err(ValidationSceneError::InvalidChar(' '))
122        ));
123
124        assert!(matches!(
125            SceneString::new("中文"), // 包含中文
126            Err(ValidationSceneError::InvalidChar(_))
127        ));
128    }
129
130    #[test]
131    fn test_conversions() {
132        // 测试 FromStr
133        let from_str = "test123".parse::<SceneString>();
134        assert!(from_str.is_ok());
135
136        // 测试 TryFrom
137        let from_string = SceneString::try_from("test123".to_string());
138        assert!(from_string.is_ok());
139
140        let from_str_ref = SceneString::try_from("test123");
141        assert!(from_str_ref.is_ok());
142    }
143}