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)]
19pub struct SceneString(String);
20
21impl SceneString {
22 pub fn new(s: &str) -> Result<Self, ValidationSceneError> {
24 if s.len() > 32 {
25 return Err(ValidationSceneError::TooLong);
26 }
27
28 for c in s.chars() {
29 if !VALID_CHARS.contains(c) {
30 return Err(ValidationSceneError::InvalidChar(c));
31 }
32 }
33
34 Ok(SceneString(s.to_string()))
35 }
36
37 pub fn as_str(&self) -> &str {
39 &self.0
40 }
41
42 pub fn into_inner(self) -> String {
44 self.0
45 }
46
47 pub fn is_empty(&self) -> bool {
49 self.0.is_empty()
50 }
51
52 pub fn len(&self) -> usize {
54 self.0.len()
55 }
56}
57
58impl FromStr for SceneString {
59 type Err = ValidationSceneError;
60
61 fn from_str(s: &str) -> Result<Self, Self::Err> {
62 SceneString::new(s)
63 }
64}
65
66impl fmt::Display for SceneString {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(f, "{}", self.0)
69 }
70}
71
72impl TryFrom<String> for SceneString {
73 type Error = ValidationSceneError;
74
75 fn try_from(value: String) -> Result<Self, Self::Error> {
76 SceneString::new(&value)
77 }
78}
79
80impl TryFrom<&str> for SceneString {
81 type Error = ValidationSceneError;
82
83 fn try_from(value: &str) -> Result<Self, Self::Error> {
84 SceneString::new(value)
85 }
86}
87
88impl std::ops::Deref for SceneString {
90 type Target = str;
91
92 fn deref(&self) -> &Self::Target {
93 &self.0
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_valid_string() {
103 assert!(SceneString::new("hello123").is_ok());
105 assert!(SceneString::new("HELLO_WORLD-123").is_ok());
106 assert!(SceneString::new("test!#$&'()*+,/:;=?@-._~").is_ok());
107
108 let long_string = "a".repeat(33);
110 assert!(matches!(
111 SceneString::new(&long_string),
112 Err(ValidationSceneError::TooLong)
113 ));
114
115 let max_length_string = "a".repeat(32);
117 assert!(SceneString::new(&max_length_string).is_ok());
118
119 assert!(matches!(
121 SceneString::new("hello world"), Err(ValidationSceneError::InvalidChar(' '))
123 ));
124
125 assert!(matches!(
126 SceneString::new("中文"), Err(ValidationSceneError::InvalidChar(_))
128 ));
129 }
130
131 #[test]
132 fn test_conversions() {
133 let from_str = "test123".parse::<SceneString>();
135 assert!(from_str.is_ok());
136
137 let from_string = SceneString::try_from("test123".to_string());
139 assert!(from_string.is_ok());
140
141 let from_str_ref = SceneString::try_from("test123");
142 assert!(from_str_ref.is_ok());
143 }
144}