1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
5#[serde(transparent)]
6pub struct AppId(pub String);
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[serde(transparent)]
10pub struct BotId(pub String);
11
12impl From<String> for BotId {
13 fn from(value: String) -> Self {
14 Self(value)
15 }
16}
17
18impl From<&str> for BotId {
19 fn from(value: &str) -> Self {
20 Self(value.to_string())
21 }
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct BotContext {
27 pub app_id: AppId,
28 pub token: String,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub webhook_secret: Option<String>,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub description: Option<String>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36#[serde(rename_all = "camelCase")]
37pub struct CallbackEnvelope<T> {
38 pub appid: String,
39 pub data: T,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43#[serde(rename_all = "camelCase")]
44pub struct ApiEnvelope<T> {
45 pub ret: i32,
46 pub msg: String,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub data: Option<T>,
49}
50
51#[derive(Debug, Error)]
52pub enum GeweError {
53 #[error("http error: {0}")]
54 Http(String),
55 #[error("api error code={code}: {message}")]
56 Api { code: i32, message: String },
57 #[error("decode error: {0}")]
58 Decode(String),
59 #[error("missing data")]
60 MissingData,
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn test_app_id_serialization() {
69 let app_id = AppId("test_app_id".to_string());
70 let json = serde_json::to_string(&app_id).unwrap();
71 assert_eq!(json, r#""test_app_id""#);
72 }
73
74 #[test]
75 fn test_app_id_deserialization() {
76 let json = r#""test_app_id""#;
77 let app_id: AppId = serde_json::from_str(json).unwrap();
78 assert_eq!(app_id.0, "test_app_id");
79 }
80
81 #[test]
82 fn test_bot_id_from_string() {
83 let bot_id: BotId = "test_bot_id".to_string().into();
84 assert_eq!(bot_id.0, "test_bot_id");
85 }
86
87 #[test]
88 fn test_bot_id_from_str() {
89 let bot_id: BotId = "test_bot_id".into();
90 assert_eq!(bot_id.0, "test_bot_id");
91 }
92
93 #[test]
94 fn test_bot_id_serialization() {
95 let bot_id = BotId("test_bot_id".to_string());
96 let json = serde_json::to_string(&bot_id).unwrap();
97 assert_eq!(json, r#""test_bot_id""#);
98 }
99
100 #[test]
101 fn test_bot_id_deserialization() {
102 let json = r#""test_bot_id""#;
103 let bot_id: BotId = serde_json::from_str(json).unwrap();
104 assert_eq!(bot_id.0, "test_bot_id");
105 }
106
107 #[test]
108 fn test_bot_context_serialization() {
109 let ctx = BotContext {
110 app_id: AppId("app123".to_string()),
111 token: "token123".to_string(),
112 webhook_secret: Some("secret123".to_string()),
113 description: Some("Test bot".to_string()),
114 };
115 let json = serde_json::to_string(&ctx).unwrap();
116 assert!(json.contains("app123"));
117 assert!(json.contains("token123"));
118 assert!(json.contains("secret123"));
119 assert!(json.contains("Test bot"));
120 }
121
122 #[test]
123 fn test_bot_context_deserialization() {
124 let json = r#"{
125 "appId": "app123",
126 "token": "token123",
127 "webhookSecret": "secret123",
128 "description": "Test bot"
129 }"#;
130 let ctx: BotContext = serde_json::from_str(json).unwrap();
131 assert_eq!(ctx.app_id.0, "app123");
132 assert_eq!(ctx.token, "token123");
133 assert_eq!(ctx.webhook_secret, Some("secret123".to_string()));
134 assert_eq!(ctx.description, Some("Test bot".to_string()));
135 }
136
137 #[test]
138 fn test_bot_context_optional_fields() {
139 let json = r#"{
140 "appId": "app123",
141 "token": "token123"
142 }"#;
143 let ctx: BotContext = serde_json::from_str(json).unwrap();
144 assert_eq!(ctx.app_id.0, "app123");
145 assert_eq!(ctx.token, "token123");
146 assert_eq!(ctx.webhook_secret, None);
147 assert_eq!(ctx.description, None);
148 }
149
150 #[test]
151 fn test_callback_envelope_serialization() {
152 let envelope = CallbackEnvelope {
153 appid: "app123".to_string(),
154 data: "test_data".to_string(),
155 };
156 let json = serde_json::to_string(&envelope).unwrap();
157 assert!(json.contains("app123"));
158 assert!(json.contains("test_data"));
159 }
160
161 #[test]
162 fn test_callback_envelope_deserialization() {
163 let json = r#"{"appid":"app123","data":"test_data"}"#;
164 let envelope: CallbackEnvelope<String> = serde_json::from_str(json).unwrap();
165 assert_eq!(envelope.appid, "app123");
166 assert_eq!(envelope.data, "test_data");
167 }
168
169 #[test]
170 fn test_api_envelope_with_data() {
171 let envelope = ApiEnvelope {
172 ret: 200,
173 msg: "success".to_string(),
174 data: Some("result".to_string()),
175 };
176 let json = serde_json::to_string(&envelope).unwrap();
177 assert!(json.contains("200"));
178 assert!(json.contains("success"));
179 assert!(json.contains("result"));
180 }
181
182 #[test]
183 fn test_api_envelope_without_data() {
184 let json = r#"{"ret":200,"msg":"success"}"#;
185 let envelope: ApiEnvelope<String> = serde_json::from_str(json).unwrap();
186 assert_eq!(envelope.ret, 200);
187 assert_eq!(envelope.msg, "success");
188 assert_eq!(envelope.data, None);
189 }
190
191 #[test]
192 fn test_gewe_error_http() {
193 let err = GeweError::Http("connection failed".to_string());
194 assert_eq!(err.to_string(), "http error: connection failed");
195 }
196
197 #[test]
198 fn test_gewe_error_api() {
199 let err = GeweError::Api {
200 code: 404,
201 message: "not found".to_string(),
202 };
203 assert_eq!(err.to_string(), "api error code=404: not found");
204 }
205
206 #[test]
207 fn test_gewe_error_decode() {
208 let err = GeweError::Decode("invalid json".to_string());
209 assert_eq!(err.to_string(), "decode error: invalid json");
210 }
211
212 #[test]
213 fn test_gewe_error_missing_data() {
214 let err = GeweError::MissingData;
215 assert_eq!(err.to_string(), "missing data");
216 }
217}