1use 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#[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 }
38
39#[derive(Debug, Clone)]
41pub struct ProtobufJsonConverter {
42 #[allow(dead_code)] pool: DescriptorPool,
45}
46
47impl ProtobufJsonConverter {
48 pub fn new(pool: DescriptorPool) -> Self {
50 Self { pool }
51 }
52
53 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 for (field_name, json_value) in obj {
70 self.set_field_from_json(&mut message, field_name, json_value)?;
71 }
72
73 self.set_default_values_for_missing_fields(&mut message)?;
75
76 Ok(message)
77 }
78
79 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 }
102 }
103
104 Ok(JsonValue::Object(obj))
105 }
106
107 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 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 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 if let Some(enum_value) = enum_descriptor.get_value_by_name(s) {
158 Ok(Value::EnumNumber(enum_value.number()))
159 } else {
160 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)) }
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 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 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 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(), };
474 json_obj.insert(key_str, json_value);
475 }
476 JsonValue::Object(json_obj)
477 }
478 })
479 }
480
481 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 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 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 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 #[allow(dead_code)] 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 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 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 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 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 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 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", ];
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 let empty_array = JsonValue::Array(vec![]);
767 assert_eq!(converter.json_type_name(&empty_array), "array");
768
769 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 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 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 let empty_object = JsonValue::Object(serde_json::Map::new());
798 assert_eq!(converter.json_type_name(&empty_object), "object");
799
800 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 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 let base64_cases = vec![
840 "", "dGVzdA==", "SGVsbG8gV29ybGQ=", "YWJjMTIzIT8kKiYoKSctPUB+", "dGVzdGluZyB3aXRoIHNwYWNlcyBhbmQgc3BlY2lhbCBjaGFycw==", "aHR0cHM6Ly9leGFtcGxlLmNvbS9wYXRoP3F1ZXJ5PXZhbHVl", ];
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 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}