rust-yaml 0.0.5

A fast, safe YAML 1.2 library for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
//! Comprehensive integration tests for comment preservation

use rust_yaml::{CommentedValue, Comments, LoaderType, Style, Value, Yaml, YamlConfig};

#[test]
fn test_comment_preservation_basic() {
    // Create YAML parser with comment preservation enabled
    let config = YamlConfig {
        preserve_comments: true,
        loader_type: LoaderType::RoundTrip,
        ..Default::default()
    };
    let yaml = Yaml::with_config(config);

    let yaml_with_comments = r#"
# This is a leading comment
key: value  # This is a trailing comment
# This is another leading comment
number: 42
"#;

    // Test that we can parse with comments using the comment-preserving API
    let result = yaml.load_str_with_comments(yaml_with_comments);
    assert!(result.is_ok(), "Should be able to parse YAML with comments");

    let commented_value = result.unwrap();

    // Verify the structure is preserved
    if let Value::Mapping(map) = &commented_value.value {
        assert!(map.contains_key(&Value::String("key".to_string())));
        assert!(map.contains_key(&Value::String("number".to_string())));
    } else {
        panic!("Expected mapping at root level");
    }

    // Test round-trip serialization
    let serialized = yaml.dump_str_with_comments(&commented_value);
    assert!(
        serialized.is_ok(),
        "Should be able to serialize commented value"
    );

    let output = serialized.unwrap();

    // Verify that the output is valid YAML
    let reparsed = yaml.load_str(&output);
    assert!(reparsed.is_ok(), "Round-trip output should be valid YAML");
}

#[test]
fn test_comment_preservation_complex() {
    let config = YamlConfig {
        preserve_comments: true,
        loader_type: LoaderType::RoundTrip,
        ..Default::default()
    };
    let yaml = Yaml::with_config(config);

    let complex_yaml_with_comments = r#"
# Main configuration file
# This controls the entire application

application:
  # Application name and version
  name: "MyApp"  # The official name
  version: "1.0.0"  # Semantic versioning
  
  # Server configuration
  server:
    # Network settings
    host: "localhost"  # Bind address
    port: 8080        # Listen port
    
    # SSL configuration
    ssl:
      enabled: true   # Enable HTTPS
      cert: "/path/to/cert.pem"  # Certificate file
      key: "/path/to/key.pem"   # Private key file

# Database configuration
database:
  # Primary database connection
  host: "db.example.com"  # Database host
  port: 5432             # Database port
  name: "myapp"          # Database name
  
  # Connection pool settings
  pool:
    min: 5   # Minimum connections
    max: 20  # Maximum connections

# Feature flags
features:
  - "authentication"  # User login system
  - "authorization"   # Permission system
  - "logging"        # Application logging
  - "metrics"        # Performance metrics
"#;

    let result = yaml.load_str_with_comments(complex_yaml_with_comments);
    assert!(result.is_ok(), "Should parse complex YAML with comments");

    let commented_value = result.unwrap();

    // Test serialization of complex structure
    let serialized = yaml.dump_str_with_comments(&commented_value);
    assert!(
        serialized.is_ok(),
        "Should serialize complex commented structure"
    );

    let output = serialized.unwrap();

    // Verify round-trip parsing
    let reparsed = yaml.load_str(&output);
    assert!(
        reparsed.is_ok(),
        "Complex round-trip should produce valid YAML"
    );

    // Verify structure is preserved
    let original_parsed = yaml.load_str(complex_yaml_with_comments).unwrap();
    let round_trip_parsed = reparsed.unwrap();
    assert_eq!(
        original_parsed, round_trip_parsed,
        "Round-trip should preserve structure"
    );
}

#[test]
fn test_comment_preservation_sequences() {
    let config = YamlConfig {
        preserve_comments: true,
        loader_type: LoaderType::RoundTrip,
        ..Default::default()
    };
    let yaml = Yaml::with_config(config);

    let sequence_yaml = r#"
# List of users
users:
  # Administrative users
  - name: "admin"     # System administrator
    role: "admin"     # Full access
    active: true      # Currently active
    
  # Regular users  
  - name: "user1"     # First user
    role: "user"      # Limited access
    active: true      # Currently active
    
  - name: "user2"     # Second user
    role: "user"      # Limited access  
    active: false     # Inactive account

# Configuration settings
settings:
  # Notification preferences
  - email: true       # Email notifications
  - sms: false       # SMS notifications
  - push: true       # Push notifications
"#;

    let result = yaml.load_str_with_comments(sequence_yaml);
    assert!(result.is_ok(), "Should parse sequences with comments");

    let commented_value = result.unwrap();

    // Test serialization
    let serialized = yaml.dump_str_with_comments(&commented_value);
    assert!(
        serialized.is_ok(),
        "Should serialize sequences with comments"
    );

    let output = serialized.unwrap();

    // Verify the output is valid and structure is preserved
    let reparsed = yaml.load_str(&output).unwrap();
    let original_parsed = yaml.load_str(sequence_yaml).unwrap();
    assert_eq!(
        original_parsed, reparsed,
        "Sequence structure should be preserved"
    );
}

#[test]
fn test_comment_preservation_multiline_strings() {
    let config = YamlConfig {
        preserve_comments: true,
        loader_type: LoaderType::RoundTrip,
        ..Default::default()
    };
    let yaml = Yaml::with_config(config);

    let multiline_yaml = r#"
# Configuration with multiline strings
config:
  # SQL query for user lookup
  user_query: |  # Literal block scalar
    SELECT id, name, email
    FROM users
    WHERE active = true
    ORDER BY name
  
  # Application description  
  description: >  # Folded block scalar
    This is a long description that will be
    folded into a single line when processed,
    making it easier to read in the YAML file
    while maintaining proper formatting.
    
  # Simple configuration values
  debug: true      # Enable debug mode
  timeout: 30      # Request timeout in seconds
"#;

    let result = yaml.load_str_with_comments(multiline_yaml);
    assert!(
        result.is_ok(),
        "Should parse multiline strings with comments"
    );

    let commented_value = result.unwrap();

    // Test serialization
    let serialized = yaml.dump_str_with_comments(&commented_value);
    assert!(
        serialized.is_ok(),
        "Should serialize multiline strings with comments"
    );

    let output = serialized.unwrap();

    // Verify structure preservation
    let reparsed = yaml.load_str(&output).unwrap();
    let original_parsed = yaml.load_str(multiline_yaml).unwrap();
    assert_eq!(
        original_parsed, reparsed,
        "Multiline string structure should be preserved"
    );
}

#[test]
fn test_comment_preservation_with_anchors_and_aliases() {
    let config = YamlConfig {
        preserve_comments: true,
        loader_type: LoaderType::RoundTrip,
        ..Default::default()
    };
    let yaml = Yaml::with_config(config);

    let anchor_yaml = r#"
# Default configuration template
defaults: &defaults  # Anchor for shared config
  timeout: 30         # Default timeout
  retries: 3         # Default retry count
  debug: false       # Default debug setting

# Development environment
development:
  <<: *defaults       # Merge defaults
  debug: true         # Override debug for dev
  host: "localhost"   # Dev host
  
# Production environment  
production:
  <<: *defaults       # Merge defaults
  host: "prod.example.com"  # Production host
  ssl: true          # Enable SSL in production
  
# Testing environment
testing:
  <<: *defaults       # Merge defaults
  host: "test.example.com"  # Test host
  debug: true         # Enable debug for testing
"#;

    let result = yaml.load_str_with_comments(anchor_yaml);
    assert!(result.is_ok(), "Should parse anchors/aliases with comments");

    let commented_value = result.unwrap();

    // Test serialization
    let serialized = yaml.dump_str_with_comments(&commented_value);
    assert!(
        serialized.is_ok(),
        "Should serialize anchors/aliases with comments"
    );

    let output = serialized.unwrap();

    // Verify structure preservation (anchors/aliases are resolved during parsing)
    let reparsed = yaml.load_str(&output).unwrap();
    let original_parsed = yaml.load_str(anchor_yaml).unwrap();
    assert_eq!(
        original_parsed, reparsed,
        "Anchor/alias structure should be preserved"
    );
}

#[test]
fn test_comment_preservation_edge_cases() {
    let config = YamlConfig {
        preserve_comments: true,
        loader_type: LoaderType::RoundTrip,
        ..Default::default()
    };
    let yaml = Yaml::with_config(config);

    // Test various edge cases
    let edge_case_yaml = r#"
# Comment at start of document
key1: value1  # Trailing comment

# Multiple leading comments
# Another leading comment
# Yet another leading comment
key2: value2

key3: value3  # Comment with special chars: @#$%^&*()

# Comment with quotes and escapes
key4: "quoted value"  # Comment with "quotes" and 'apostrophes'

# Empty lines and spacing


key5: value5  # Comment after empty lines

# Comment before end of document
"#;

    let result = yaml.load_str_with_comments(edge_case_yaml);
    assert!(
        result.is_ok(),
        "Should handle edge cases in comment preservation"
    );

    let commented_value = result.unwrap();

    // Test serialization
    let serialized = yaml.dump_str_with_comments(&commented_value);
    assert!(serialized.is_ok(), "Should serialize edge cases");

    let output = serialized.unwrap();

    // Verify the output is valid YAML
    let reparsed = yaml.load_str(&output);
    assert!(
        reparsed.is_ok(),
        "Edge case round-trip should be valid YAML"
    );
}

#[test]
fn test_comment_preservation_fallback_to_regular_parsing() {
    // Test that when comment preservation is disabled, we fall back to regular parsing
    let regular_yaml = Yaml::new(); // Default config without comment preservation

    let yaml_with_comments = r#"
# This comment will be ignored
key: value  # This comment will also be ignored
number: 42
"#;

    // Should parse successfully but ignore comments
    let result = regular_yaml.load_str(yaml_with_comments);
    assert!(result.is_ok(), "Should parse YAML ignoring comments");

    // Verify structure is still correct
    let value = result.unwrap();
    if let Value::Mapping(map) = value {
        assert_eq!(
            map.get(&Value::String("key".to_string())),
            Some(&Value::String("value".to_string()))
        );
        assert_eq!(
            map.get(&Value::String("number".to_string())),
            Some(&Value::Int(42))
        );
    } else {
        panic!("Expected mapping");
    }
}

#[test]
fn test_comment_preservation_multi_document() {
    let config = YamlConfig {
        preserve_comments: true,
        loader_type: LoaderType::RoundTrip,
        ..Default::default()
    };
    let yaml = Yaml::with_config(config);

    let multi_doc_yaml = r#"
# First document
# Configuration for service A
name: "service-a"  # Service name
port: 8080        # Listen port
---
# Second document  
# Configuration for service B
name: "service-b"  # Service name
port: 8081        # Listen port
---
# Third document
# Configuration for service C
name: "service-c"  # Service name
port: 8082        # Listen port
"#;

    // Test parsing multiple documents with comments
    let docs_result = yaml.load_all_str(multi_doc_yaml);
    assert!(
        docs_result.is_ok(),
        "Should parse multi-document YAML with comments"
    );

    let docs = docs_result.unwrap();
    assert_eq!(docs.len(), 3, "Should have 3 documents");

    // Verify each document has correct structure
    for (i, doc) in docs.iter().enumerate() {
        if let Value::Mapping(map) = doc {
            let expected_name = format!("service-{}", ['a', 'b', 'c'][i]);
            #[allow(clippy::cast_possible_wrap)]
            let expected_port = 8080 + i as i64;

            assert_eq!(
                map.get(&Value::String("name".to_string())),
                Some(&Value::String(expected_name))
            );
            assert_eq!(
                map.get(&Value::String("port".to_string())),
                Some(&Value::Int(expected_port))
            );
        } else {
            panic!("Expected mapping in document {}", i);
        }
    }
}

#[test]
fn test_comment_preservation_performance() {
    let config = YamlConfig {
        preserve_comments: true,
        loader_type: LoaderType::RoundTrip,
        ..Default::default()
    };
    let yaml = Yaml::with_config(config);

    // Create a moderately complex YAML with many comments
    let complex_yaml = (0..50)
        .map(|i| {
            format!(
                r#"
# Configuration item {}
item_{}: 
  # Sub-item properties
  id: {}          # Unique identifier
  name: "Item {}"  # Display name
  active: true    # Status flag
  priority: {}    # Priority level
"#,
                i,
                i,
                i,
                i,
                i % 5
            )
        })
        .collect::<Vec<_>>()
        .join("\n");

    // Test that parsing doesn't take unreasonably long
    let start = std::time::Instant::now();
    let result = yaml.load_str_with_comments(&complex_yaml);
    let parse_duration = start.elapsed();

    assert!(
        result.is_ok(),
        "Should parse complex YAML with many comments"
    );
    assert!(
        parse_duration.as_secs() < 5,
        "Parsing should complete within 5 seconds"
    );

    // Test serialization performance
    let commented_value = result.unwrap();
    let start = std::time::Instant::now();
    let serialized = yaml.dump_str_with_comments(&commented_value);
    let serialize_duration = start.elapsed();

    assert!(
        serialized.is_ok(),
        "Should serialize complex commented YAML"
    );
    assert!(
        serialize_duration.as_secs() < 5,
        "Serialization should complete within 5 seconds"
    );
}

#[test]
fn test_commented_value_construction() {
    // Test manual construction of CommentedValue
    let value = Value::String("test".to_string());
    let mut comments = Comments::new();
    comments.add_leading("This is a leading comment".to_string());
    comments.set_trailing("This is a trailing comment".to_string());

    let commented_value = CommentedValue {
        value,
        comments,
        style: Style::default(),
    };

    assert!(
        commented_value.has_comments(),
        "Should detect presence of comments"
    );

    let config = YamlConfig {
        preserve_comments: true,
        loader_type: LoaderType::RoundTrip,
        ..Default::default()
    };
    let yaml = Yaml::with_config(config);

    // Test serialization of manually constructed CommentedValue
    let result = yaml.dump_str_with_comments(&commented_value);
    assert!(
        result.is_ok(),
        "Should serialize manually constructed CommentedValue"
    );
}

#[test]
fn test_comment_preservation_with_different_quote_styles() {
    let config = YamlConfig {
        preserve_comments: true,
        preserve_quotes: true,
        loader_type: LoaderType::RoundTrip,
        ..Default::default()
    };
    let yaml = Yaml::with_config(config);

    let quoted_yaml = r#"
# Different quote styles
plain_string: hello world      # No quotes
single_quoted: 'single quotes' # Single quotes
double_quoted: "double quotes" # Double quotes

# Special cases
quoted_number: "123"          # Quoted to prevent parsing as number
quoted_boolean: 'true'        # Quoted to prevent parsing as boolean
special_chars: "line1\nline2" # Escaped characters
"#;

    let result = yaml.load_str_with_comments(quoted_yaml);
    assert!(result.is_ok(), "Should parse quoted strings with comments");

    let commented_value = result.unwrap();

    // Test serialization preserves quote styles and comments
    let serialized = yaml.dump_str_with_comments(&commented_value);
    assert!(
        serialized.is_ok(),
        "Should serialize quoted strings with comments"
    );

    let output = serialized.unwrap();

    // Verify structure is preserved
    let reparsed = yaml.load_str(&output);
    assert!(
        reparsed.is_ok(),
        "Round-trip with quotes and comments should be valid"
    );
}