mockforge_grpc/dynamic/http_bridge/
converters.rs

1//! JSON to Protobuf conversion utilities
2//!
3//! This module provides utilities to convert between JSON and protobuf messages,
4//! enabling HTTP REST API access to gRPC services.
5
6use base64::{engine::general_purpose, Engine as _};
7use prost_reflect::{
8    DescriptorPool, DynamicMessage, FieldDescriptor, Kind, MessageDescriptor, ReflectMessage, Value,
9};
10use serde_json::{self, Value as JsonValue};
11use std::string::String as StdString;
12use tracing::{debug, warn};
13
14/// Errors that can occur during JSON/protobuf conversion
15#[derive(Debug, thiserror::Error)]
16pub enum ConversionError {
17    #[error("Field '{field}' required but missing from JSON")]
18    MissingField { field: String },
19    #[error("Invalid value for field '{field}': {message}")]
20    InvalidValue { field: String, message: String },
21    #[error("Unknown field '{field}' in message")]
22    UnknownField { field: String },
23    #[error("Type mismatch for field '{field}': expected {expected}, got {actual}")]
24    TypeMismatch {
25        field: String,
26        expected: String,
27        actual: String,
28    },
29    #[error("Failed to convert nested message: {0}")]
30    NestedError(String),
31    #[error("Protobuf reflection error: {0}")]
32    ProtobufError(String),
33}
34
35impl ConversionError {
36    // with_field method removed - was unused
37}
38
39/// Converter for JSON to Protobuf and vice versa
40#[derive(Debug, Clone)]
41pub struct ProtobufJsonConverter {
42    /// Descriptor pool containing protobuf definitions
43    #[allow(dead_code)] // Used in future implementations
44    pool: DescriptorPool,
45}
46
47impl ProtobufJsonConverter {
48    /// Create a new converter with the given descriptor pool
49    pub fn new(pool: DescriptorPool) -> Self {
50        Self { pool }
51    }
52
53    /// Convert JSON to a protobuf DynamicMessage
54    pub fn json_to_protobuf(
55        &self,
56        descriptor: &MessageDescriptor,
57        json: &JsonValue,
58    ) -> Result<DynamicMessage, ConversionError> {
59        debug!("Converting JSON to protobuf message: {}", descriptor.name());
60
61        let mut message = DynamicMessage::new(descriptor.clone());
62
63        let obj = json.as_object().ok_or_else(|| ConversionError::InvalidValue {
64            field: descriptor.name().to_string(),
65            message: "Expected JSON object".to_string(),
66        })?;
67
68        // Convert each JSON field to protobuf field
69        for (field_name, json_value) in obj {
70            self.set_field_from_json(&mut message, field_name, json_value)?;
71        }
72
73        // Set default values for required fields that weren't provided
74        self.set_default_values_for_missing_fields(&mut message)?;
75
76        Ok(message)
77    }
78
79    /// Convert a protobuf DynamicMessage to JSON
80    pub fn protobuf_to_json(
81        &self,
82        descriptor: &MessageDescriptor,
83        message: &DynamicMessage,
84    ) -> Result<JsonValue, ConversionError> {
85        debug!("Converting protobuf message to JSON: {}", descriptor.name());
86
87        let mut obj = serde_json::Map::new();
88
89        for field in descriptor.fields() {
90            let field_name = field.name();
91
92            if message.has_field(&field) {
93                let field_value = message.get_field(&field).into_owned();
94                let json_value = self.convert_protobuf_value_to_json(&field, field_value)?;
95                obj.insert(field_name.to_string(), json_value);
96            } else if field.supports_presence() && !field.is_list() {
97                // Field supports presence but isn't set - this means null/absent
98                // In JSON, we don't include null values for optional fields by default
99                // But we can include them as null if the protobuf field was explicitly set to default
100                // For now, we'll skip unset optional fields
101            }
102        }
103
104        Ok(JsonValue::Object(obj))
105    }
106
107    /// Convert JSON to a protobuf value for a specific field
108    fn set_field_from_json(
109        &self,
110        message: &mut DynamicMessage,
111        field_name: &str,
112        json_value: &JsonValue,
113    ) -> Result<(), ConversionError> {
114        let field = message.descriptor().get_field_by_name(field_name).ok_or_else(|| {
115            ConversionError::UnknownField {
116                field: field_name.to_string(),
117            }
118        })?;
119
120        let protobuf_value = self.convert_json_value_to_protobuf(&field, json_value)?;
121        message.set_field(&field, protobuf_value);
122
123        Ok(())
124    }
125
126    /// Convert a JSON value to a protobuf Value
127    fn convert_json_value_to_protobuf(
128        &self,
129        field: &FieldDescriptor,
130        json_value: &JsonValue,
131    ) -> Result<Value, ConversionError> {
132        use prost_reflect::Kind::*;
133
134        match field.kind() {
135            Message(ref message_descriptor) => {
136                match json_value {
137                    JsonValue::Object(_) => {
138                        let nested_message =
139                            self.json_to_protobuf(message_descriptor, json_value)?;
140                        Ok(Value::Message(nested_message))
141                    }
142                    JsonValue::Null if field.supports_presence() => {
143                        // For optional message fields, null means unset
144                        Ok(Value::Message(DynamicMessage::new(message_descriptor.clone())))
145                    }
146                    _ => Err(ConversionError::TypeMismatch {
147                        field: field.name().to_string(),
148                        expected: "object".to_string(),
149                        actual: self.json_type_name(json_value),
150                    }),
151                }
152            }
153            Enum(ref enum_descriptor) => {
154                match json_value {
155                    JsonValue::String(s) => {
156                        // Try to find enum value by name first
157                        if let Some(enum_value) = enum_descriptor.get_value_by_name(s) {
158                            Ok(Value::EnumNumber(enum_value.number()))
159                        } else {
160                            // Try to parse as number
161                            match s.parse::<i32>() {
162                                Ok(num) => {
163                                    if enum_descriptor.get_value(num).is_some() {
164                                        Ok(Value::EnumNumber(num))
165                                    } else {
166                                        Err(ConversionError::InvalidValue {
167                                            field: field.name().to_string(),
168                                            message: format!("Invalid enum value: {}", num),
169                                        })
170                                    }
171                                }
172                                Err(_) => Err(ConversionError::InvalidValue {
173                                    field: field.name().to_string(),
174                                    message: format!("Unknown enum value: {}", s),
175                                }),
176                            }
177                        }
178                    }
179                    JsonValue::Number(n) => {
180                        if let Some(num) = n.as_i64() {
181                            let num = num as i32;
182                            if enum_descriptor.get_value(num).is_some() {
183                                Ok(Value::EnumNumber(num))
184                            } else {
185                                Err(ConversionError::InvalidValue {
186                                    field: field.name().to_string(),
187                                    message: format!("Invalid enum number: {}", num),
188                                })
189                            }
190                        } else {
191                            Err(ConversionError::TypeMismatch {
192                                field: field.name().to_string(),
193                                expected: "integer".to_string(),
194                                actual: "number".to_string(),
195                            })
196                        }
197                    }
198                    JsonValue::Null if field.supports_presence() => {
199                        Ok(Value::EnumNumber(0)) // Default enum value
200                    }
201                    _ => Err(ConversionError::TypeMismatch {
202                        field: field.name().to_string(),
203                        expected: "string or number".to_string(),
204                        actual: self.json_type_name(json_value),
205                    }),
206                }
207            }
208            String => match json_value {
209                JsonValue::String(s) => Ok(Value::String(s.clone())),
210                JsonValue::Null if field.supports_presence() => Ok(Value::String(StdString::new())),
211                _ => Err(ConversionError::TypeMismatch {
212                    field: field.name().to_string(),
213                    expected: "string".to_string(),
214                    actual: self.json_type_name(json_value),
215                }),
216            },
217            Int32 | Sint32 | Sfixed32 => match json_value {
218                JsonValue::Number(n) => {
219                    if let Some(i) = n.as_i64() {
220                        Ok(Value::I32(i as i32))
221                    } else {
222                        Err(ConversionError::InvalidValue {
223                            field: field.name().to_string(),
224                            message: "Number out of range for int32".to_string(),
225                        })
226                    }
227                }
228                JsonValue::String(s) => match s.parse::<i32>() {
229                    Ok(i) => Ok(Value::I32(i)),
230                    Err(_) => Err(ConversionError::InvalidValue {
231                        field: field.name().to_string(),
232                        message: format!("Invalid int32 value: {}", s),
233                    }),
234                },
235                JsonValue::Null if field.supports_presence() => Ok(Value::I32(0)),
236                _ => Err(ConversionError::TypeMismatch {
237                    field: field.name().to_string(),
238                    expected: "number or string".to_string(),
239                    actual: self.json_type_name(json_value),
240                }),
241            },
242            Int64 | Sint64 | Sfixed64 => match json_value {
243                JsonValue::Number(n) => {
244                    if let Some(i) = n.as_i64() {
245                        Ok(Value::I64(i))
246                    } else {
247                        Err(ConversionError::InvalidValue {
248                            field: field.name().to_string(),
249                            message: "Number out of range for int64".to_string(),
250                        })
251                    }
252                }
253                JsonValue::String(s) => match s.parse::<i64>() {
254                    Ok(i) => Ok(Value::I64(i)),
255                    Err(_) => Err(ConversionError::InvalidValue {
256                        field: field.name().to_string(),
257                        message: format!("Invalid int64 value: {}", s),
258                    }),
259                },
260                JsonValue::Null if field.supports_presence() => Ok(Value::I64(0)),
261                _ => Err(ConversionError::TypeMismatch {
262                    field: field.name().to_string(),
263                    expected: "number or string".to_string(),
264                    actual: self.json_type_name(json_value),
265                }),
266            },
267            Uint32 | Fixed32 => match json_value {
268                JsonValue::Number(n) => {
269                    if let Some(i) = n.as_u64() {
270                        Ok(Value::U32(i as u32))
271                    } else {
272                        Err(ConversionError::InvalidValue {
273                            field: field.name().to_string(),
274                            message: "Number out of range for uint32".to_string(),
275                        })
276                    }
277                }
278                JsonValue::String(s) => match s.parse::<u32>() {
279                    Ok(i) => Ok(Value::U32(i)),
280                    Err(_) => Err(ConversionError::InvalidValue {
281                        field: field.name().to_string(),
282                        message: format!("Invalid uint32 value: {}", s),
283                    }),
284                },
285                JsonValue::Null if field.supports_presence() => Ok(Value::U32(0)),
286                _ => Err(ConversionError::TypeMismatch {
287                    field: field.name().to_string(),
288                    expected: "number or string".to_string(),
289                    actual: self.json_type_name(json_value),
290                }),
291            },
292            Uint64 | Fixed64 => match json_value {
293                JsonValue::Number(n) => {
294                    if let Some(i) = n.as_u64() {
295                        Ok(Value::U64(i))
296                    } else {
297                        Err(ConversionError::InvalidValue {
298                            field: field.name().to_string(),
299                            message: "Number out of range for uint64".to_string(),
300                        })
301                    }
302                }
303                JsonValue::String(s) => match s.parse::<u64>() {
304                    Ok(i) => Ok(Value::U64(i)),
305                    Err(_) => Err(ConversionError::InvalidValue {
306                        field: field.name().to_string(),
307                        message: format!("Invalid uint64 value: {}", s),
308                    }),
309                },
310                JsonValue::Null if field.supports_presence() => Ok(Value::U64(0)),
311                _ => Err(ConversionError::TypeMismatch {
312                    field: field.name().to_string(),
313                    expected: "number or string".to_string(),
314                    actual: self.json_type_name(json_value),
315                }),
316            },
317            Float => match json_value {
318                JsonValue::Number(n) => {
319                    if let Some(f) = n.as_f64() {
320                        Ok(Value::F32(f as f32))
321                    } else {
322                        Ok(Value::F32(0.0))
323                    }
324                }
325                JsonValue::String(s) => match s.parse::<f32>() {
326                    Ok(f) => Ok(Value::F32(f)),
327                    Err(_) => Err(ConversionError::InvalidValue {
328                        field: field.name().to_string(),
329                        message: format!("Invalid float value: {}", s),
330                    }),
331                },
332                JsonValue::Null if field.supports_presence() => Ok(Value::F32(0.0)),
333                _ => Err(ConversionError::TypeMismatch {
334                    field: field.name().to_string(),
335                    expected: "number or string".to_string(),
336                    actual: self.json_type_name(json_value),
337                }),
338            },
339            Double => match json_value {
340                JsonValue::Number(n) => {
341                    if let Some(f) = n.as_f64() {
342                        Ok(Value::F64(f))
343                    } else {
344                        Ok(Value::F64(0.0))
345                    }
346                }
347                JsonValue::String(s) => match s.parse::<f64>() {
348                    Ok(f) => Ok(Value::F64(f)),
349                    Err(_) => Err(ConversionError::InvalidValue {
350                        field: field.name().to_string(),
351                        message: format!("Invalid double value: {}", s),
352                    }),
353                },
354                JsonValue::Null if field.supports_presence() => Ok(Value::F64(0.0)),
355                _ => Err(ConversionError::TypeMismatch {
356                    field: field.name().to_string(),
357                    expected: "number or string".to_string(),
358                    actual: self.json_type_name(json_value),
359                }),
360            },
361            Bool => match json_value {
362                JsonValue::Bool(b) => Ok(Value::Bool(*b)),
363                JsonValue::String(s) => match s.to_lowercase().as_str() {
364                    "true" | "1" | "yes" | "on" => Ok(Value::Bool(true)),
365                    "false" | "0" | "no" | "off" | "" => Ok(Value::Bool(false)),
366                    _ => Err(ConversionError::InvalidValue {
367                        field: field.name().to_string(),
368                        message: format!("Invalid boolean value: {}", s),
369                    }),
370                },
371                JsonValue::Number(n) => {
372                    if let Some(i) = n.as_i64() {
373                        Ok(Value::Bool(i != 0))
374                    } else {
375                        Ok(Value::Bool(false))
376                    }
377                }
378                JsonValue::Null if field.supports_presence() => Ok(Value::Bool(false)),
379                _ => Err(ConversionError::TypeMismatch {
380                    field: field.name().to_string(),
381                    expected: "boolean, number, or string".to_string(),
382                    actual: self.json_type_name(json_value),
383                }),
384            },
385            Bytes => match json_value {
386                JsonValue::String(s) => match general_purpose::STANDARD.decode(s) {
387                    Ok(bytes) => Ok(Value::Bytes(bytes.into())),
388                    Err(_) => Err(ConversionError::InvalidValue {
389                        field: field.name().to_string(),
390                        message: format!("Invalid base64 string: {}", s),
391                    }),
392                },
393                JsonValue::Null if field.supports_presence() => Ok(Value::Bytes(vec![].into())),
394                _ => Err(ConversionError::TypeMismatch {
395                    field: field.name().to_string(),
396                    expected: "base64 string".to_string(),
397                    actual: self.json_type_name(json_value),
398                }),
399            },
400        }
401    }
402
403    /// Convert a protobuf Value to a JSON value
404    fn convert_protobuf_value_to_json(
405        &self,
406        field: &FieldDescriptor,
407        value: Value,
408    ) -> Result<JsonValue, ConversionError> {
409        use prost_reflect::Value::*;
410
411        Ok(match value {
412            String(s) => JsonValue::String(s),
413            I32(i) => JsonValue::Number(i.into()),
414            I64(i) => JsonValue::Number(i.into()),
415            U32(u) => JsonValue::Number(u.into()),
416            U64(u) => JsonValue::Number(u.into()),
417            F32(f) => {
418                if f.is_finite() {
419                    JsonValue::Number(serde_json::Number::from_f64(f as f64).unwrap_or(0.into()))
420                } else {
421                    JsonValue::Number(0.into())
422                }
423            }
424            F64(f) => {
425                if f.is_finite() {
426                    JsonValue::Number(serde_json::Number::from_f64(f).unwrap_or(0.into()))
427                } else {
428                    JsonValue::Number(0.into())
429                }
430            }
431            Bool(b) => JsonValue::Bool(b),
432            EnumNumber(n) => {
433                if let Kind::Enum(ref enum_descriptor) = field.kind() {
434                    if let Some(enum_value) = enum_descriptor.get_value(n) {
435                        JsonValue::String(enum_value.name().to_string())
436                    } else {
437                        // Fallback to number if enum value not found
438                        warn!("Unknown enum value {} for field {}", n, field.name());
439                        JsonValue::String(n.to_string())
440                    }
441                } else {
442                    JsonValue::String(n.to_string())
443                }
444            }
445            Bytes(b) => JsonValue::String(general_purpose::STANDARD.encode(b)),
446            Message(msg) => self.protobuf_to_json(&msg.descriptor(), &msg)?,
447            List(list) => {
448                let mut json_array = Vec::new();
449                for item in list {
450                    let json_item = self.convert_protobuf_value_to_json(field, item)?;
451                    json_array.push(json_item);
452                }
453                JsonValue::Array(json_array)
454            }
455            Map(map) => {
456                let mut json_obj = serde_json::Map::new();
457                for (key, value) in map {
458                    let json_key = match key {
459                        prost_reflect::MapKey::String(s) => serde_json::Value::String(s),
460                        prost_reflect::MapKey::I32(i) => serde_json::Value::Number(i.into()),
461                        prost_reflect::MapKey::I64(i) => serde_json::Value::Number(i.into()),
462                        prost_reflect::MapKey::Bool(b) => serde_json::Value::Bool(b),
463                        prost_reflect::MapKey::U32(u) => serde_json::Value::Number(u.into()),
464                        prost_reflect::MapKey::U64(u) => serde_json::Value::Number(u.into()),
465                    };
466                    let json_value = self.convert_protobuf_value_to_json(field, value)?;
467                    // JSON object keys must be strings
468                    let key_str = match json_key {
469                        JsonValue::String(s) => s,
470                        JsonValue::Number(n) => n.to_string(),
471                        JsonValue::Bool(b) => b.to_string(),
472                        _ => json_key.to_string(), // Fallback for other types
473                    };
474                    json_obj.insert(key_str, json_value);
475                }
476                JsonValue::Object(json_obj)
477            }
478        })
479    }
480
481    /// Set default values for missing required fields
482    fn set_default_values_for_missing_fields(
483        &self,
484        message: &mut DynamicMessage,
485    ) -> Result<(), ConversionError> {
486        let descriptor = message.descriptor();
487
488        for field in descriptor.fields() {
489            if !message.has_field(&field) {
490                // Only set defaults for non-repeated fields that don't support presence
491                if !field.is_list() && !field.supports_presence() {
492                    let default_value = self.get_default_value_for_field(&field)?;
493                    message.set_field(&field, default_value);
494                    debug!("Set default value for field: {}", field.name());
495                }
496            }
497        }
498
499        Ok(())
500    }
501
502    /// Get default value for a field based on its type
503    fn get_default_value_for_field(
504        &self,
505        field: &FieldDescriptor,
506    ) -> Result<Value, ConversionError> {
507        use prost_reflect::Kind::*;
508
509        Ok(match field.kind() {
510            String => Value::String(StdString::new()),
511            Int32 | Sint32 | Sfixed32 => Value::I32(0),
512            Int64 | Sint64 | Sfixed64 => Value::I64(0),
513            Uint32 | Fixed32 => Value::U32(0),
514            Uint64 | Fixed64 => Value::U64(0),
515            Float => Value::F32(0.0),
516            Double => Value::F64(0.0),
517            Bool => Value::Bool(false),
518            Bytes => Value::Bytes(vec![].into()),
519            Enum(_) => Value::EnumNumber(0),
520            Message(ref message_descriptor) => {
521                Value::Message(DynamicMessage::new(message_descriptor.clone()))
522            }
523        })
524    }
525
526    /// Get string representation of JSON value type
527    fn json_type_name(&self, value: &JsonValue) -> String {
528        match value {
529            JsonValue::Null => "null".to_string(),
530            JsonValue::Bool(_) => "boolean".to_string(),
531            JsonValue::Number(_) => "number".to_string(),
532            JsonValue::String(_) => "string".to_string(),
533            JsonValue::Array(_) => "array".to_string(),
534            JsonValue::Object(_) => "object".to_string(),
535        }
536    }
537
538    /// Handle repeated field conversion for JSON arrays
539    #[allow(dead_code)] // Used in future implementations
540    fn convert_json_array_to_protobuf_list(
541        &self,
542        field: &FieldDescriptor,
543        json_array: &[JsonValue],
544    ) -> Result<Value, ConversionError> {
545        let mut list = Vec::new();
546
547        for json_item in json_array {
548            let protobuf_item = self.convert_json_value_to_protobuf(field, json_item)?;
549            list.push(protobuf_item);
550        }
551
552        Ok(Value::List(list))
553    }
554}
555
556#[cfg(test)]
557mod tests {
558    use super::*;
559
560    #[test]
561    fn test_json_to_protobuf_simple_types() {
562        // This test requires having a descriptor pool with actual messages
563        // For now, we'll create a simple test that validates the converter creation
564        let pool = DescriptorPool::new();
565        let converter = ProtobufJsonConverter::new(pool);
566        assert!(converter.pool.services().count() == 0);
567    }
568
569    #[test]
570    fn test_default_values() {
571        let pool = DescriptorPool::new();
572        let converter = ProtobufJsonConverter::new(pool);
573
574        // Test that we can get default values for different field types
575        // Note: This is more of an integration test that would need actual descriptors
576        assert!(converter.pool.services().count() == 0);
577    }
578
579    #[test]
580    fn test_json_type_name() {
581        let pool = DescriptorPool::new();
582        let converter = ProtobufJsonConverter::new(pool);
583
584        assert_eq!(converter.json_type_name(&JsonValue::Null), "null");
585        assert_eq!(converter.json_type_name(&JsonValue::Bool(true)), "boolean");
586        assert_eq!(converter.json_type_name(&JsonValue::Number(42.into())), "number");
587        assert_eq!(converter.json_type_name(&JsonValue::String("test".to_string())), "string");
588        assert_eq!(converter.json_type_name(&JsonValue::Array(vec![])), "array");
589        assert_eq!(converter.json_type_name(&JsonValue::Object(serde_json::Map::new())), "object");
590    }
591
592    #[test]
593    fn test_conversion_error_display() {
594        let error = ConversionError::MissingField {
595            field: "test_field".to_string(),
596        };
597        assert!(error.to_string().contains("test_field"));
598
599        let error = ConversionError::InvalidValue {
600            field: "test_field".to_string(),
601            message: "invalid value".to_string(),
602        };
603        assert!(error.to_string().contains("test_field"));
604        assert!(error.to_string().contains("invalid value"));
605
606        let error = ConversionError::UnknownField {
607            field: "unknown_field".to_string(),
608        };
609        assert!(error.to_string().contains("unknown_field"));
610
611        let error = ConversionError::TypeMismatch {
612            field: "test_field".to_string(),
613            expected: "string".to_string(),
614            actual: "number".to_string(),
615        };
616        assert!(error.to_string().contains("test_field"));
617        assert!(error.to_string().contains("string"));
618        assert!(error.to_string().contains("number"));
619
620        let error = ConversionError::NestedError("nested error".to_string());
621        assert!(error.to_string().contains("nested error"));
622
623        let error = ConversionError::ProtobufError("protobuf error".to_string());
624        assert!(error.to_string().contains("protobuf error"));
625    }
626
627    #[test]
628    fn test_converter_creation() {
629        let pool = DescriptorPool::new();
630        let converter = ProtobufJsonConverter::new(pool.clone());
631
632        assert_eq!(converter.pool.services().count(), 0);
633
634        // Test that we can create multiple converters
635        let converter2 = ProtobufJsonConverter::new(pool);
636        assert_eq!(converter2.pool.services().count(), 0);
637    }
638
639    #[test]
640    fn test_json_value_type_detection() {
641        let pool = DescriptorPool::new();
642        let converter = ProtobufJsonConverter::new(pool);
643
644        // Test all JSON value types
645        assert_eq!(converter.json_type_name(&JsonValue::Null), "null");
646        assert_eq!(converter.json_type_name(&JsonValue::Bool(true)), "boolean");
647        assert_eq!(converter.json_type_name(&JsonValue::Bool(false)), "boolean");
648        assert_eq!(
649            converter.json_type_name(&JsonValue::Number(serde_json::Number::from(42))),
650            "number"
651        );
652        assert_eq!(
653            converter.json_type_name(&JsonValue::Number(
654                serde_json::Number::from_f64(std::f64::consts::PI).unwrap()
655            )),
656            "number"
657        );
658        assert_eq!(converter.json_type_name(&JsonValue::String("test".to_string())), "string");
659        assert_eq!(converter.json_type_name(&JsonValue::Array(vec![])), "array");
660        assert_eq!(converter.json_type_name(&JsonValue::Array(vec![JsonValue::Null])), "array");
661        assert_eq!(converter.json_type_name(&JsonValue::Object(serde_json::Map::new())), "object");
662
663        let mut obj = serde_json::Map::new();
664        obj.insert("key".to_string(), JsonValue::String("value".to_string()));
665        assert_eq!(converter.json_type_name(&JsonValue::Object(obj)), "object");
666    }
667
668    #[test]
669    fn test_boolean_conversion_variations() {
670        let pool = DescriptorPool::new();
671        let converter = ProtobufJsonConverter::new(pool);
672
673        // Test different boolean representations
674        let test_cases = vec![
675            (JsonValue::Bool(true), "true"),
676            (JsonValue::Bool(false), "false"),
677            (JsonValue::String("true".to_string()), "string true"),
678            (JsonValue::String("false".to_string()), "string false"),
679            (JsonValue::String("1".to_string()), "string 1"),
680            (JsonValue::String("0".to_string()), "string 0"),
681            (JsonValue::String("yes".to_string()), "string yes"),
682            (JsonValue::String("no".to_string()), "string no"),
683            (JsonValue::String("on".to_string()), "string on"),
684            (JsonValue::String("off".to_string()), "string off"),
685            (JsonValue::String("".to_string()), "empty string"),
686            (JsonValue::Number(serde_json::Number::from(1)), "number 1"),
687            (JsonValue::Number(serde_json::Number::from(0)), "number 0"),
688            (JsonValue::Number(serde_json::Number::from(42)), "number 42"),
689        ];
690
691        for (json_value, description) in test_cases {
692            // This would normally test conversion, but since we don't have actual descriptors,
693            // we just test that the converter can handle the type detection
694            let type_name = converter.json_type_name(&json_value);
695            assert!(!type_name.is_empty(), "Type name should not be empty for {}", description);
696        }
697    }
698
699    #[test]
700    fn test_string_conversion_edge_cases() {
701        let pool = DescriptorPool::new();
702        let converter = ProtobufJsonConverter::new(pool);
703
704        let test_strings = vec![
705            "",
706            " ",
707            "simple string",
708            "string with spaces",
709            "string-with-dashes",
710            "string_with_underscores",
711            "string123",
712            "123string",
713            "string with \"quotes\"",
714            "string with 'apostrophes'",
715            "string with /slashes/",
716            "string with \\backslashes\\",
717            "string with \ttabs\tand\nnewlines",
718            "string with unicode: 你好世界 🌍",
719            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // Large string
720        ];
721
722        for test_string in test_strings {
723            let json_value = JsonValue::String(test_string.to_string());
724            let type_name = converter.json_type_name(&json_value);
725            assert_eq!(type_name, "string", "Failed for string: '{}'", test_string);
726        }
727    }
728
729    #[test]
730    fn test_number_conversion_variations() {
731        let pool = DescriptorPool::new();
732        let converter = ProtobufJsonConverter::new(pool);
733
734        let test_numbers = vec![
735            serde_json::Number::from(0),
736            serde_json::Number::from(1),
737            serde_json::Number::from(-1),
738            serde_json::Number::from(42),
739            serde_json::Number::from(-42),
740            serde_json::Number::from(i32::MAX),
741            serde_json::Number::from(i32::MIN),
742            serde_json::Number::from(i64::MAX),
743            serde_json::Number::from(i64::MIN),
744            serde_json::Number::from_f64(0.0).unwrap(),
745            serde_json::Number::from_f64(1.5).unwrap(),
746            serde_json::Number::from_f64(-1.5).unwrap(),
747            serde_json::Number::from_f64(std::f64::consts::PI).unwrap(),
748            serde_json::Number::from_f64(1e10).unwrap(),
749            serde_json::Number::from_f64(-1e10).unwrap(),
750            serde_json::Number::from_f64(1.23456789e-10).unwrap(),
751        ];
752
753        for number in test_numbers {
754            let json_value = JsonValue::Number(number);
755            let type_name = converter.json_type_name(&json_value);
756            assert_eq!(type_name, "number", "Failed for number: {}", json_value);
757        }
758    }
759
760    #[test]
761    fn test_array_conversion_variations() {
762        let pool = DescriptorPool::new();
763        let converter = ProtobufJsonConverter::new(pool);
764
765        // Test empty array
766        let empty_array = JsonValue::Array(vec![]);
767        assert_eq!(converter.json_type_name(&empty_array), "array");
768
769        // Test array with mixed types
770        let mixed_array = JsonValue::Array(vec![
771            JsonValue::Null,
772            JsonValue::Bool(true),
773            JsonValue::Number(42.into()),
774            JsonValue::String("test".to_string()),
775            JsonValue::Object(serde_json::Map::new()),
776        ]);
777        assert_eq!(converter.json_type_name(&mixed_array), "array");
778
779        // Test nested arrays
780        let nested_array = JsonValue::Array(vec![
781            JsonValue::Array(vec![JsonValue::Number(1.into())]),
782            JsonValue::Array(vec![JsonValue::String("nested".to_string())]),
783        ]);
784        assert_eq!(converter.json_type_name(&nested_array), "array");
785
786        // Test array with 1000 elements
787        let large_array = JsonValue::Array(vec![JsonValue::Number(1.into()); 1000]);
788        assert_eq!(converter.json_type_name(&large_array), "array");
789    }
790
791    #[test]
792    fn test_object_conversion_variations() {
793        let pool = DescriptorPool::new();
794        let converter = ProtobufJsonConverter::new(pool);
795
796        // Test empty object
797        let empty_object = JsonValue::Object(serde_json::Map::new());
798        assert_eq!(converter.json_type_name(&empty_object), "object");
799
800        // Test object with various key types
801        let mut obj = serde_json::Map::new();
802        obj.insert("string_key".to_string(), JsonValue::String("value".to_string()));
803        obj.insert("number_key".to_string(), JsonValue::Number(42.into()));
804        obj.insert("boolean_key".to_string(), JsonValue::Bool(true));
805        obj.insert("null_key".to_string(), JsonValue::Null);
806        obj.insert("array_key".to_string(), JsonValue::Array(vec![]));
807        obj.insert("object_key".to_string(), JsonValue::Object(serde_json::Map::new()));
808
809        let complex_object = JsonValue::Object(obj);
810        assert_eq!(converter.json_type_name(&complex_object), "object");
811
812        // Test deeply nested object
813        let mut nested_obj = serde_json::Map::new();
814        nested_obj.insert(
815            "level1".to_string(),
816            JsonValue::Object({
817                let mut level2 = serde_json::Map::new();
818                level2.insert(
819                    "level2".to_string(),
820                    JsonValue::Object({
821                        let mut level3 = serde_json::Map::new();
822                        level3.insert("level3".to_string(), JsonValue::String("deep".to_string()));
823                        level3
824                    }),
825                );
826                level2
827            }),
828        );
829        let nested_object = JsonValue::Object(nested_obj);
830        assert_eq!(converter.json_type_name(&nested_object), "object");
831    }
832
833    #[test]
834    fn test_base64_encoding_detection() {
835        let pool = DescriptorPool::new();
836        let converter = ProtobufJsonConverter::new(pool);
837
838        // Test various base64 strings
839        let base64_cases = vec![
840            "",                                                     // empty
841            "dGVzdA==",                                             // "test"
842            "SGVsbG8gV29ybGQ=",                                     // "Hello World"
843            "YWJjMTIzIT8kKiYoKSctPUB+",                             // "abc123!?$*&()'-=@~"
844            "dGVzdGluZyB3aXRoIHNwYWNlcyBhbmQgc3BlY2lhbCBjaGFycw==", // "testing with spaces and special chars"
845            "aHR0cHM6Ly9leGFtcGxlLmNvbS9wYXRoP3F1ZXJ5PXZhbHVl",     // URL-like base64
846        ];
847
848        for base64_str in base64_cases {
849            let json_value = JsonValue::String(base64_str.to_string());
850            let type_name = converter.json_type_name(&json_value);
851            assert_eq!(type_name, "string", "Failed for base64 string: '{}'", base64_str);
852        }
853
854        // Test invalid base64 strings
855        let invalid_base64 = vec![
856            "invalid!@#$%",
857            "not-base64",
858            "abc123!@#$%",
859            "This is not base64 encoded",
860        ];
861
862        for invalid_str in invalid_base64 {
863            let json_value = JsonValue::String(invalid_str.to_string());
864            let type_name = converter.json_type_name(&json_value);
865            assert_eq!(type_name, "string", "Failed for invalid base64: '{}'", invalid_str);
866        }
867    }
868}