use crate::error::XlsxToMdError;
use crate::grid::LogicalGrid;
use crate::types::MergedRegion;
use std::io::Write;
pub struct MarkdownFormatter;
impl MarkdownFormatter {
pub fn render<W: Write>(
&self,
grid: &LogicalGrid,
writer: &mut W,
_merged_regions: &[MergedRegion],
) -> Result<(), XlsxToMdError> {
grid.render_markdown(writer)
}
}
pub struct HtmlFormatter;
impl HtmlFormatter {
pub fn render<W: Write>(
&self,
grid: &LogicalGrid,
writer: &mut W,
merged_regions: &[MergedRegion],
) -> Result<(), XlsxToMdError> {
grid.render_html(writer, merged_regions)
}
}
pub struct JsonFormatter;
impl JsonFormatter {
pub fn render<W: Write>(
&self,
grid: &LogicalGrid,
writer: &mut W,
_merged_regions: &[MergedRegion],
) -> Result<(), XlsxToMdError> {
use serde_json::json;
let rows = grid.get_rows();
let cols = grid.get_cols();
if rows == 0 || cols == 0 {
writeln!(writer, "{{}}")?;
return Ok(());
}
let column_names: Vec<String> = (0..cols)
.map(|col| {
let col_letter = col_to_letter(col as u32);
col_letter
})
.collect();
let json_rows: Vec<serde_json::Value> = (0..rows)
.map(|row_idx| {
let row = grid.get_row(row_idx);
let mut row_obj = serde_json::Map::new();
for (col_idx, cell) in row.iter().enumerate() {
let col_name = &column_names[col_idx];
if !cell.is_merged || cell.merge_parent.is_none() {
row_obj.insert(col_name.clone(), json!(cell.content));
}
}
json!(row_obj)
})
.collect();
let json_output = json!({
"rows": json_rows
});
serde_json::to_writer_pretty(&mut *writer, &json_output).map_err(|e| {
XlsxToMdError::Config(format!("JSON serialization error: {}", e))
})?;
writeln!(writer)?;
writer.flush()?;
Ok(())
}
}
pub struct CsvFormatter;
impl CsvFormatter {
pub fn render<W: Write>(
&self,
grid: &LogicalGrid,
writer: &mut W,
_merged_regions: &[MergedRegion],
) -> Result<(), XlsxToMdError> {
let rows = grid.get_rows();
let cols = grid.get_cols();
if rows == 0 || cols == 0 {
return Ok(());
}
for row_idx in 0..rows {
let row = grid.get_row(row_idx);
let mut first = true;
for (_col_idx, cell) in row.iter().enumerate() {
if cell.is_merged && cell.merge_parent.is_some() {
continue;
}
if !first {
write!(writer, ",")?;
}
first = false;
let escaped = escape_csv(&cell.content);
write!(writer, "{}", escaped)?;
}
writeln!(writer)?;
}
writer.flush()?;
Ok(())
}
}
fn col_to_letter(mut col: u32) -> String {
let mut result = String::new();
loop {
result.push((b'A' + (col % 26) as u8) as char);
if col < 26 {
break;
}
col = col / 26 - 1;
}
result.chars().rev().collect()
}
fn escape_csv(s: &str) -> String {
if s.contains(',') || s.contains('"') || s.contains('\n') || s.contains('\r') {
format!("\"{}\"", s.replace('"', "\"\""))
} else {
s.to_string()
}
}