hawk_data/
output.rs

1use indexmap::IndexSet;
2use serde_json::Value;
3use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
4
5use crate::{value_to_string, Error, OutputFormat};
6
7#[derive(Debug)]
8enum DataType {
9    SimpleList,  // [string, number, bool]
10    ObjectArray, // [{"name": "Alice"}, {"name": "Bob"}]
11    NestedArray, // [[{"name": "Project1"}], [{"name": "Project2"}]]
12    Mixed,       // Other complex structures
13}
14
15/// カラー出力設定
16struct ColorScheme {
17    header: ColorSpec,
18    number: ColorSpec,
19    string: ColorSpec,
20    boolean: ColorSpec,
21    null: ColorSpec,
22    array_info: ColorSpec,
23}
24
25impl ColorScheme {
26    fn new() -> Self {
27        let mut header = ColorSpec::new();
28        header.set_fg(Some(Color::Blue)).set_bold(true);
29
30        let mut number = ColorSpec::new();
31        number.set_fg(Some(Color::Green));
32
33        let mut boolean = ColorSpec::new();
34        boolean.set_fg(Some(Color::Yellow));
35
36        let mut null = ColorSpec::new();
37        null.set_fg(Some(Color::Black)).set_intense(true); // グレー
38
39        let mut array_info = ColorSpec::new();
40        array_info.set_fg(Some(Color::Cyan));
41
42        Self {
43            header,
44            number,
45            string: ColorSpec::new(), // デフォルト色
46            boolean,
47            null,
48            array_info,
49        }
50    }
51}
52
53/// TTY判定とカラー出力の可否
54fn should_use_colors() -> bool {
55    std::io::IsTerminal::is_terminal(&std::io::stdout()) && std::env::var("NO_COLOR").is_err()
56}
57
58/// 値の型に応じたカラースペックを取得
59fn get_color_for_value<'a>(value: &Value, colors: &'a ColorScheme) -> &'a ColorSpec {
60    match value {
61        Value::Number(_) => &colors.number,
62        Value::Bool(_) => &colors.boolean,
63        Value::Null => &colors.null,
64        _ => &colors.string,
65    }
66}
67
68pub fn format_output(data: &[Value], format: OutputFormat) -> Result<(), Error> {
69    if data.is_empty() {
70        return Ok(());
71    }
72
73    let use_colors = should_use_colors();
74
75    match format {
76        OutputFormat::Json => {
77            // 明示的にJSON出力
78            print_as_json(data, use_colors)?;
79        }
80        OutputFormat::Table => {
81            // 明示的にテーブル出力(可能な場合)
82            if is_object_array(data) {
83                print_as_table(data, use_colors)?;
84            } else {
85                let flattened = flatten_nested_arrays(data);
86                if is_object_array(&flattened) {
87                    print_as_table(&flattened, use_colors)?;
88                } else {
89                    return Err(Error::InvalidQuery(
90                        "Cannot display as table: data is not object array".into(),
91                    ));
92                }
93            }
94        }
95        OutputFormat::List => {
96            // 明示的にリスト出力
97            print_as_list(data, use_colors)?;
98        }
99        OutputFormat::Csv => {
100            print_as_csv(data, use_colors)?;
101        }
102        OutputFormat::Auto => {
103            // 既存のスマート判定ロジック
104            match analyze_data_structure(data) {
105                DataType::SimpleList => print_as_list(data, use_colors)?,
106                DataType::ObjectArray => print_as_table(data, use_colors)?,
107                DataType::NestedArray => {
108                    let flattened = flatten_nested_arrays(data);
109                    if is_object_array(&flattened) {
110                        print_as_table(&flattened, use_colors)?;
111                    } else if is_simple_values(&flattened) {
112                        print_as_list(&flattened, use_colors)?;
113                    } else {
114                        print_as_json(data, use_colors)?;
115                    }
116                }
117                DataType::Mixed => print_as_json(data, use_colors)?,
118            }
119        }
120    }
121
122    Ok(())
123}
124
125fn analyze_data_structure(data: &[Value]) -> DataType {
126    if is_simple_values(data) {
127        return DataType::SimpleList;
128    }
129
130    if is_object_array(data) {
131        return DataType::ObjectArray;
132    }
133
134    // ネストした配列かチェック
135    if data.len() == 1 && data[0].is_array() {
136        return DataType::NestedArray;
137    }
138
139    DataType::Mixed
140}
141
142fn flatten_nested_arrays(data: &[Value]) -> Vec<Value> {
143    let mut flattened = Vec::new();
144
145    for item in data {
146        match item {
147            Value::Array(arr) => {
148                // 配列の中身を展開
149                flattened.extend(arr.iter().cloned());
150            }
151            _ => {
152                flattened.push(item.clone());
153            }
154        }
155    }
156
157    flattened
158}
159
160fn collect_flattened_fields_ordered(value: &Value, prefix: &str, fields: &mut IndexSet<String>) {
161    match value {
162        Value::Object(obj) => {
163            for (key, val) in obj {
164                // serde_json::Mapは順序を保持
165                let field_name = if prefix.is_empty() {
166                    key.clone()
167                } else {
168                    format!("{}.{}", prefix, key)
169                };
170
171                match val {
172                    Value::Object(_) => {
173                        collect_flattened_fields_ordered(val, &field_name, fields);
174                    }
175                    _ => {
176                        fields.insert(field_name);
177                    }
178                }
179            }
180        }
181        _ => {
182            if !prefix.is_empty() {
183                fields.insert(prefix.to_string());
184            }
185        }
186    }
187}
188
189fn get_flattened_value(item: &Value, field_path: &str) -> String {
190    let parts: Vec<&str> = field_path.split('.').collect();
191    let mut current = item;
192
193    for part in parts {
194        match current.get(part) {
195            Some(val) => current = val,
196            None => return "".to_string(),
197        }
198    }
199
200    match current {
201        Value::Array(arr) => {
202            // 配列は簡略表示
203            format!("[{} items]", arr.len())
204        }
205        _ => value_to_string(current),
206    }
207}
208
209fn get_field_value_for_coloring(item: &Value, field_path: &str) -> Value {
210    let parts: Vec<&str> = field_path.split('.').collect();
211    let mut current = item;
212
213    for part in parts {
214        match current.get(part) {
215            Some(val) => current = val,
216            None => return Value::Null,
217        }
218    }
219
220    current.clone()
221}
222
223fn get_value_type_info(value: &Value) -> &'static str {
224    match value {
225        Value::String(_) => "String",
226        Value::Number(_) => "Number",
227        Value::Bool(_) => "Boolean",
228        Value::Array(_) => "Array",
229        Value::Object(_) => "Object",
230        Value::Null => "Null",
231    }
232}
233
234fn get_sample_value(value: &Value) -> String {
235    match value {
236        Value::String(s) => format!("\"{}\"", s.chars().take(20).collect::<String>()),
237        Value::Number(n) => n.to_string(),
238        Value::Bool(b) => b.to_string(),
239        Value::Array(arr) => format!("[{} items]", arr.len()),
240        Value::Object(obj) => format!("{{{}...}}", obj.keys().next().unwrap_or(&"".to_string())),
241        Value::Null => "null".to_string(),
242    }
243}
244
245fn is_simple_values(data: &[Value]) -> bool {
246    data.iter()
247        .all(|v| matches!(v, Value::String(_) | Value::Number(_) | Value::Bool(_)))
248}
249
250fn is_object_array(data: &[Value]) -> bool {
251    data.iter().all(|v| v.is_object())
252}
253
254fn print_as_list(data: &[Value], use_colors: bool) -> Result<(), Error> {
255    if use_colors {
256        let mut stdout = StandardStream::stdout(ColorChoice::Always);
257        let colors = ColorScheme::new();
258
259        for item in data {
260            let color = get_color_for_value(item, &colors);
261            stdout.set_color(color)?;
262            print!("{}", value_to_string(item));
263            stdout.reset()?;
264            println!();
265        }
266    } else {
267        data.iter().for_each(|item| {
268            println!("{}", value_to_string(item));
269        });
270    }
271
272    Ok(())
273}
274
275fn print_as_json(data: &[Value], use_colors: bool) -> Result<(), Error> {
276    if use_colors {
277        print_colored_json_simple(data)?;
278    } else {
279        let json = serde_json::to_string_pretty(data).map_err(Error::Json)?;
280        println!("{}", json);
281    }
282
283    Ok(())
284}
285
286fn print_colored_json_simple(data: &[Value]) -> Result<(), Error> {
287    let mut stdout = StandardStream::stdout(ColorChoice::Always);
288    let colors = ColorScheme::new();
289
290    // シンプルなアプローチ:行ごとに処理
291    let json = serde_json::to_string_pretty(data).map_err(Error::Json)?;
292
293    for line in json.lines() {
294        let trimmed = line.trim();
295
296        // 行の内容に応じて色付け
297        if trimmed.starts_with('"') && trimmed.contains(':') {
298            // キー行: "key": value
299            if let Some(colon_pos) = trimmed.find(':') {
300                let key_part = &trimmed[..colon_pos + 1];
301                let value_part = &trimmed[colon_pos + 1..].trim();
302
303                // インデントを保持
304                let indent = &line[..line.len() - line.trim_start().len()];
305                print!("{}", indent);
306
307                // キー部分(青色)
308                stdout.set_color(&colors.header)?;
309                print!("{}", key_part);
310                stdout.reset()?;
311
312                print!(" ");
313
314                // 値部分(型に応じた色)
315                print_colored_json_value(value_part, &colors, &mut stdout)?;
316                println!();
317            } else {
318                println!("{}", line);
319            }
320        } else if trimmed.starts_with('{')
321            || trimmed.starts_with('}')
322            || trimmed.starts_with('[')
323            || trimmed.starts_with(']')
324        {
325            // 構造文字(青色)
326            let indent = &line[..line.len() - line.trim_start().len()];
327            print!("{}", indent);
328            stdout.set_color(&colors.header)?;
329            print!("{}", trimmed);
330            stdout.reset()?;
331            println!();
332        } else {
333            // その他の行(配列要素など)
334            let indent = &line[..line.len() - line.trim_start().len()];
335            print!("{}", indent);
336            print_colored_json_value(trimmed, &colors, &mut stdout)?;
337            println!();
338        }
339    }
340
341    Ok(())
342}
343
344fn print_colored_json_value(
345    value_str: &str,
346    colors: &ColorScheme,
347    stdout: &mut StandardStream,
348) -> Result<(), Error> {
349    let clean_value = value_str.trim_end_matches(',');
350
351    if clean_value == "null" {
352        stdout.set_color(&colors.null)?;
353        print!("{}", value_str);
354        stdout.reset()?;
355    } else if clean_value == "true" || clean_value == "false" {
356        stdout.set_color(&colors.boolean)?;
357        print!("{}", value_str);
358        stdout.reset()?;
359    } else if clean_value.starts_with('"') && clean_value.ends_with('"') {
360        // 文字列値
361        stdout.set_color(&colors.string)?;
362        print!("{}", value_str);
363        stdout.reset()?;
364    } else if clean_value.parse::<f64>().is_ok() {
365        // 数値
366        stdout.set_color(&colors.number)?;
367        print!("{}", value_str);
368        stdout.reset()?;
369    } else {
370        // その他
371        print!("{}", value_str);
372    }
373
374    Ok(())
375}
376
377fn print_as_csv(data: &[Value], use_colors: bool) -> Result<(), Error> {
378    if data.is_empty() {
379        return Ok(());
380    }
381
382    // 1. 全オブジェクトからフラット化されたフィールド名を収集
383    let mut all_fields = IndexSet::new();
384    for item in data {
385        collect_flattened_fields_ordered(item, "", &mut all_fields);
386    }
387
388    let fields: Vec<String> = all_fields.into_iter().collect();
389
390    if use_colors {
391        print_colored_csv(data, &fields)?;
392    } else {
393        print_plain_csv(data, &fields);
394    }
395
396    Ok(())
397}
398
399fn print_colored_csv(data: &[Value], fields: &[String]) -> Result<(), Error> {
400    let mut stdout = StandardStream::stdout(ColorChoice::Always);
401    let colors = ColorScheme::new();
402
403    // 2. ヘッダー出力(色付き)
404    stdout.set_color(&colors.header)?;
405    let header_row = escape_and_join_csv_row(fields);
406    print!("{}", header_row);
407    stdout.reset()?;
408    println!();
409
410    // 3. データ行出力(色付き)
411    for item in data {
412        let row_values: Vec<String> = fields
413            .iter()
414            .map(|field| get_flattened_value(item, field))
415            .collect();
416
417        print_colored_csv_row(item, fields, &row_values, &mut stdout, &colors)?;
418    }
419
420    Ok(())
421}
422
423/// Color付きCSV行出力
424fn print_colored_csv_row(
425    item: &Value,
426    fields: &[String],
427    row_values: &[String],
428    stdout: &mut StandardStream,
429    colors: &ColorScheme,
430) -> Result<(), Error> {
431    let mut first = true;
432    
433    for (i, value_str) in row_values.iter().enumerate() {
434        if !first {
435            print!(",");
436        }
437        first = false;
438
439        // 値の型に応じて色を設定
440        let field = &fields[i];
441        let value = get_field_value_for_coloring(item, field);
442        let color = get_color_for_value(&value, colors);
443
444        stdout.set_color(color)?;
445        let escaped_value = escape_csv_value(value_str);
446        print!("{}", escaped_value);
447        stdout.reset()?;
448    }
449    println!();
450
451    Ok(())
452}
453
454/// Plain CSV出力
455fn print_plain_csv(data: &[Value], fields: &[String]) {
456    // 2. ヘッダー出力
457    print_csv_row(fields);
458
459    // 3. データ行出力
460    for item in data {
461        let row_values: Vec<String> = fields
462            .iter()
463            .map(|field| get_flattened_value(item, field))
464            .collect();
465        print_csv_row(&row_values);
466    }
467}
468
469/// CSV行を出力する(CSVエスケープ処理付き)
470fn print_csv_row(values: &[String]) {
471    let escaped_values: Vec<String> = values
472        .iter()
473        .map(|value| escape_csv_value(value))
474        .collect();
475    
476    println!("{}", escaped_values.join(","));
477}
478
479/// CSV値をエスケープしてカンマで結合
480fn escape_and_join_csv_row(values: &[String]) -> String {
481    let escaped_values: Vec<String> = values
482        .iter()
483        .map(|value| escape_csv_value(value))
484        .collect();
485    
486    escaped_values.join(",")
487}
488
489/// CSV値をエスケープする
490fn escape_csv_value(value: &str) -> String {
491    // CSVエスケープが必要な条件:
492    // 1. カンマが含まれている
493    // 2. ダブルクォートが含まれている  
494    // 3. 改行が含まれている
495    // 4. 先頭または末尾にスペースがある
496    
497    let needs_quoting = value.contains(',') 
498        || value.contains('"') 
499        || value.contains('\n') 
500        || value.contains('\r')
501        || value.starts_with(' ') 
502        || value.ends_with(' ')
503        || value.is_empty(); // 空文字列も引用符で囲む
504
505    if needs_quoting {
506        // ダブルクォートをエスケープ(" → "")してから全体を引用符で囲む
507        format!("\"{}\"", value.replace('"', "\"\""))
508    } else {
509        value.to_string()
510    }
511}
512
513fn print_as_table(data: &[Value], use_colors: bool) -> Result<(), Error> {
514    if data.is_empty() {
515        return Ok(());
516    }
517
518    // 1. 全オブジェクトからフラット化されたフィールド名を収集
519    let mut all_fields = IndexSet::new();
520    for item in data {
521        collect_flattened_fields_ordered(item, "", &mut all_fields);
522    }
523
524    let fields: Vec<String> = all_fields.into_iter().collect();
525
526    // 2. 各列の最大幅を計算
527    let mut max_widths = vec![0; fields.len()];
528
529    // ヘッダーの幅
530    for (i, field) in fields.iter().enumerate() {
531        max_widths[i] = field.len();
532    }
533
534    // データの幅
535    for item in data {
536        for (i, field) in fields.iter().enumerate() {
537            let value_str = get_flattened_value(item, field);
538            max_widths[i] = max_widths[i].max(value_str.len());
539        }
540    }
541
542    if use_colors {
543        print_colored_table(data, &fields, &max_widths)?;
544    } else {
545        print_plain_table(data, &fields, &max_widths);
546    }
547
548    Ok(())
549}
550
551fn print_colored_table(
552    data: &[Value],
553    fields: &[String],
554    max_widths: &[usize],
555) -> Result<(), Error> {
556    let mut stdout = StandardStream::stdout(ColorChoice::Always);
557    let colors = ColorScheme::new();
558
559    // 3. ヘッダー出力(色付き)
560    stdout.set_color(&colors.header)?;
561    for (i, field) in fields.iter().enumerate() {
562        print!("{:<width$}", field, width = max_widths[i]);
563        if i < fields.len() - 1 {
564            print!("  ");
565        }
566    }
567    stdout.reset()?;
568    println!();
569
570    // 4. データ行出力(色付き)
571    for item in data {
572        for (i, field) in fields.iter().enumerate() {
573            let value_str = get_flattened_value(item, field);
574
575            // 値の型に応じて色を設定
576            let value = get_field_value_for_coloring(item, field);
577            let color = get_color_for_value(&value, &colors);
578
579            stdout.set_color(color)?;
580            print!("{:<width$}", value_str, width = max_widths[i]);
581            stdout.reset()?;
582
583            if i < fields.len() - 1 {
584                print!("  ");
585            }
586        }
587        println!();
588    }
589
590    Ok(())
591}
592
593fn print_plain_table(data: &[Value], fields: &[String], max_widths: &[usize]) {
594    // 3. ヘッダー出力
595    for (i, field) in fields.iter().enumerate() {
596        print!("{:<width$}", field, width = max_widths[i]);
597        if i < fields.len() - 1 {
598            print!("  ");
599        }
600    }
601    println!();
602
603    // 4. データ行出力
604    for item in data {
605        for (i, field) in fields.iter().enumerate() {
606            let value_str = get_flattened_value(item, field);
607            print!("{:<width$}", value_str, width = max_widths[i]);
608            if i < fields.len() - 1 {
609                print!("  ");
610            }
611        }
612        println!();
613    }
614}
615
616pub fn print_data_info(data: &[Value]) {
617    let use_colors = should_use_colors();
618
619    if use_colors {
620        print_colored_data_info(data).unwrap_or_else(|_| print_plain_data_info(data));
621    } else {
622        print_plain_data_info(data);
623    }
624}
625
626fn print_colored_data_info(data: &[Value]) -> Result<(), Error> {
627    let mut stdout = StandardStream::stdout(ColorChoice::Always);
628    let colors = ColorScheme::new();
629
630    // タイトル
631    stdout.set_color(&colors.header)?;
632    println!("=== Data Information ===");
633    stdout.reset()?;
634
635    println!("Total records: {}", data.len());
636
637    if data.is_empty() {
638        return Ok(());
639    }
640
641    // データ型の分析 - 修正部分
642    if data.len() == 1 {
643        let first_item = &data[0];
644        match first_item {
645            Value::Object(obj) => {
646                println!("Type: Single Object"); // ← 修正: "Object Array" → "Single Object"
647                println!("Fields: {}", obj.len());
648                println!();
649
650                // フィールド一覧と型情報
651                stdout.set_color(&colors.header)?;
652                println!("Field Details:");
653                stdout.reset()?;
654
655                for (key, value) in obj {
656                    let field_type = get_value_type_info(value);
657                    let sample_value = get_sample_value(value);
658
659                    // フィールド名を色付き
660                    stdout.set_color(&colors.string)?;
661                    print!("  {:<15}", key);
662                    stdout.reset()?;
663
664                    // 型情報を色付き
665                    let type_color = get_color_for_value(value, &colors);
666                    stdout.set_color(type_color)?;
667                    print!(" {:<10}", field_type);
668                    stdout.reset()?;
669
670                    println!(" (e.g., {})", sample_value);
671                }
672
673                // 配列フィールドの詳細
674                println!();
675                stdout.set_color(&colors.header)?;
676                println!("Array Fields:");
677                stdout.reset()?;
678
679                for (key, value) in obj {
680                    if let Value::Array(arr) = value {
681                        stdout.set_color(&colors.array_info)?;
682                        print!("  {:<15}", key);
683                        stdout.reset()?;
684                        println!(" [{} items]", arr.len());
685
686                        if let Some(first_elem) = arr.first() {
687                            if let Value::Object(elem_obj) = first_elem {
688                                print!("    └─ ");
689                                let sub_fields: Vec<&String> = elem_obj.keys().collect();
690                                let sub_fields: Vec<&str> =
691                                    sub_fields.into_iter().map(|f| f.as_str()).collect();
692                                println!("{}", sub_fields.join(", "));
693                            }
694                        }
695                    }
696                }
697            }
698            Value::Array(_) => {
699                println!("Type: Nested Array");
700            }
701            _ => {
702                println!("Type: Single Value");
703                println!("Value: {}", get_sample_value(first_item));
704            }
705        }
706    } else {
707        // 複数のレコードがある場合(本来のObject Array)
708        let first_item = &data[0];
709        match first_item {
710            Value::Object(obj) => {
711                println!("Type: Object Array");
712                println!("Fields: {}", obj.len());
713                println!();
714
715                // 以下は既存のObject Array処理...
716                stdout.set_color(&colors.header)?;
717                println!("Field Details:");
718                stdout.reset()?;
719
720                for (key, value) in obj {
721                    let field_type = get_value_type_info(value);
722                    let sample_value = get_sample_value(value);
723
724                    stdout.set_color(&colors.string)?;
725                    print!("  {:<15}", key);
726                    stdout.reset()?;
727
728                    let type_color = get_color_for_value(value, &colors);
729                    stdout.set_color(type_color)?;
730                    print!(" {:<10}", field_type);
731                    stdout.reset()?;
732
733                    println!(" (e.g., {})", sample_value);
734                }
735
736                println!();
737                stdout.set_color(&colors.header)?;
738                println!("Array Fields:");
739                stdout.reset()?;
740
741                for (key, value) in obj {
742                    if let Value::Array(arr) = value {
743                        stdout.set_color(&colors.array_info)?;
744                        print!("  {:<15}", key);
745                        stdout.reset()?;
746                        println!(" [{} items]", arr.len());
747
748                        if let Some(first_elem) = arr.first() {
749                            if let Value::Object(elem_obj) = first_elem {
750                                print!("    └─ ");
751                                let sub_fields: Vec<&String> = elem_obj.keys().collect();
752                                let sub_fields: Vec<&str> =
753                                    sub_fields.into_iter().map(|f| f.as_str()).collect();
754                                println!("{}", sub_fields.join(", "));
755                            }
756                        }
757                    }
758                }
759            }
760            _ => {
761                println!("Type: Simple Values");
762            }
763        }
764    }
765
766    Ok(())
767}
768
769fn print_plain_data_info(data: &[Value]) {
770    println!("=== Data Information ===");
771    println!("Total records: {}", data.len());
772
773    if data.is_empty() {
774        return;
775    }
776
777    // データ型の分析 - 修正部分
778    if data.len() == 1 {
779        let first_item = &data[0];
780        match first_item {
781            Value::Object(obj) => {
782                println!("Type: Single Object"); // ← 修正
783                println!("Fields: {}", obj.len());
784                println!();
785
786                println!("Field Details:");
787                for (key, value) in obj {
788                    let field_type = get_value_type_info(value);
789                    let sample_value = get_sample_value(value);
790                    println!("  {:<15} {:<10} (e.g., {})", key, field_type, sample_value);
791                }
792
793                println!();
794                println!("Array Fields:");
795                for (key, value) in obj {
796                    if let Value::Array(arr) = value {
797                        println!("  {:<15} [{} items]", key, arr.len());
798                        if let Some(first_elem) = arr.first() {
799                            if let Value::Object(elem_obj) = first_elem {
800                                print!("    └─ ");
801                                let sub_fields: Vec<&String> = elem_obj.keys().collect();
802                                let sub_fields: Vec<&str> =
803                                    sub_fields.into_iter().map(|f| f.as_str()).collect();
804                                println!("{}", sub_fields.join(", "));
805                            }
806                        }
807                    }
808                }
809            }
810            Value::Array(_) => {
811                println!("Type: Nested Array");
812            }
813            _ => {
814                println!("Type: Single Value");
815                println!("Value: {}", get_sample_value(first_item));
816            }
817        }
818    } else {
819        // 複数レコード用の既存処理
820        let first_item = &data[0];
821        match first_item {
822            Value::Object(obj) => {
823                println!("Type: Object Array");
824                println!("Fields: {}", obj.len());
825                println!();
826
827                println!("Field Details:");
828                for (key, value) in obj {
829                    let field_type = get_value_type_info(value);
830                    let sample_value = get_sample_value(value);
831                    println!("  {:<15} {:<10} (e.g., {})", key, field_type, sample_value);
832                }
833
834                println!();
835                println!("Array Fields:");
836                for (key, value) in obj {
837                    if let Value::Array(arr) = value {
838                        println!("  {:<15} [{} items]", key, arr.len());
839                        if let Some(first_elem) = arr.first() {
840                            if let Value::Object(elem_obj) = first_elem {
841                                print!("    └─ ");
842                                let sub_fields: Vec<&String> = elem_obj.keys().collect();
843                                let sub_fields: Vec<&str> =
844                                    sub_fields.into_iter().map(|f| f.as_str()).collect();
845                                println!("{}", sub_fields.join(", "));
846                            }
847                        }
848                    }
849                }
850            }
851            Value::Array(_) => {
852                println!("Type: Nested Array");
853            }
854            _ => {
855                println!("Type: Simple Values");
856            }
857        }
858    }
859}