diff --git a/Cargo.lock b/Cargo.lock index 7199eb7..0235a8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index f86e77d..e0fb8dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/book/mod.rs b/src/book/mod.rs index eccb9ba..249247c 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -132,27 +132,19 @@ impl Book { Ok(()) } - /// 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())) + /// 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> { + let rows = self.get_rows_for_range(&range).unwrap_or_default(); + rows_to_clipboard_content(rows) + } + + /// Get rows for current sheet to export. pub fn get_export_rows(&self) -> Result>> { 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>) -> 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, diff --git a/src/book/test.rs b/src/book/test.rs index 240a288..220229f 100644 --- a/src/book/test.rs +++ b/src/book/test.rs @@ -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("")); + assert!(html.contains("")); + assert!(html.contains("
")); + 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("")); + assert!(html.contains("")); + assert!(html.contains("
")); + 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); +}