1use serde::Serialize;
4use serde_json::{Map, Value};
5use std::convert::TryFrom;
6use std::str::FromStr;
7use thiserror::Error;
8
9#[derive(Clone)]
13pub enum AnyInput<S: Serialize + Clone> {
14 String(String),
16 Serialize(S),
18}
19
20#[derive(Debug, Error)]
26pub enum AnyInputError {
27 #[error("Failed to parse JSON from string: {0}")]
29 JsonParseError(#[from] serde_json::Error),
30
31 #[error("Expected JSON object, but got {value_type}")]
33 NotAnObject {
34 value_type: String,
36 },
37
38 #[error("Invalid JSON string: {message}")]
40 InvalidJson {
41 message: String,
43 },
44}
45
46impl AnyInputError {
47 pub fn not_an_object(value: &Value) -> Self {
49 let value_type = match value {
50 Value::Null => "null".to_string(),
51 Value::Bool(_) => "boolean".to_string(),
52 Value::Number(_) => "number".to_string(),
53 Value::String(_) => "string".to_string(),
54 Value::Array(_) => "array".to_string(),
55 Value::Object(_) => "object".to_string(), };
57
58 AnyInputError::NotAnObject { value_type }
59 }
60}
61
62impl FromStr for AnyInput<serde_json::Value> {
81 type Err = AnyInputError;
82
83 fn from_str(s: &str) -> Result<Self, Self::Err> {
84 let value = serde_json::from_str(s)?;
85 Ok(AnyInput::Serialize(value))
86 }
87}
88
89impl<S: Serialize + Clone> From<S> for AnyInput<S> {
90 fn from(value: S) -> Self {
91 AnyInput::Serialize(value)
92 }
93}
94
95impl<S: Serialize + Clone> TryFrom<AnyInput<S>> for Map<String, Value> {
113 type Error = AnyInputError;
114
115 fn try_from(input: AnyInput<S>) -> Result<Self, Self::Error> {
116 match input {
117 AnyInput::String(value) => {
118 let json_value = serde_json::from_str::<Value>(&value)?;
120
121 json_value
123 .as_object()
124 .cloned()
125 .ok_or_else(|| AnyInputError::not_an_object(&json_value))
126 }
127 AnyInput::Serialize(value) => {
128 let json_value = serde_json::to_value(value)?;
130
131 json_value
133 .as_object()
134 .cloned()
135 .ok_or_else(|| AnyInputError::not_an_object(&json_value))
136 }
137 }
138 }
139}
140
141#[derive(Serialize, PartialEq, Clone)]
146pub struct PhantomSignature {}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_from_str_valid_json() {
154 let json_str = r#"{"type": "post", "text": "Hello", "count": 42}"#;
155 let result: Result<AnyInput<serde_json::Value>, _> = json_str.parse();
156
157 assert!(result.is_ok());
158
159 let input = result.unwrap();
160 match input {
161 AnyInput::Serialize(value) => {
162 assert_eq!(value["type"], "post");
163 assert_eq!(value["text"], "Hello");
164 assert_eq!(value["count"], 42);
165 }
166 _ => panic!("Expected AnyInput::Serialize variant"),
167 }
168 }
169
170 #[test]
171 fn test_from_str_invalid_json() {
172 let invalid_json = r#"{"type": "post", "text": "Hello" invalid json"#;
173 let result: Result<AnyInput<serde_json::Value>, _> = invalid_json.parse();
174
175 assert!(result.is_err());
176 }
177
178 #[test]
179 fn test_from_str_array() {
180 let json_array = r#"[1, 2, 3, "four"]"#;
181 let result: Result<AnyInput<serde_json::Value>, _> = json_array.parse();
182
183 assert!(result.is_ok());
184
185 let input = result.unwrap();
186 match input {
187 AnyInput::Serialize(value) => {
188 assert!(value.is_array());
189 let array = value.as_array().unwrap();
190 assert_eq!(array.len(), 4);
191 assert_eq!(array[0], 1);
192 assert_eq!(array[3], "four");
193 }
194 _ => panic!("Expected AnyInput::Serialize variant"),
195 }
196 }
197
198 #[test]
199 fn test_from_str_null() {
200 let null_str = "null";
201 let result: Result<AnyInput<serde_json::Value>, _> = null_str.parse();
202
203 assert!(result.is_ok());
204
205 let input = result.unwrap();
206 match input {
207 AnyInput::Serialize(value) => {
208 assert!(value.is_null());
209 }
210 _ => panic!("Expected AnyInput::Serialize variant"),
211 }
212 }
213
214 #[test]
215 fn test_from_str_with_use() {
216 let input: AnyInput<serde_json::Value> = r#"{"$type": "app.bsky.feed.post"}"#
218 .parse()
219 .expect("Failed to parse JSON");
220
221 match input {
222 AnyInput::Serialize(value) => {
223 assert_eq!(value["$type"], "app.bsky.feed.post");
224 }
225 _ => panic!("Expected AnyInput::Serialize variant"),
226 }
227 }
228
229 #[test]
230 fn test_try_into_from_string() {
231 use std::convert::TryInto;
232
233 let input = AnyInput::<Value>::String(r#"{"type": "post", "text": "Hello"}"#.to_string());
234 let result: Result<Map<String, Value>, _> = input.try_into();
235
236 assert!(result.is_ok());
237 let map = result.unwrap();
238 assert_eq!(map.get("type").unwrap(), "post");
239 assert_eq!(map.get("text").unwrap(), "Hello");
240 }
241
242 #[test]
243 fn test_try_into_from_serialize() {
244 use serde_json::json;
245 use std::convert::TryInto;
246
247 let input = AnyInput::Serialize(json!({"$type": "app.bsky.feed.post", "count": 42}));
248 let result: Result<Map<String, Value>, _> = input.try_into();
249
250 assert!(result.is_ok());
251 let map = result.unwrap();
252 assert_eq!(map.get("$type").unwrap(), "app.bsky.feed.post");
253 assert_eq!(map.get("count").unwrap(), 42);
254 }
255
256 #[test]
257 fn test_try_into_string_not_object() {
258 use std::convert::TryInto;
259
260 let input = AnyInput::<Value>::String(r#"["array", "not", "object"]"#.to_string());
261 let result: Result<Map<String, Value>, AnyInputError> = input.try_into();
262
263 assert!(result.is_err());
264 match result.unwrap_err() {
265 AnyInputError::NotAnObject { value_type } => {
266 assert_eq!(value_type, "array");
267 }
268 _ => panic!("Expected NotAnObject error"),
269 }
270 }
271
272 #[test]
273 fn test_try_into_serialize_not_object() {
274 use serde_json::json;
275 use std::convert::TryInto;
276
277 let input = AnyInput::Serialize(json!([1, 2, 3]));
278 let result: Result<Map<String, Value>, AnyInputError> = input.try_into();
279
280 assert!(result.is_err());
281 match result.unwrap_err() {
282 AnyInputError::NotAnObject { value_type } => {
283 assert_eq!(value_type, "array");
284 }
285 _ => panic!("Expected NotAnObject error"),
286 }
287 }
288
289 #[test]
290 fn test_try_into_invalid_json_string() {
291 use std::convert::TryInto;
292
293 let input = AnyInput::<Value>::String("not valid json".to_string());
294 let result: Result<Map<String, Value>, AnyInputError> = input.try_into();
295
296 assert!(result.is_err());
297 match result.unwrap_err() {
298 AnyInputError::JsonParseError(_) => {}
299 _ => panic!("Expected JsonParseError"),
300 }
301 }
302
303 #[test]
304 fn test_try_into_null() {
305 use serde_json::json;
306 use std::convert::TryInto;
307
308 let input = AnyInput::Serialize(json!(null));
309 let result: Result<Map<String, Value>, AnyInputError> = input.try_into();
310
311 assert!(result.is_err());
312 match result.unwrap_err() {
313 AnyInputError::NotAnObject { value_type } => {
314 assert_eq!(value_type, "null");
315 }
316 _ => panic!("Expected NotAnObject error"),
317 }
318 }
319
320 #[test]
321 fn test_any_input_error_not_an_object() {
322 use serde_json::json;
323
324 let err = AnyInputError::not_an_object(&json!(null));
326 match err {
327 AnyInputError::NotAnObject { value_type } => {
328 assert_eq!(value_type, "null");
329 }
330 _ => panic!("Expected NotAnObject error"),
331 }
332
333 let err = AnyInputError::not_an_object(&json!(true));
335 match err {
336 AnyInputError::NotAnObject { value_type } => {
337 assert_eq!(value_type, "boolean");
338 }
339 _ => panic!("Expected NotAnObject error"),
340 }
341
342 let err = AnyInputError::not_an_object(&json!(42));
344 match err {
345 AnyInputError::NotAnObject { value_type } => {
346 assert_eq!(value_type, "number");
347 }
348 _ => panic!("Expected NotAnObject error"),
349 }
350
351 let err = AnyInputError::not_an_object(&json!("hello"));
353 match err {
354 AnyInputError::NotAnObject { value_type } => {
355 assert_eq!(value_type, "string");
356 }
357 _ => panic!("Expected NotAnObject error"),
358 }
359
360 let err = AnyInputError::not_an_object(&json!([1, 2, 3]));
362 match err {
363 AnyInputError::NotAnObject { value_type } => {
364 assert_eq!(value_type, "array");
365 }
366 _ => panic!("Expected NotAnObject error"),
367 }
368 }
369
370 #[test]
371 fn test_error_display() {
372 use serde_json::json;
373
374 let err = AnyInputError::not_an_object(&json!(42));
376 assert_eq!(err.to_string(), "Expected JSON object, but got number");
377
378 let err = AnyInputError::InvalidJson {
380 message: "unexpected token".to_string(),
381 };
382 assert_eq!(err.to_string(), "Invalid JSON string: unexpected token");
383 }
384}