jgd_rs/type_spec/field.rs
1//! # Field Module
2//!
3//! This module defines the core `Field` enum which represents any value that can be generated
4//! in a JGD (JSON Generator Definition) schema. Fields are the building blocks of JSON data
5//! generation, supporting primitive types, complex structures, and dynamic content generation.
6//!
7//! ## Overview
8//!
9//! The `Field` enum supports all JSON value types plus specialized generator types:
10//! - **Primitive types**: strings, numbers, booleans, null
11//! - **Complex types**: arrays, entities (objects), optional values
12//! - **Dynamic types**: references to other generated values, template strings with placeholders
13//!
14//! ## Template String Processing
15//!
16//! String fields support template syntax with `${...}` placeholders for:
17//! - Fake data generation: `"${name.firstName}"`, `"${address.city}"`
18//! - Cross-references: `"${users.id}"`, `"${posts.title}"`
19//! - Function calls with arguments: `"${lorem.sentence(5)}"`
20
21use indexmap::IndexMap;
22use serde::Deserialize;
23use serde_json::Value;
24use crate::{type_spec::{ArraySpec, Entity, GeneratorConfig, JsonGenerator, NumberSpec, OptionalSpec, ReplacerCollection}, JgdGeneratorError, LocalConfig};
25
26/// A field specification that can generate any JSON value type.
27///
28/// Fields are the fundamental building blocks in JGD schemas. Each field variant
29/// corresponds to a different type of value generation strategy. The enum uses
30/// `#[serde(untagged)]` to support flexible JSON deserialization where the structure
31/// determines the variant.
32///
33/// # Variants
34///
35/// ## Complex Types
36/// - **`Array`**: Generates arrays with configurable element types and counts
37/// - **`Entity`**: Generates nested objects with multiple fields
38/// - **`Optional`**: Conditionally generates values based on probability
39///
40/// ## Dynamic Types
41/// - **`Ref`**: References values from other generated entities
42/// - **`Str`**: Template strings with placeholder substitution support
43///
44/// ## Primitive Types
45/// - **`Number`**: Generates numbers within specified ranges
46/// - **`Bool`**: Static boolean values
47/// - **`I64`**: Static 64-bit integer values
48/// - **`F64`**: Static 64-bit floating-point values
49/// - **`Null`**: JSON null values
50///
51/// # JGD Schema Examples
52///
53/// ```json
54/// {
55/// "name": "John Doe", // Field::Str
56/// "age": { "number": { "min": 18, "max": 65 } }, // Field::Number
57/// "active": true, // Field::Bool
58/// "score": 95.5, // Field::F64
59/// "id": 12345, // Field::I64
60/// "metadata": null, // Field::Null
61/// "email": "${internet.email}", // Field::Str with template
62/// "user_id": { "ref": "users.id" }, // Field::Ref
63/// "tags": { // Field::Array
64/// "array": {
65/// "count": 3,
66/// "of": "${lorem.word}"
67/// }
68/// },
69/// "profile": { // Field::Optional
70/// "optional": {
71/// "prob": 0.8,
72/// "of": { "bio": "${lorem.sentence}" }
73/// }
74/// }
75/// }
76/// ```
77///
78/// # Deserialization
79///
80/// The `#[serde(untagged)]` attribute allows automatic variant detection:
81/// - Objects with `"array"` key → `Field::Array`
82/// - Objects with `"number"` key → `Field::Number`
83/// - Objects with `"optional"` key → `Field::Optional`
84/// - Objects with `"ref"` key → `Field::Ref`
85/// - Plain strings → `Field::Str`
86/// - Plain numbers → `Field::I64` or `Field::F64`
87/// - Plain booleans → `Field::Bool`
88/// - `null` → `Field::Null`
89#[derive(Debug, Deserialize, Clone)]
90#[serde(untagged)]
91pub enum Field {
92 /// Array field that generates JSON arrays.
93 ///
94 /// Wraps an `ArraySpec` that defines the element type and count for array generation.
95 /// Arrays can contain any field type as elements and support dynamic sizing.
96 Array {
97 array: ArraySpec
98 },
99
100 /// Entity field that generates nested JSON objects.
101 ///
102 /// Embeds a complete `Entity` specification for generating complex nested structures.
103 /// Entities can contain multiple fields and support uniqueness constraints.
104 Entity(Entity),
105
106 /// Number field that generates numeric values within ranges.
107 ///
108 /// Wraps a `NumberSpec` that defines the range and type (integer/float) for number generation.
109 /// Supports both discrete integer ranges and continuous floating-point ranges.
110 Number {
111 number: NumberSpec
112 },
113
114 /// Optional field that conditionally generates values.
115 ///
116 /// Wraps an `OptionalSpec` that defines probability-based value generation.
117 /// Can generate the specified field or null based on the configured probability.
118 Optional {
119 optional: OptionalSpec
120 },
121
122 /// Reference field that links to other generated entities.
123 ///
124 /// Contains a dot-notation path string for accessing values from previously generated
125 /// entities. Enables cross-referencing and relational data generation.
126 Ref {
127 r#ref: String
128 },
129
130 /// String field with template support.
131 ///
132 /// Can be a literal string or contain `${...}` placeholders for dynamic content generation.
133 /// Supports faker function calls and cross-references to other entities.
134 Str(String),
135
136 /// Static boolean field.
137 ///
138 /// Generates a fixed boolean value without any dynamic behavior.
139 Bool(bool),
140
141 /// Static 64-bit integer field.
142 ///
143 /// Generates a fixed integer value without any dynamic behavior.
144 I64(i64),
145
146 /// Static 64-bit floating-point field.
147 ///
148 /// Generates a fixed floating-point value without any dynamic behavior.
149 F64(f64),
150
151 /// Null field.
152 ///
153 /// Always generates a JSON null value.
154 Null,
155}
156
157impl Field {
158 /// Resolves a reference path to retrieve a value from generated entities.
159 ///
160 /// This method handles cross-reference resolution by looking up values in the
161 /// generator configuration's `gen_value` map using dot-notation paths.
162 ///
163 /// # Parameters
164 /// - `r#ref`: The dot-notation path to resolve (e.g., "users.name", "posts.0.title")
165 /// - `config`: Mutable reference to the generator configuration containing generated values
166 ///
167 /// # Returns
168 /// - `Value`: The resolved value if found, or an error message string if the path doesn't exist
169 ///
170 /// # Path Resolution
171 ///
172 /// Paths follow dot-notation syntax:
173 /// - `"entity.field"` - Access a field from a single entity
174 /// - `"entity.0.field"` - Access a field from the first item in an entity array
175 /// - `"entity.id"` - Common pattern for referencing entity IDs
176 ///
177 /// # Examples
178 ///
179 /// ```rust,ignore
180 /// // Reference a user's name from a posts entity
181 /// let user_ref = Field::Ref { r#ref: "users.name".to_string() };
182 /// let resolved_value = user_ref.generate_for_ref("users.name", &mut config);
183 ///
184 /// // Reference an ID from an entity array
185 /// let id_ref = Field::Ref { r#ref: "users.0.id".to_string() };
186 /// let user_id = id_ref.generate_for_ref("users.0.id", &mut config);
187 /// ```
188 fn generate_for_ref(&self, r#ref: &str, config: &mut GeneratorConfig, local_config: Option<&mut LocalConfig>
189 ) -> Result<Value, JgdGeneratorError> {
190 let value = config.get_value_from_path(r#ref.to_string());
191
192 if let Some(value) = value {
193 return Ok(value.clone());
194 }
195
196 let (entity_name, field_name) = if let Some(local_config) = local_config {
197 let entity_name = local_config.entity_name.clone();
198 let field_name = local_config.field_name.clone();
199 (entity_name, field_name)
200 } else {
201 (None, None)
202 };
203
204 Err(JgdGeneratorError {
205 message: format!("The path {} is not found", r#ref),
206 entity: entity_name,
207 field: field_name,
208 })
209 }
210}
211
212impl JsonGenerator for Field {
213 /// Generates a JSON value based on the field type.
214 ///
215 /// This method dispatches to the appropriate generation logic for each field variant.
216 /// It handles all supported field types and ensures proper JSON value generation
217 /// according to the JGD specification.
218 ///
219 /// # Parameters
220 /// - `config`: Mutable reference to generator configuration for accessing state and utilities
221 ///
222 /// # Returns
223 /// - `serde_json::Value`: The generated JSON value appropriate for the field type
224 ///
225 /// # Generation Behavior
226 ///
227 /// - **Array**: Delegates to `ArraySpec::generate()` for array creation
228 /// - **Entity**: Delegates to `Entity::generate()` for object creation
229 /// - **Number**: Delegates to `NumberSpec::generate()` for numeric value generation
230 /// - **Optional**: Delegates to `OptionalSpec::generate()` for probability-based generation
231 /// - **Ref**: Resolves cross-references using `generate_for_ref()`
232 /// - **Str**: Processes template strings with placeholder replacement
233 /// - **Bool/I64/F64/Null**: Direct conversion to corresponding JSON values
234 ///
235 /// # Template Processing
236 ///
237 /// String fields undergo template processing to replace `${...}` placeholders:
238 /// - Faker calls: `"${name.firstName}"` → `"John"`
239 /// - Cross-references: `"${users.id}"` → `"12345"`
240 /// - Function calls: `"${lorem.words(3)}"` → `"lorem ipsum dolor"`
241 ///
242 /// # Examples
243 ///
244 /// ```rust,ignore
245 /// let field = Field::Str("Hello ${name.firstName}!".to_string());
246 /// let result = field.generate(&mut config);
247 /// // Result: Value::String("Hello John!")
248 ///
249 /// let number_field = Field::Number {
250 /// number: NumberSpec::new_integer(1.0, 100.0)
251 /// };
252 /// let result = number_field.generate(&mut config);
253 /// // Result: Value::Number(42)
254 /// ```
255 fn generate(&self, config: &mut super::GeneratorConfig, local_config: Option<&mut LocalConfig>
256 ) -> Result<Value, JgdGeneratorError> {
257 match self {
258 // Field::Object { object } => object.generate(config),
259 Field::Array { array } => array.generate(config, local_config),
260 Field::Entity(entity) => entity.generate(config, local_config),
261 Field::Number { number } => number.generate(config, local_config),
262 Field::Optional { optional } => optional.generate(config, local_config),
263 Field::Ref { r#ref } => self.generate_for_ref(r#ref, config, local_config),
264 Field::Str(value) => value.generate(config, local_config),
265 Field::Bool(value) => Ok(Value::Bool(*value)),
266 Field::I64(value) => Ok(Value::Number(serde_json::Number::from(*value))),
267 Field::F64(value) => Ok(Value::Number(serde_json::Number::from_f64(*value).unwrap())),
268 Field::Null => Ok(Value::Null),
269 }
270 }
271}
272
273impl JsonGenerator for IndexMap<String, Field> {
274 /// Generates a JSON object from a map of field specifications.
275 ///
276 /// This implementation allows `IndexMap<String, Field>` to be used directly as a
277 /// JSON generator, converting each field to its corresponding JSON value while
278 /// preserving key ordering.
279 ///
280 /// # Parameters
281 /// - `config`: Mutable reference to generator configuration for field generation
282 ///
283 /// # Returns
284 /// - `Value`: A JSON object containing all generated field values
285 ///
286 /// # Behavior
287 ///
288 /// - Iterates through the map in insertion order (preserved by `IndexMap`)
289 /// - Generates each field value using the field's `generate()` method
290 /// - Collects all key-value pairs into a JSON object
291 /// - Maintains field ordering as defined in the original specification
292 ///
293 /// # Examples
294 ///
295 /// ```rust,ignore
296 /// let mut fields = IndexMap::new();
297 /// fields.insert("name".to_string(), Field::Str("John".to_string()));
298 /// fields.insert("age".to_string(), Field::I64(30));
299 /// fields.insert("active".to_string(), Field::Bool(true));
300 ///
301 /// let result = fields.generate(&mut config);
302 /// // Result: {"name": "John", "age": 30, "active": true}
303 /// ```
304 ///
305 /// # Use Cases
306 ///
307 /// This implementation is primarily used by:
308 /// - Entity field generation for creating object structures
309 /// - Root-level object generation in JGD schemas
310 /// - Nested object creation within complex field hierarchies
311 fn generate(&self, config: &mut super::GeneratorConfig, local_config: Option<&mut LocalConfig>
312 ) -> Result<Value, JgdGeneratorError> {
313
314 let mut local_config = LocalConfig::from_current_with_config(None, None, local_config);
315
316 let mut map = serde_json::Map::new();
317 for (key, field) in self {
318 local_config.field_name = Some(key.clone());
319 let generated = field.generate(config, Some(&mut local_config))?;
320 map.insert(key.clone(), generated);
321 }
322
323 Ok(Value::Object(map))
324 }
325}
326
327impl JsonGenerator for String {
328 /// Generates a JSON value from a string with template processing.
329 ///
330 /// This implementation enables `String` values to be used directly in the JSON generation
331 /// pipeline with automatic template processing for placeholder substitution.
332 ///
333 /// # Parameters
334 /// - `config`: Mutable reference to generator configuration for replacement processing
335 ///
336 /// # Returns
337 /// - `Value`: A JSON string value, either the original string or with placeholders replaced
338 ///
339 /// # Template Processing
340 ///
341 /// The method uses `ReplacerCollection` to detect and process `${...}` placeholders:
342 ///
343 /// 1. **No placeholders**: Returns the string as-is
344 /// 2. **With placeholders**: Attempts replacement using available replacers
345 /// 3. **Replacement failure**: Falls back to the original string
346 ///
347 /// # Placeholder Types
348 ///
349 /// - **Faker calls**: `"${name.firstName}"` → generates fake names
350 /// - **Cross-references**: `"${users.id}"` → references other entity values
351 /// - **Function calls**: `"${lorem.sentence(5)}"` → calls faker functions with arguments
352 /// - **Mixed content**: `"User: ${name.firstName} (${internet.email})"` → multiple replacements
353 ///
354 /// # Examples
355 ///
356 /// ```rust,ignore
357 /// // Simple string without placeholders
358 /// let simple = "Hello World".to_string();
359 /// let result = simple.generate(&mut config);
360 /// // Result: Value::String("Hello World")
361 ///
362 /// // Template string with faker call
363 /// let template = "Welcome ${name.firstName}!".to_string();
364 /// let result = template.generate(&mut config);
365 /// // Result: Value::String("Welcome John!")
366 ///
367 /// // Complex template with multiple placeholders
368 /// let complex = "User ${name.firstName} lives in ${address.city}".to_string();
369 /// let result = complex.generate(&mut config);
370 /// // Result: Value::String("User John lives in New York")
371 /// ```
372 ///
373 /// # Error Handling
374 ///
375 /// If replacement fails for any reason, the method gracefully falls back to
376 /// returning the original string value, ensuring generation never fails due
377 /// to template processing errors.
378 fn generate(&self, config: &mut super::GeneratorConfig, local_config: Option<&mut LocalConfig>
379 ) -> Result<Value, JgdGeneratorError> {
380
381 let value = self.to_string();
382 let replacers = ReplacerCollection::new(value.clone());
383 if replacers.is_empty() {
384 return Ok(Value::String(value));
385 }
386
387 replacers.replace(config, local_config)
388 }
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394 use crate::type_spec::{Count, NumberSpec};
395 use serde_json::json;
396
397 fn create_test_config(seed: Option<u64>) -> GeneratorConfig {
398 GeneratorConfig::new("EN", seed)
399 }
400
401 #[test]
402 fn test_field_str_without_placeholders() {
403 let mut config = create_test_config(Some(42));
404 let field = Field::Str("Hello World".to_string());
405
406 let result = field.generate(&mut config, None);
407 assert!(result.is_ok());
408
409 if let Ok(result) = result {
410 assert_eq!(result, Value::String("Hello World".to_string()));
411 }
412 }
413
414 #[test]
415 fn test_field_bool_true() {
416 let mut config = create_test_config(Some(42));
417 let field = Field::Bool(true);
418
419 let result = field.generate(&mut config, None);
420 assert!(result.is_ok());
421
422 if let Ok(result) = result {
423 assert_eq!(result, Value::Bool(true));
424 }
425 }
426
427 #[test]
428 fn test_field_bool_false() {
429 let mut config = create_test_config(Some(42));
430 let field = Field::Bool(false);
431
432 let result = field.generate(&mut config, None);
433 assert!(result.is_ok());
434
435 if let Ok(result) = result {
436 assert_eq!(result, Value::Bool(false));
437 }
438 }
439
440 #[test]
441 fn test_field_i64() {
442 let mut config = create_test_config(Some(42));
443 let field = Field::I64(12345);
444
445 let result = field.generate(&mut config, None);
446 assert!(result.is_ok());
447
448 if let Ok(result) = result {
449 assert_eq!(result, Value::Number(serde_json::Number::from(12345)));
450 }
451 }
452
453 #[test]
454 fn test_field_f64() {
455 let mut config = create_test_config(Some(42));
456 let field = Field::F64(123.45);
457
458 let result = field.generate(&mut config, None);
459 assert!(result.is_ok());
460
461 if let Ok(result) = result {
462 if let Value::Number(num) = result {
463 assert_eq!(num.as_f64(), Some(123.45));
464 } else {
465 panic!("Expected number value");
466 }
467 }
468 }
469
470 #[test]
471 fn test_field_null() {
472 let mut config = create_test_config(Some(42));
473 let field = Field::Null;
474
475 let result = field.generate(&mut config, None);
476 assert!(result.is_ok());
477
478 if let Ok(result) = result {
479 assert_eq!(result, Value::Null);
480 }
481 }
482
483 #[test]
484 fn test_field_number() {
485 let mut config = create_test_config(Some(42));
486 let number_spec = NumberSpec::new_integer(1.0, 10.0);
487 let field = Field::Number { number: number_spec };
488
489 let result = field.generate(&mut config, None);
490 assert!(result.is_ok());
491
492 if let Ok(result) = result {
493 assert!(result.is_number());
494
495 if let Value::Number(num) = result {
496 let value = num.as_i64().unwrap();
497 assert!((1..=10).contains(&value));
498 }
499 }
500 }
501
502 #[test]
503 fn test_field_array() {
504 let mut config = create_test_config(Some(42));
505 let array_spec = ArraySpec {
506 count: Some(Count::Fixed(3)),
507 of: Box::new(Field::Str("test".to_string())),
508 };
509 let field = Field::Array { array: array_spec };
510
511 let result = field.generate(&mut config, None);
512 assert!(result.is_ok());
513
514 if let Ok(result) = result {
515 assert!(result.is_array());
516
517 if let Value::Array(arr) = result {
518 assert_eq!(arr.len(), 3);
519 for item in arr {
520 assert_eq!(item, Value::String("test".to_string()));
521 }
522 }
523 }
524 }
525
526 #[test]
527 fn test_field_ref_existing_path() {
528 let mut config = create_test_config(Some(42));
529
530 // Set up a reference value in the config
531 config.gen_value.insert("users".to_string(), json!({
532 "id": 12345,
533 "name": "John Doe"
534 }));
535
536 let field = Field::Ref { r#ref: "users.name".to_string() };
537 let result = field.generate(&mut config, None);
538 assert!(result.is_ok());
539
540 if let Ok(result) = result {
541 assert_eq!(result, Value::String("John Doe".to_string()));
542 }
543 }
544
545 #[test]
546 fn test_field_ref_missing_path() {
547 let mut config = create_test_config(Some(42));
548 let field = Field::Ref { r#ref: "nonexistent.path".to_string() };
549
550 let result = field.generate(&mut config, None);
551 assert!(result.is_err());
552
553 if let Err(error) = result {
554 assert_eq!(error.message, "The path nonexistent.path is not found".to_string());
555 }
556 }
557
558 #[test]
559 fn test_field_entity() {
560 let mut config = create_test_config(Some(42));
561
562 let mut fields = IndexMap::new();
563 fields.insert("name".to_string(), Field::Str("Test User".to_string()));
564 fields.insert("age".to_string(), Field::I64(25));
565
566 let entity = Entity {
567 count: None,
568 seed: None,
569 unique_by: vec![],
570 fields,
571 };
572
573 let field = Field::Entity(entity);
574 let result = field.generate(&mut config, None);
575 assert!(result.is_ok());
576
577 if let Ok(result) = result {
578 assert!(result.is_object());
579 if let Value::Object(obj) = result {
580 assert_eq!(obj.get("name"), Some(&Value::String("Test User".to_string())));
581 assert_eq!(obj.get("age"), Some(&Value::Number(serde_json::Number::from(25))));
582 }
583 }
584 }
585
586 #[test]
587 fn test_indexmap_field_generation() {
588 let mut config = create_test_config(Some(42));
589 let mut fields = IndexMap::new();
590
591 fields.insert("string_field".to_string(), Field::Str("Hello".to_string()));
592 fields.insert("number_field".to_string(), Field::I64(42));
593 fields.insert("bool_field".to_string(), Field::Bool(true));
594 fields.insert("null_field".to_string(), Field::Null);
595
596 let result = fields.generate(&mut config, None);
597 assert!(result.is_ok());
598
599 if let Ok(result) = result {
600 assert!(result.is_object());
601 if let Value::Object(obj) = result {
602 assert_eq!(obj.get("string_field"), Some(&Value::String("Hello".to_string())));
603 assert_eq!(obj.get("number_field"), Some(&Value::Number(serde_json::Number::from(42))));
604 assert_eq!(obj.get("bool_field"), Some(&Value::Bool(true)));
605 assert_eq!(obj.get("null_field"), Some(&Value::Null));
606
607 // Verify ordering is preserved (IndexMap maintains insertion order)
608 let keys: Vec<&String> = obj.keys().collect();
609 assert_eq!(keys, vec!["string_field", "number_field", "bool_field", "null_field"]);
610 }
611 }
612 }
613
614 #[test]
615 fn test_indexmap_empty_generation() {
616 let mut config = create_test_config(Some(42));
617 let fields: IndexMap<String, Field> = IndexMap::new();
618
619 let result = fields.generate(&mut config, None);
620 assert!(result.is_ok());
621
622 if let Ok(result) = result {
623 assert!(result.is_object());
624 if let Value::Object(obj) = result {
625 assert!(obj.is_empty());
626 }
627 }
628 }
629
630 #[test]
631 fn test_string_template_processing() {
632 let mut config = create_test_config(Some(42));
633
634 let template = "Hello ${name.name}!".to_string();
635 let result = template.generate(&mut config, None);
636 assert!(result.is_ok());
637
638 if let Ok(result) = result {
639 // The result should either be the template with replacement or the original string
640 // Since we can't control the exact replacement logic in this test,
641 // we verify it's still a string
642 assert!(result.is_string());
643 }
644 }
645
646 #[test]
647 fn test_string_invalid_template_error() {
648 let mut config = create_test_config(Some(42));
649
650 let template = "Hello ${invalid.key}!".to_string();
651 let result = template.generate(&mut config, None);
652 assert!(result.is_err());
653
654 if let Err(error) = result {
655 // The result should either be the template with replacement or the original string
656 // Since we can't control the exact replacement logic in this test,
657 // we verify it's still a string
658 assert_eq!(error.message, "Error to process the pattern ${invalid.key}".to_string());
659 }
660 }
661
662 #[test]
663 fn test_string_no_placeholders() {
664 let mut config = create_test_config(Some(42));
665 let simple_string = "No placeholders here".to_string();
666
667 let result = simple_string.generate(&mut config, None);
668 assert!(result.is_ok());
669
670 if let Ok(result) = result {
671 assert_eq!(result, Value::String("No placeholders here".to_string()));
672 }
673 }
674
675 #[test]
676 fn test_field_clone() {
677 let field = Field::Str("test".to_string());
678 let cloned = field.clone();
679
680 match (field, cloned) {
681 (Field::Str(original), Field::Str(cloned_str)) => {
682 assert_eq!(original, cloned_str);
683 }
684 _ => panic!("Clone should preserve field type"),
685 }
686 }
687
688 #[test]
689 fn test_field_debug() {
690 let field = Field::Bool(true);
691 let debug_str = format!("{:?}", field);
692 assert!(debug_str.contains("Bool"));
693 assert!(debug_str.contains("true"));
694 }
695
696 #[test]
697 fn test_complex_nested_fields() {
698 let mut config = create_test_config(Some(42));
699
700 // Create a complex nested structure
701 let mut inner_fields = IndexMap::new();
702 inner_fields.insert("inner_str".to_string(), Field::Str("inner_value".to_string()));
703 inner_fields.insert("inner_num".to_string(), Field::I64(99));
704
705 let inner_entity = Entity {
706 count: None,
707 seed: None,
708 unique_by: vec![],
709 fields: inner_fields,
710 };
711
712 let mut outer_fields = IndexMap::new();
713 outer_fields.insert("nested".to_string(), Field::Entity(inner_entity));
714 outer_fields.insert("simple".to_string(), Field::Str("outer_value".to_string()));
715
716 let result = outer_fields.generate(&mut config, None);
717 assert!(result.is_ok());
718
719 if let Ok(result) = result {
720 assert!(result.is_object());
721 if let Value::Object(outer_obj) = result {
722 assert!(outer_obj.contains_key("nested"));
723 assert!(outer_obj.contains_key("simple"));
724
725 if let Some(Value::Object(inner_obj)) = outer_obj.get("nested") {
726 assert_eq!(inner_obj.get("inner_str"), Some(&Value::String("inner_value".to_string())));
727 assert_eq!(inner_obj.get("inner_num"), Some(&Value::Number(serde_json::Number::from(99))));
728 } else {
729 panic!("Expected nested object");
730 }
731 }
732 }
733 }
734
735 #[test]
736 fn test_field_variants_coverage() {
737 let mut config = create_test_config(Some(42));
738
739 config.gen_value.insert("test".to_string(), json!({
740 "path": "found"
741 }));
742
743 // Test all field variants to ensure they can be created and generate values
744 let variants = vec![
745 Field::Str("test".to_string()),
746 Field::Bool(true),
747 Field::I64(42),
748 Field::F64(123.45), // Using arbitrary float to avoid clippy warnings
749 Field::Null,
750 Field::Number { number: NumberSpec::new_integer(1.0, 10.0) },
751 Field::Ref { r#ref: "test.path".to_string() },
752 ];
753
754 for field in variants {
755 let result = field.generate(&mut config, None);
756
757 assert!(result.is_ok());
758
759 if let Ok(result) = result {
760 // Each field should generate some valid JSON value
761 assert!(result.is_string() || result.is_number() || result.is_boolean() || result.is_null());
762 }
763 }
764 }
765}