use crate::workbook::{CellValue, SheetData};
use anyhow::Result;
use prettytable::{Cell, Row, Table, format};
fn format_cell_value(value: &str, max_width: usize, wrap: bool) -> String {
let char_count = value.chars().count();
if char_count <= max_width {
return value.to_string();
}
if wrap {
if max_width > 3 {
let truncated: String = value.chars().take(max_width - 3).collect();
format!("{}...", truncated)
} else {
value.chars().take(max_width).collect()
}
} else {
if max_width > 3 {
let truncated: String = value.chars().take(max_width - 3).collect();
format!("{}...", truncated)
} else {
value.chars().take(max_width).collect()
}
}
}
pub fn display_table(
data: &SheetData,
sheet_name: &str,
max_rows: usize,
all_sheets: &[&str],
max_width: usize,
wrap: bool,
show_formulas: bool,
) -> Result<()> {
println!("\n╔═════════════════════════════════════════════════╗");
println!("║ xleak - Excel File Viewer ║");
println!("╚═════════════════════════════════════════════════╝");
println!();
println!(
"Sheet: {} ({} rows × {} columns)",
sheet_name, data.height, data.width
);
if all_sheets.len() > 1 {
println!("Available sheets: {}", all_sheets.join(", "));
}
println!();
if data.rows.is_empty() {
println!("⚠️ Sheet is empty");
return Ok(());
}
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_BOX_CHARS);
let header_cells: Vec<Cell> = data
.headers
.iter()
.map(|h| {
let formatted = format_cell_value(h, max_width, wrap);
Cell::new(&formatted).style_spec("Fgbc")
})
.collect();
table.set_titles(Row::new(header_cells));
let rows_to_show = if max_rows == 0 {
data.rows.len()
} else {
std::cmp::min(max_rows, data.rows.len())
};
for (row_idx, row) in data.rows.iter().enumerate().take(rows_to_show) {
let cells: Vec<Cell> = row
.iter()
.enumerate()
.map(|(col_idx, cell)| {
let value = if show_formulas {
data.formulas
.get(row_idx)
.and_then(|formula_row| formula_row.get(col_idx))
.and_then(|f| f.as_ref())
.cloned()
.unwrap_or_else(|| cell.to_string())
} else {
cell.to_string()
};
let formatted = format_cell_value(&value, max_width, wrap);
let cell_obj = Cell::new(&formatted);
if show_formulas {
cell_obj.style_spec("Fg") } else {
match cell {
CellValue::Int(_) | CellValue::Float(_) => {
cell_obj.style_spec("Fr") }
CellValue::Bool(_) => {
cell_obj.style_spec("Fc") }
CellValue::Error(_) => {
cell_obj.style_spec("Frc") }
_ => cell_obj,
}
}
})
.collect();
table.add_row(Row::new(cells));
}
table.printstd();
println!();
if rows_to_show < data.rows.len() {
println!(
"⚠️ Showing {} of {} rows (use -n 0 to show all)",
rows_to_show,
data.rows.len()
);
} else {
println!("Total: {} rows × {} columns", data.height, data.width);
}
println!();
Ok(())
}
pub fn export_csv(data: &SheetData) -> Result<()> {
println!("{}", data.headers.join(","));
for row in &data.rows {
let row_str: Vec<String> = row
.iter()
.map(|cell| {
let val = cell.to_string();
if val.contains(',') || val.contains('"') {
format!("\"{}\"", val.replace('"', "\"\""))
} else {
val
}
})
.collect();
println!("{}", row_str.join(","));
}
Ok(())
}
pub fn export_json(data: &SheetData, sheet_name: &str) -> Result<()> {
println!("{{");
println!(" \"sheet\": \"{sheet_name}\",");
println!(" \"rows\": {},", data.height);
println!(" \"columns\": {},", data.width);
println!(" \"headers\": [");
for (i, header) in data.headers.iter().enumerate() {
let comma = if i < data.headers.len() - 1 { "," } else { "" };
println!(" \"{header}\"{comma}");
}
println!(" ],");
println!(" \"data\": [");
for (i, row) in data.rows.iter().enumerate() {
print!(" [");
for (j, cell) in row.iter().enumerate() {
let value = match cell {
CellValue::String(s) => format!("\"{}\"", s.replace('"', "\\\"")),
CellValue::Int(i) => i.to_string(),
CellValue::Float(f) => f.to_string(),
CellValue::Bool(b) => b.to_string(),
CellValue::Empty => "null".to_string(),
_ => format!("\"{cell}\""),
};
print!("{value}");
if j < row.len() - 1 {
print!(", ");
}
}
let comma = if i < data.rows.len() - 1 { "," } else { "" };
println!("]{comma}");
}
println!(" ]");
println!("}}");
Ok(())
}
pub fn export_text(data: &SheetData) -> Result<()> {
println!("{}", data.headers.join("\t"));
for row in &data.rows {
let row_str: Vec<String> = row.iter().map(|cell| cell.to_string()).collect();
println!("{}", row_str.join("\t"));
}
Ok(())
}