wip: convert a sheet into clipboard format

This commit is contained in:
Jeremy Wall 2025-04-08 23:49:57 -04:00
parent 6a044f174a
commit fbbdcb983f
4 changed files with 152 additions and 34 deletions

19
Cargo.lock generated
View File

@ -844,19 +844,10 @@ dependencies = [
]
[[package]]
name = "html"
version = "0.6.3"
name = "htmf"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "944d7db81871c611549302f3014418fedbcfbc46902f97e6a1c4f53e785903d2"
dependencies = [
"html-sys",
]
[[package]]
name = "html-sys"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13eca55667a5657dd1b86db77c5fe2d1810e3f9413e9555a2c4c461733dd2573"
checksum = "abcb5a4078c86d49875d3079c1b31c3bd5c277ad6adb88800a4d5c6af0fec9b2"
[[package]]
name = "iana-time-zone"
@ -1029,7 +1020,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
dependencies = [
"cfg-if",
"windows-targets 0.48.5",
"windows-targets 0.52.6",
]
[[package]]
@ -1780,7 +1771,7 @@ dependencies = [
"crossterm",
"csv",
"futures",
"html",
"htmf",
"ironcalc",
"pulldown-cmark 0.13.0",
"ratatui",

View File

@ -22,5 +22,5 @@ colorsys = "0.6.7"
tui-markdown = { version = "0.3.1", features = [] }
csv = "1.3.1"
pulldown-cmark = "0.13.0"
html = "0.6.3"
arboard = { version = "3.5.0", features = ["wayland-data-control"] }
htmf = "0.2.0"

View File

@ -132,27 +132,19 @@ impl Book {
Ok(())
}
/// Construct a payload of (html, csv_text) for a sheet.
pub fn sheeet_to_clipboard_content(&self, sheet: u32) -> Result<(String, String), anyhow::Error> {
let rows = self.get_export_rows_for_sheet(sheet)?;
rows_to_clipboard_content(rows)
}
/// Construct a payload of (html, csv_text) for the address range.
pub fn range_to_clipboard_content(&self, range: AddressRange) -> Result<(String, String), anyhow::Error> {
use html::tables;
let rows = self.get_rows_for_range(&range).unwrap_or_default();
let mut html = tables::Table::builder();
let mut writer = csv::Writer::from_writer(vec![]);
for row in rows {
let mut html_row = tables::TableRow::builder();
writer.write_record(&row)?;
for cell in row {
let mut html_cell = tables::TableCell::builder();
html_cell.text(cell);
html_row.push(html_cell.build());
}
html.push(html_row.build());
}
let csv_content = writer.into_inner().expect("Failed to get the csv content");
Ok((html.build().to_string(), String::from_utf8_lossy(&csv_content).to_string()))
rows_to_clipboard_content(rows)
}
/// Get rows for current sheet to export.
pub fn get_export_rows(&self) -> Result<Vec<Vec<String>>> {
let sheet = self.location.sheet;
Ok(self.get_export_rows_for_sheet(sheet)?)
@ -730,6 +722,25 @@ impl Book {
}
}
fn rows_to_clipboard_content(rows: Vec<Vec<String>>) -> std::result::Result<(String, String), anyhow::Error> {
use htmf::prelude::*;
let table = table([]);
let mut writer = csv::Writer::from_writer(vec![]);
let mut table_rows = vec![];
for row in rows {
let table_row = tr([]);
writer.write_record(&row)?;
let mut row_cells = vec![];
for cell in row {
row_cells.push(td([]).with(text(cell)));
}
table_rows.push(table_row.with(row_cells));
}
let csv_content = writer.into_inner().expect("Failed to get the csv content");
Ok((table.with(table_rows).to_html(), String::from_utf8_lossy(&csv_content).to_string()))
}
fn calculate_area(sheet: u32, start: &Address, end: &Address) -> Area {
let area = Area {
sheet,

View File

@ -200,3 +200,119 @@ fn test_book_get_exportable_rows() {
]
);
}
#[test]
fn test_sheet_to_clipboard_content() {
let mut book = Book::default();
book.update_cell(
&Address {
sheet: 0,
row: 1,
col: 1,
},
"A1",
)
.expect("failed to edit cell");
book.update_cell(
&Address {
sheet: 0,
row: 1,
col: 2,
},
"B1",
)
.expect("failed to edit cell");
book.update_cell(
&Address {
sheet: 0,
row: 2,
col: 1,
},
"A2",
)
.expect("failed to edit cell");
book.update_cell(
&Address {
sheet: 0,
row: 2,
col: 2,
},
"B2",
)
.expect("failed to edit cell");
let (html, csv) = dbg!(book.sheeet_to_clipboard_content(0).expect("Failed to get clipboard content"));
// Check that HTML contains table elements and our data
assert!(html.contains("<table>"));
assert!(html.contains("<tr>"));
assert!(html.contains("<td>"));
assert!(html.contains("A1"));
assert!(html.contains("B1"));
assert!(html.contains("A2"));
assert!(html.contains("B2"));
// Check CSV content
let expected_csv = ",,\n,A1,B1\n,A2,B2\n";
assert_eq!(csv, expected_csv);
}
#[test]
fn test_range_to_clipboard_content() {
let mut book = Book::default();
book.update_cell(
&Address {
sheet: 0,
row: 1,
col: 1,
},
"A1",
)
.expect("failed to edit cell");
book.update_cell(
&Address {
sheet: 0,
row: 1,
col: 2,
},
"B1",
)
.expect("failed to edit cell");
book.update_cell(
&Address {
sheet: 0,
row: 2,
col: 1,
},
"A2",
)
.expect("failed to edit cell");
book.update_cell(
&Address {
sheet: 0,
row: 2,
col: 2,
},
"B2",
)
.expect("failed to edit cell");
let start = Address { sheet: 0, row: 1, col: 1 };
let end = Address { sheet: 0, row: 2, col: 2 };
let range = super::AddressRange { start: &start, end: &end };
let (html, csv) = book.range_to_clipboard_content(range).expect("Failed to get clipboard content");
// Check that HTML contains table elements and our data
assert!(html.contains("<table>"));
assert!(html.contains("<tr>"));
assert!(html.contains("<td>"));
assert!(html.contains("A1"));
assert!(html.contains("B1"));
assert!(html.contains("A2"));
assert!(html.contains("B2"));
// Check CSV content
let expected_csv = "A1,B1\nA2,B2\n";
assert_eq!(csv, expected_csv);
}