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