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