wechat_minapp/new_type/
scene.rs1use 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
13const VALID_CHARS: &str =
15 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$&'()*+,/:;=?@-._~";
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct SceneString(String);
19
20impl SceneString {
21 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 pub fn as_str(&self) -> &str {
38 &self.0
39 }
40
41 pub fn into_inner(self) -> String {
43 self.0
44 }
45
46 pub fn is_empty(&self) -> bool {
48 self.0.is_empty()
49 }
50
51 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
87impl 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 assert!(SceneString::new("hello123").is_ok());
104 assert!(SceneString::new("HELLO_WORLD-123").is_ok());
105 assert!(SceneString::new("test!#$&'()*+,/:;=?@-._~").is_ok());
106
107 let long_string = "a".repeat(33);
109 assert!(matches!(
110 SceneString::new(&long_string),
111 Err(ValidationSceneError::TooLong)
112 ));
113
114 let max_length_string = "a".repeat(32);
116 assert!(SceneString::new(&max_length_string).is_ok());
117
118 assert!(matches!(
120 SceneString::new("hello world"), Err(ValidationSceneError::InvalidChar(' '))
122 ));
123
124 assert!(matches!(
125 SceneString::new("中文"), Err(ValidationSceneError::InvalidChar(_))
127 ));
128 }
129
130 #[test]
131 fn test_conversions() {
132 let from_str = "test123".parse::<SceneString>();
134 assert!(from_str.is_ok());
135
136 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}