parquet 0.5.12 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +295 -98
  3. data/Cargo.toml +1 -1
  4. data/Gemfile +1 -0
  5. data/README.md +94 -3
  6. data/ext/parquet/Cargo.toml +8 -5
  7. data/ext/parquet/src/adapter_ffi.rs +156 -0
  8. data/ext/parquet/src/lib.rs +13 -21
  9. data/ext/parquet-core/Cargo.toml +23 -0
  10. data/ext/parquet-core/src/arrow_conversion.rs +1133 -0
  11. data/ext/parquet-core/src/error.rs +163 -0
  12. data/ext/parquet-core/src/lib.rs +60 -0
  13. data/ext/parquet-core/src/reader.rs +263 -0
  14. data/ext/parquet-core/src/schema.rs +283 -0
  15. data/ext/parquet-core/src/test_utils.rs +308 -0
  16. data/ext/parquet-core/src/traits/mod.rs +5 -0
  17. data/ext/parquet-core/src/traits/schema.rs +151 -0
  18. data/ext/parquet-core/src/value.rs +209 -0
  19. data/ext/parquet-core/src/writer.rs +839 -0
  20. data/ext/parquet-core/tests/arrow_conversion_tests.rs +423 -0
  21. data/ext/parquet-core/tests/binary_data.rs +437 -0
  22. data/ext/parquet-core/tests/column_projection.rs +557 -0
  23. data/ext/parquet-core/tests/complex_types.rs +821 -0
  24. data/ext/parquet-core/tests/compression_tests.rs +434 -0
  25. data/ext/parquet-core/tests/concurrent_access.rs +430 -0
  26. data/ext/parquet-core/tests/decimal_tests.rs +488 -0
  27. data/ext/parquet-core/tests/edge_cases_corner_cases.rs +322 -0
  28. data/ext/parquet-core/tests/error_handling_comprehensive_tests.rs +547 -0
  29. data/ext/parquet-core/tests/null_handling_tests.rs +430 -0
  30. data/ext/parquet-core/tests/performance_memory.rs +181 -0
  31. data/ext/parquet-core/tests/primitive_types.rs +547 -0
  32. data/ext/parquet-core/tests/real_world_patterns.rs +777 -0
  33. data/ext/parquet-core/tests/roundtrip_correctness.rs +279 -0
  34. data/ext/parquet-core/tests/schema_comprehensive_tests.rs +534 -0
  35. data/ext/parquet-core/tests/temporal_tests.rs +518 -0
  36. data/ext/parquet-core/tests/test_helpers.rs +132 -0
  37. data/ext/parquet-core/tests/writer_tests.rs +545 -0
  38. data/ext/parquet-ruby-adapter/Cargo.toml +22 -0
  39. data/ext/parquet-ruby-adapter/build.rs +5 -0
  40. data/ext/parquet-ruby-adapter/examples/try_into_value_demo.rs +98 -0
  41. data/ext/parquet-ruby-adapter/src/batch_manager.rs +116 -0
  42. data/ext/parquet-ruby-adapter/src/chunk_reader.rs +237 -0
  43. data/ext/parquet-ruby-adapter/src/converter.rs +1685 -0
  44. data/ext/parquet-ruby-adapter/src/error.rs +148 -0
  45. data/ext/{parquet/src/ruby_reader.rs → parquet-ruby-adapter/src/io.rs} +190 -56
  46. data/ext/parquet-ruby-adapter/src/lib.rs +90 -0
  47. data/ext/parquet-ruby-adapter/src/logger.rs +64 -0
  48. data/ext/parquet-ruby-adapter/src/metadata.rs +427 -0
  49. data/ext/parquet-ruby-adapter/src/reader.rs +317 -0
  50. data/ext/parquet-ruby-adapter/src/schema.rs +810 -0
  51. data/ext/parquet-ruby-adapter/src/string_cache.rs +106 -0
  52. data/ext/parquet-ruby-adapter/src/try_into_value.rs +91 -0
  53. data/ext/parquet-ruby-adapter/src/types.rs +94 -0
  54. data/ext/parquet-ruby-adapter/src/utils.rs +186 -0
  55. data/ext/parquet-ruby-adapter/src/writer.rs +435 -0
  56. data/lib/parquet/schema.rb +19 -0
  57. data/lib/parquet/version.rb +1 -1
  58. metadata +50 -24
  59. data/ext/parquet/src/enumerator.rs +0 -68
  60. data/ext/parquet/src/header_cache.rs +0 -99
  61. data/ext/parquet/src/logger.rs +0 -171
  62. data/ext/parquet/src/reader/common.rs +0 -111
  63. data/ext/parquet/src/reader/mod.rs +0 -211
  64. data/ext/parquet/src/reader/parquet_column_reader.rs +0 -44
  65. data/ext/parquet/src/reader/parquet_row_reader.rs +0 -43
  66. data/ext/parquet/src/reader/unified/mod.rs +0 -363
  67. data/ext/parquet/src/types/core_types.rs +0 -120
  68. data/ext/parquet/src/types/mod.rs +0 -100
  69. data/ext/parquet/src/types/parquet_value.rs +0 -1275
  70. data/ext/parquet/src/types/record_types.rs +0 -603
  71. data/ext/parquet/src/types/schema_converter.rs +0 -290
  72. data/ext/parquet/src/types/schema_node.rs +0 -424
  73. data/ext/parquet/src/types/timestamp.rs +0 -285
  74. data/ext/parquet/src/types/type_conversion.rs +0 -1949
  75. data/ext/parquet/src/types/writer_types.rs +0 -329
  76. data/ext/parquet/src/utils.rs +0 -184
  77. data/ext/parquet/src/writer/mod.rs +0 -505
  78. data/ext/parquet/src/writer/write_columns.rs +0 -238
  79. data/ext/parquet/src/writer/write_rows.rs +0 -488
@@ -0,0 +1,534 @@
1
+ use bytes::Bytes;
2
+ use parquet_core::*;
3
+
4
+ mod test_helpers;
5
+
6
+ // ====== Schema Builder Tests ======
7
+
8
+ #[test]
9
+ fn test_schema_builder_error_cases() {
10
+ // Test building without root node
11
+ let result = SchemaBuilder::new().build();
12
+ assert!(result.is_err());
13
+ assert_eq!(result.unwrap_err(), "Schema must have a root node");
14
+ }
15
+
16
+ #[test]
17
+ fn test_schema_builder_default() {
18
+ let builder1 = SchemaBuilder::new();
19
+ let builder2 = SchemaBuilder::default();
20
+
21
+ // Both should fail with the same error when building without a root
22
+ let result1 = builder1.build();
23
+ let result2 = builder2.build();
24
+
25
+ assert!(result1.is_err());
26
+ assert!(result2.is_err());
27
+ assert_eq!(result1.unwrap_err(), result2.unwrap_err());
28
+ }
29
+
30
+ #[test]
31
+ fn test_schema_equality() {
32
+ let schema1 = SchemaBuilder::new()
33
+ .with_root(SchemaNode::Struct {
34
+ name: "root".to_string(),
35
+ nullable: false,
36
+ fields: vec![
37
+ SchemaNode::Primitive {
38
+ name: "id".to_string(),
39
+ primitive_type: PrimitiveType::Int64,
40
+ nullable: false,
41
+ format: None,
42
+ },
43
+ SchemaNode::Primitive {
44
+ name: "name".to_string(),
45
+ primitive_type: PrimitiveType::String,
46
+ nullable: true,
47
+ format: None,
48
+ },
49
+ ],
50
+ })
51
+ .build()
52
+ .unwrap();
53
+
54
+ let schema2 = SchemaBuilder::new()
55
+ .with_root(SchemaNode::Struct {
56
+ name: "root".to_string(),
57
+ nullable: false,
58
+ fields: vec![
59
+ SchemaNode::Primitive {
60
+ name: "id".to_string(),
61
+ primitive_type: PrimitiveType::Int64,
62
+ nullable: false,
63
+ format: None,
64
+ },
65
+ SchemaNode::Primitive {
66
+ name: "name".to_string(),
67
+ primitive_type: PrimitiveType::String,
68
+ nullable: true,
69
+ format: None,
70
+ },
71
+ ],
72
+ })
73
+ .build()
74
+ .unwrap();
75
+
76
+ assert_eq!(schema1, schema2);
77
+ }
78
+
79
+ #[test]
80
+ fn test_schema_inequality() {
81
+ let schema1 = SchemaBuilder::new()
82
+ .with_root(SchemaNode::Struct {
83
+ name: "root".to_string(),
84
+ nullable: false,
85
+ fields: vec![SchemaNode::Primitive {
86
+ name: "id".to_string(),
87
+ primitive_type: PrimitiveType::Int64,
88
+ nullable: false,
89
+ format: None,
90
+ }],
91
+ })
92
+ .build()
93
+ .unwrap();
94
+
95
+ let schema2 = SchemaBuilder::new()
96
+ .with_root(SchemaNode::Struct {
97
+ name: "root".to_string(),
98
+ nullable: false,
99
+ fields: vec![SchemaNode::Primitive {
100
+ name: "id".to_string(),
101
+ primitive_type: PrimitiveType::Int32,
102
+ nullable: false,
103
+ format: None,
104
+ }],
105
+ })
106
+ .build()
107
+ .unwrap();
108
+
109
+ assert_ne!(schema1, schema2);
110
+ }
111
+
112
+ // ====== Complex Schema Construction Tests ======
113
+
114
+ #[test]
115
+ fn test_deeply_nested_schema_construction() {
116
+ let inner_struct = SchemaNode::Struct {
117
+ name: "inner".to_string(),
118
+ nullable: true,
119
+ fields: vec![SchemaNode::Primitive {
120
+ name: "value".to_string(),
121
+ primitive_type: PrimitiveType::String,
122
+ nullable: false,
123
+ format: None,
124
+ }],
125
+ };
126
+
127
+ let list_of_structs = SchemaNode::List {
128
+ name: "list".to_string(),
129
+ nullable: false,
130
+ item: Box::new(inner_struct),
131
+ };
132
+
133
+ let map_with_complex_value = SchemaNode::Map {
134
+ name: "map".to_string(),
135
+ nullable: true,
136
+ key: Box::new(SchemaNode::Primitive {
137
+ name: "key".to_string(),
138
+ primitive_type: PrimitiveType::String,
139
+ nullable: false,
140
+ format: None,
141
+ }),
142
+ value: Box::new(list_of_structs),
143
+ };
144
+
145
+ let schema = SchemaBuilder::new()
146
+ .with_root(SchemaNode::Struct {
147
+ name: "root".to_string(),
148
+ nullable: false,
149
+ fields: vec![map_with_complex_value],
150
+ })
151
+ .build()
152
+ .unwrap();
153
+
154
+ assert_eq!(schema.root.name(), "root");
155
+ }
156
+
157
+ #[test]
158
+ fn test_complex_schema_with_all_node_types() {
159
+ let schema = SchemaBuilder::new()
160
+ .with_root(SchemaNode::Struct {
161
+ name: "root".to_string(),
162
+ nullable: false,
163
+ fields: vec![
164
+ SchemaNode::Primitive {
165
+ name: "id".to_string(),
166
+ primitive_type: PrimitiveType::Int64,
167
+ nullable: false,
168
+ format: None,
169
+ },
170
+ SchemaNode::List {
171
+ name: "tags".to_string(),
172
+ nullable: true,
173
+ item: Box::new(SchemaNode::Primitive {
174
+ name: "tag".to_string(),
175
+ primitive_type: PrimitiveType::String,
176
+ nullable: false,
177
+ format: None,
178
+ }),
179
+ },
180
+ SchemaNode::Map {
181
+ name: "metadata".to_string(),
182
+ nullable: false,
183
+ key: Box::new(SchemaNode::Primitive {
184
+ name: "key".to_string(),
185
+ primitive_type: PrimitiveType::String,
186
+ nullable: false,
187
+ format: None,
188
+ }),
189
+ value: Box::new(SchemaNode::Primitive {
190
+ name: "value".to_string(),
191
+ primitive_type: PrimitiveType::String,
192
+ nullable: true,
193
+ format: None,
194
+ }),
195
+ },
196
+ SchemaNode::Struct {
197
+ name: "nested".to_string(),
198
+ nullable: true,
199
+ fields: vec![
200
+ SchemaNode::Primitive {
201
+ name: "field1".to_string(),
202
+ primitive_type: PrimitiveType::Float64,
203
+ nullable: false,
204
+ format: None,
205
+ },
206
+ SchemaNode::Primitive {
207
+ name: "field2".to_string(),
208
+ primitive_type: PrimitiveType::Boolean,
209
+ nullable: true,
210
+ format: None,
211
+ },
212
+ ],
213
+ },
214
+ ],
215
+ })
216
+ .build()
217
+ .unwrap();
218
+
219
+ assert_eq!(schema.root.name(), "root");
220
+ assert!(!schema.root.is_nullable());
221
+ }
222
+
223
+ // ====== Primitive Type Tests ======
224
+
225
+ #[test]
226
+ fn test_primitive_type_names_and_format_requirements() {
227
+ // Test type_name() for all types
228
+ assert_eq!(PrimitiveType::Int8.type_name(), "Int8");
229
+ assert_eq!(PrimitiveType::Int16.type_name(), "Int16");
230
+ assert_eq!(PrimitiveType::Int32.type_name(), "Int32");
231
+ assert_eq!(PrimitiveType::Int64.type_name(), "Int64");
232
+ assert_eq!(PrimitiveType::UInt8.type_name(), "UInt8");
233
+ assert_eq!(PrimitiveType::UInt16.type_name(), "UInt16");
234
+ assert_eq!(PrimitiveType::UInt32.type_name(), "UInt32");
235
+ assert_eq!(PrimitiveType::UInt64.type_name(), "UInt64");
236
+ assert_eq!(PrimitiveType::Float32.type_name(), "Float32");
237
+ assert_eq!(PrimitiveType::Float64.type_name(), "Float64");
238
+ assert_eq!(PrimitiveType::Decimal128(10, 2).type_name(), "Decimal128");
239
+ assert_eq!(PrimitiveType::Decimal256(20, 4).type_name(), "Decimal256");
240
+ assert_eq!(PrimitiveType::Boolean.type_name(), "Boolean");
241
+ assert_eq!(PrimitiveType::String.type_name(), "String");
242
+ assert_eq!(PrimitiveType::Binary.type_name(), "Binary");
243
+ assert_eq!(PrimitiveType::Date32.type_name(), "Date32");
244
+ assert_eq!(PrimitiveType::Date64.type_name(), "Date64");
245
+ assert_eq!(
246
+ PrimitiveType::TimestampSecond(None).type_name(),
247
+ "TimestampSecond"
248
+ );
249
+ assert_eq!(
250
+ PrimitiveType::TimestampMillis(None).type_name(),
251
+ "TimestampMillis"
252
+ );
253
+ assert_eq!(
254
+ PrimitiveType::TimestampMicros(None).type_name(),
255
+ "TimestampMicros"
256
+ );
257
+ assert_eq!(
258
+ PrimitiveType::TimestampNanos(None).type_name(),
259
+ "TimestampNanos"
260
+ );
261
+ assert_eq!(PrimitiveType::TimeMillis.type_name(), "TimeMillis");
262
+ assert_eq!(PrimitiveType::TimeMicros.type_name(), "TimeMicros");
263
+ assert_eq!(
264
+ PrimitiveType::FixedLenByteArray(16).type_name(),
265
+ "FixedLenByteArray"
266
+ );
267
+
268
+ // Test requires_format()
269
+ assert!(!PrimitiveType::Int32.requires_format());
270
+ assert!(!PrimitiveType::String.requires_format());
271
+ assert!(!PrimitiveType::Binary.requires_format());
272
+ assert!(!PrimitiveType::Decimal128(10, 2).requires_format());
273
+ assert!(!PrimitiveType::FixedLenByteArray(16).requires_format());
274
+
275
+ assert!(PrimitiveType::Date32.requires_format());
276
+ assert!(PrimitiveType::Date64.requires_format());
277
+ assert!(PrimitiveType::TimestampSecond(None).requires_format());
278
+ assert!(PrimitiveType::TimestampMillis(None).requires_format());
279
+ assert!(PrimitiveType::TimestampMicros(None).requires_format());
280
+ assert!(PrimitiveType::TimestampNanos(None).requires_format());
281
+ assert!(PrimitiveType::TimeMillis.requires_format());
282
+ assert!(PrimitiveType::TimeMicros.requires_format());
283
+ }
284
+
285
+ #[test]
286
+ fn test_repetition_from_nullability() {
287
+ let nullable_node = SchemaNode::Primitive {
288
+ name: "nullable".to_string(),
289
+ primitive_type: PrimitiveType::String,
290
+ nullable: true,
291
+ format: None,
292
+ };
293
+ assert_eq!(nullable_node.repetition(), Repetition::Optional);
294
+
295
+ let required_node = SchemaNode::Primitive {
296
+ name: "required".to_string(),
297
+ primitive_type: PrimitiveType::String,
298
+ nullable: false,
299
+ format: None,
300
+ };
301
+ assert_eq!(required_node.repetition(), Repetition::Required);
302
+
303
+ let nullable_struct = SchemaNode::Struct {
304
+ name: "struct".to_string(),
305
+ nullable: true,
306
+ fields: vec![],
307
+ };
308
+ assert_eq!(nullable_struct.repetition(), Repetition::Optional);
309
+
310
+ let nullable_list = SchemaNode::List {
311
+ name: "list".to_string(),
312
+ nullable: true,
313
+ item: Box::new(required_node.clone()),
314
+ };
315
+ assert_eq!(nullable_list.repetition(), Repetition::Optional);
316
+
317
+ let nullable_map = SchemaNode::Map {
318
+ name: "map".to_string(),
319
+ nullable: true,
320
+ key: Box::new(required_node.clone()),
321
+ value: Box::new(nullable_node.clone()),
322
+ };
323
+ assert_eq!(nullable_map.repetition(), Repetition::Optional);
324
+ }
325
+
326
+ // ====== Empty File Handling Test ======
327
+
328
+ #[test]
329
+ fn test_empty_file_handling() {
330
+ let schema = SchemaBuilder::new()
331
+ .with_root(SchemaNode::Struct {
332
+ name: "root".to_string(),
333
+ nullable: false,
334
+ fields: vec![SchemaNode::Primitive {
335
+ name: "id".to_string(),
336
+ primitive_type: PrimitiveType::Int64,
337
+ nullable: false,
338
+ format: None,
339
+ }],
340
+ })
341
+ .build()
342
+ .unwrap();
343
+
344
+ let mut buffer = Vec::new();
345
+ {
346
+ let writer = Writer::new(&mut buffer, schema).unwrap();
347
+ // Close without writing any rows
348
+ writer.close().unwrap();
349
+ }
350
+
351
+ // Try to read empty file
352
+ let bytes = Bytes::from(buffer);
353
+ let reader = Reader::new(bytes);
354
+
355
+ let rows: Vec<_> = reader
356
+ .read_rows()
357
+ .unwrap()
358
+ .collect::<Result<Vec<_>>>()
359
+ .unwrap();
360
+
361
+ assert_eq!(rows.len(), 0);
362
+ }
363
+
364
+ // ====== All Primitive Types Test ======
365
+ // Using the comprehensive version from schema_builder_tests.rs which includes all types
366
+
367
+ #[test]
368
+ fn test_all_primitive_types_in_schema() {
369
+ let fields = vec![
370
+ SchemaNode::Primitive {
371
+ name: "int8".to_string(),
372
+ primitive_type: PrimitiveType::Int8,
373
+ nullable: false,
374
+ format: None,
375
+ },
376
+ SchemaNode::Primitive {
377
+ name: "int16".to_string(),
378
+ primitive_type: PrimitiveType::Int16,
379
+ nullable: false,
380
+ format: None,
381
+ },
382
+ SchemaNode::Primitive {
383
+ name: "int32".to_string(),
384
+ primitive_type: PrimitiveType::Int32,
385
+ nullable: false,
386
+ format: None,
387
+ },
388
+ SchemaNode::Primitive {
389
+ name: "int64".to_string(),
390
+ primitive_type: PrimitiveType::Int64,
391
+ nullable: false,
392
+ format: None,
393
+ },
394
+ SchemaNode::Primitive {
395
+ name: "uint8".to_string(),
396
+ primitive_type: PrimitiveType::UInt8,
397
+ nullable: false,
398
+ format: None,
399
+ },
400
+ SchemaNode::Primitive {
401
+ name: "uint16".to_string(),
402
+ primitive_type: PrimitiveType::UInt16,
403
+ nullable: false,
404
+ format: None,
405
+ },
406
+ SchemaNode::Primitive {
407
+ name: "uint32".to_string(),
408
+ primitive_type: PrimitiveType::UInt32,
409
+ nullable: false,
410
+ format: None,
411
+ },
412
+ SchemaNode::Primitive {
413
+ name: "uint64".to_string(),
414
+ primitive_type: PrimitiveType::UInt64,
415
+ nullable: false,
416
+ format: None,
417
+ },
418
+ SchemaNode::Primitive {
419
+ name: "float32".to_string(),
420
+ primitive_type: PrimitiveType::Float32,
421
+ nullable: false,
422
+ format: None,
423
+ },
424
+ SchemaNode::Primitive {
425
+ name: "float64".to_string(),
426
+ primitive_type: PrimitiveType::Float64,
427
+ nullable: false,
428
+ format: None,
429
+ },
430
+ SchemaNode::Primitive {
431
+ name: "decimal128".to_string(),
432
+ primitive_type: PrimitiveType::Decimal128(38, 10),
433
+ nullable: false,
434
+ format: None,
435
+ },
436
+ SchemaNode::Primitive {
437
+ name: "decimal256".to_string(),
438
+ primitive_type: PrimitiveType::Decimal256(76, 20),
439
+ nullable: false,
440
+ format: None,
441
+ },
442
+ SchemaNode::Primitive {
443
+ name: "boolean".to_string(),
444
+ primitive_type: PrimitiveType::Boolean,
445
+ nullable: false,
446
+ format: None,
447
+ },
448
+ SchemaNode::Primitive {
449
+ name: "string".to_string(),
450
+ primitive_type: PrimitiveType::String,
451
+ nullable: false,
452
+ format: None,
453
+ },
454
+ SchemaNode::Primitive {
455
+ name: "binary".to_string(),
456
+ primitive_type: PrimitiveType::Binary,
457
+ nullable: false,
458
+ format: None,
459
+ },
460
+ SchemaNode::Primitive {
461
+ name: "date32".to_string(),
462
+ primitive_type: PrimitiveType::Date32,
463
+ nullable: false,
464
+ format: Some("date".to_string()),
465
+ },
466
+ SchemaNode::Primitive {
467
+ name: "date64".to_string(),
468
+ primitive_type: PrimitiveType::Date64,
469
+ nullable: false,
470
+ format: Some("date".to_string()),
471
+ },
472
+ SchemaNode::Primitive {
473
+ name: "timestamp_second".to_string(),
474
+ primitive_type: PrimitiveType::TimestampSecond(None),
475
+ nullable: false,
476
+ format: Some("timestamp".to_string()),
477
+ },
478
+ SchemaNode::Primitive {
479
+ name: "timestamp_millis".to_string(),
480
+ primitive_type: PrimitiveType::TimestampMillis(None),
481
+ nullable: false,
482
+ format: Some("timestamp".to_string()),
483
+ },
484
+ SchemaNode::Primitive {
485
+ name: "timestamp_micros".to_string(),
486
+ primitive_type: PrimitiveType::TimestampMicros(None),
487
+ nullable: false,
488
+ format: Some("timestamp".to_string()),
489
+ },
490
+ SchemaNode::Primitive {
491
+ name: "timestamp_nanos".to_string(),
492
+ primitive_type: PrimitiveType::TimestampNanos(None),
493
+ nullable: false,
494
+ format: Some("timestamp".to_string()),
495
+ },
496
+ SchemaNode::Primitive {
497
+ name: "time_millis".to_string(),
498
+ primitive_type: PrimitiveType::TimeMillis,
499
+ nullable: false,
500
+ format: Some("time".to_string()),
501
+ },
502
+ SchemaNode::Primitive {
503
+ name: "time_micros".to_string(),
504
+ primitive_type: PrimitiveType::TimeMicros,
505
+ nullable: false,
506
+ format: Some("time".to_string()),
507
+ },
508
+ SchemaNode::Primitive {
509
+ name: "fixed_len_byte_array".to_string(),
510
+ primitive_type: PrimitiveType::FixedLenByteArray(16),
511
+ nullable: false,
512
+ format: None,
513
+ },
514
+ ];
515
+
516
+ let schema = SchemaBuilder::new()
517
+ .with_root(SchemaNode::Struct {
518
+ name: "root".to_string(),
519
+ nullable: false,
520
+ fields,
521
+ })
522
+ .build()
523
+ .unwrap();
524
+
525
+ assert_eq!(schema.root.name(), "root");
526
+
527
+ // Verify we can create a writer with this schema
528
+ let mut buffer = Vec::new();
529
+ let writer_result = Writer::new(&mut buffer, schema);
530
+ assert!(
531
+ writer_result.is_ok(),
532
+ "Should be able to create writer with all primitive types"
533
+ );
534
+ }