sql_cli/
column_manager.rs

1use serde_json::Value;
2
3/// Manages column-related operations for table display
4pub struct ColumnManager;
5
6impl ColumnManager {
7    /// Calculate optimal column widths based on data content
8    pub fn calculate_optimal_widths(data: &[Value]) -> Vec<u16> {
9        if data.is_empty() {
10            return Vec::new();
11        }
12
13        let first_row = match data.first() {
14            Some(row) => row,
15            None => return Vec::new(),
16        };
17
18        let obj = match first_row.as_object() {
19            Some(obj) => obj,
20            None => return Vec::new(),
21        };
22
23        let headers: Vec<&str> = obj.keys().map(|k| k.as_str()).collect();
24        let mut widths = Vec::new();
25
26        // For large datasets, sample rows instead of checking all
27        const MAX_ROWS_TO_CHECK: usize = 100;
28        let total_rows = data.len();
29
30        // Determine which rows to sample
31        let rows_to_check: Vec<usize> = if total_rows <= MAX_ROWS_TO_CHECK {
32            // Check all rows for small datasets
33            (0..total_rows).collect()
34        } else {
35            // Sample evenly distributed rows for large datasets
36            let step = total_rows / MAX_ROWS_TO_CHECK;
37            (0..MAX_ROWS_TO_CHECK)
38                .map(|i| (i * step).min(total_rows - 1))
39                .collect()
40        };
41
42        for header in &headers {
43            // Start with header width
44            let mut max_width = header.len();
45
46            // Check only sampled rows for this column
47            for &row_idx in &rows_to_check {
48                if let Some(row) = data.get(row_idx) {
49                    if let Some(obj) = row.as_object() {
50                        if let Some(value) = obj.get(*header) {
51                            let display_len = match value {
52                                Value::String(s) => s.len(),
53                                Value::Number(n) => n.to_string().len(),
54                                Value::Bool(b) => b.to_string().len(),
55                                Value::Null => 4, // "null".len()
56                                _ => value.to_string().len(),
57                            };
58                            max_width = max_width.max(display_len);
59                        }
60                    }
61                }
62            }
63
64            // Add some padding and set reasonable limits
65            let optimal_width = (max_width + 2).max(4).min(50); // 4-50 char range with 2 char padding
66            widths.push(optimal_width as u16);
67        }
68
69        widths
70    }
71
72    /// Calculate column widths for filtered data (string arrays)
73    pub fn calculate_widths_for_filtered(headers: &[String], data: &[Vec<String>]) -> Vec<u16> {
74        let mut widths = Vec::new();
75
76        // For large datasets, sample rows instead of checking all
77        const MAX_ROWS_TO_CHECK: usize = 100;
78        let total_rows = data.len();
79
80        // Determine which rows to sample
81        let rows_to_check: Vec<usize> = if total_rows <= MAX_ROWS_TO_CHECK {
82            (0..total_rows).collect()
83        } else {
84            let step = total_rows / MAX_ROWS_TO_CHECK;
85            (0..MAX_ROWS_TO_CHECK)
86                .map(|i| (i * step).min(total_rows - 1))
87                .collect()
88        };
89
90        for (col_idx, header) in headers.iter().enumerate() {
91            // Start with header width
92            let mut max_width = header.len();
93
94            // Check only sampled rows for this column
95            for &row_idx in &rows_to_check {
96                if let Some(row) = data.get(row_idx) {
97                    if let Some(value) = row.get(col_idx) {
98                        max_width = max_width.max(value.len());
99                    }
100                }
101            }
102
103            // Add some padding and set reasonable limits
104            let optimal_width = (max_width + 2).max(4).min(50);
105            widths.push(optimal_width as u16);
106        }
107
108        widths
109    }
110
111    /// Get display width for a single value
112    pub fn get_value_display_width(value: &Value) -> usize {
113        match value {
114            Value::String(s) => s.len(),
115            Value::Number(n) => n.to_string().len(),
116            Value::Bool(b) => b.to_string().len(),
117            Value::Null => 4, // "null"
118            _ => value.to_string().len(),
119        }
120    }
121}