refactor debut

refactor
Nicolas Sanchez 4 weeks ago
parent ed32ae77e0
commit 78d955afcb

112
Cargo.lock generated

@ -26,6 +26,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"const-random",
"getrandom 0.3.3",
"once_cell",
"version_check",
@ -136,6 +137,12 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -269,6 +276,26 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "const-random"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
dependencies = [
"const-random-macro",
]
[[package]]
name = "const-random-macro"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
dependencies = [
"getrandom 0.2.16",
"once_cell",
"tiny-keccak",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
@ -299,6 +326,12 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -515,6 +548,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
@ -594,6 +636,9 @@ name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
dependencies = [
"portable-atomic",
]
[[package]]
name = "once_cell_polyfill"
@ -645,6 +690,12 @@ dependencies = [
"sha2",
]
[[package]]
name = "portable-atomic"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd"
[[package]]
name = "proc-macro2"
version = "1.0.101"
@ -708,6 +759,34 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
[[package]]
name = "rhai"
version = "1.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4e35aaaa439a5bda2f8d15251bc375e4edfac75f9865734644782c9701b5709"
dependencies = [
"ahash",
"bitflags",
"instant",
"num-traits",
"once_cell",
"rhai_codegen",
"smallvec",
"smartstring",
"thin-vec",
]
[[package]]
name = "rhai_codegen"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4322a2a4e8cf30771dd9f27f7f37ca9ac8fe812dddd811096a98483080dabe6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "rustversion"
version = "1.0.22"
@ -786,6 +865,29 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "smartstring"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
dependencies = [
"autocfg",
"static_assertions",
"version_check",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.11.1"
@ -861,6 +963,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]]
name = "typenum"
version = "1.19.0"
@ -1157,6 +1268,7 @@ name = "xlsxtocsv"
version = "0.1.0"
dependencies = [
"clap",
"rhai",
"umya-spreadsheet",
]

@ -5,4 +5,5 @@ edition = "2024"
[dependencies]
clap = { version = "4.5.48", features = ["derive"] }
rhai = "1.23.6"
umya-spreadsheet = "2.3.3"

251
f

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
edition = "2024"

@ -1,20 +1,21 @@
use clap::{Parser, ValueEnum};
use std::fmt;
#[derive(Clone, Debug, ValueEnum)]
pub enum FillMergedCells {
None,
Horizontal,
Vertical,
Both
Both,
}
impl ToString for FillMergedCells {
fn to_string(&self) -> String {
impl fmt::Display for FillMergedCells {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FillMergedCells::None => "none".into(),
FillMergedCells::Horizontal => "horizontal".into(),
FillMergedCells::Vertical => "vertical".into(),
FillMergedCells::Both => "both".into(),
FillMergedCells::None => write!(f, "none"),
FillMergedCells::Horizontal => write!(f, "horizontal"),
FillMergedCells::Vertical => write!(f, "vertical"),
FillMergedCells::Both => write!(f, "both"),
}
}
}
@ -27,34 +28,36 @@ pub enum IncludeHidden {
Both,
}
impl ToString for IncludeHidden {
fn to_string(&self) -> String {
impl fmt::Display for IncludeHidden {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IncludeHidden::None => "none".into(),
IncludeHidden::Rows => "rows".into(),
IncludeHidden::Columns => "columns".into(),
IncludeHidden::Both => "both".into(),
IncludeHidden::None => write!(f, "none"),
IncludeHidden::Rows => write!(f, "rows"),
IncludeHidden::Columns => write!(f, "columns"),
IncludeHidden::Both => write!(f, "both"),
}
}
}
#[derive(Clone, Debug, ValueEnum)]
pub enum TrimSpaces {
End,
Start,
Both,
None
None,
}
impl ToString for TrimSpaces {
fn to_string(&self) -> String {
impl fmt::Display for TrimSpaces {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
TrimSpaces::End => "end".into(),
TrimSpaces::Start => "start".into(),
TrimSpaces::Both => "both".into(),
TrimSpaces::None => "none".into(),
TrimSpaces::End => "end",
TrimSpaces::Start => "start",
TrimSpaces::Both => "both",
TrimSpaces::None => "none",
}
)
}
}
@ -65,17 +68,20 @@ pub enum NumberRows {
None,
}
impl ToString for NumberRows {
fn to_string(&self) -> String {
impl fmt::Display for NumberRows {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
NumberRows::AsIs => "as-is".into(),
NumberRows::Sequential => "sequential".into(),
NumberRows::None => "none".into(),
NumberRows::AsIs => "as-is",
NumberRows::Sequential => "sequential",
NumberRows::None => "none",
}
)
}
}
#[derive(Parser, Debug)]
pub struct Arguments {
/// Path to the xlsx file
@ -106,7 +112,7 @@ pub struct Arguments {
#[arg(short, long, default_value_t = NumberRows::None)]
pub number_rows: NumberRows,
/// avoid nth first rows of xlsx file
#[arg(short = 'k', long, default_value_t= 0)]
#[arg(short = 'k', long, default_value_t = 0)]
pub skip_rows: u32,
/// change end of line character
#[arg(short, long, default_value_t = String::from("\n"))]
@ -114,4 +120,7 @@ pub struct Arguments {
/// replacement for end of line character inside cells
#[arg(short = 'R', long)]
pub replace_end_of_line_by: Option<String>,
/// a rhai function to filter lines
#[arg(long)]
pub filter: Option<String>,
}

@ -1,18 +1,22 @@
use std::fmt;
#[derive(Debug, Clone)]
pub struct Error {
pub msg: String
pub enum Error {
Msg(String),
XlsxError(String),
}
impl Error {
pub fn new(msg: &str) -> Self {
Error { msg: String::from(msg)}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Msg(msg) => write!(f, "{msg}"),
Error::XlsxError(msg) => write!(f, "XlsxError: {msg}"),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error: {}", self.msg)
impl From<umya_spreadsheet::XlsxError> for Error {
fn from(value: umya_spreadsheet::XlsxError) -> Self {
Error::XlsxError(value.to_string())
}
}

@ -0,0 +1,2 @@
pub mod error;
pub mod xlsxtocsv;

@ -1,6 +1,4 @@
pub mod arguments;
pub mod error;
pub mod xlsxtocsv;
use arguments::Arguments;
use clap::Parser;

@ -2,20 +2,19 @@ use std::io::{BufWriter, Write, stdout};
use std::path::Path;
use umya_spreadsheet::{Cell, Range, Worksheet, reader};
//use rhai::
use crate::arguments::{Arguments, IncludeHidden, NumberRows, TrimSpaces};
use crate::error::Error;
pub fn xlsxtocsv(args: &Arguments) -> Result<(), Error> {
let book = reader::xlsx::read(Path::new(&args.file))
.expect(format!("Can't open {}", args.file).as_str());
let book = reader::xlsx::read(Path::new(&args.file))?;
if args.list_worksheets {
println!("List of worksheets :");
let mut i = 0;
let sheets = book.get_sheet_collection();
for sheet in sheets {
for (i, sheet) in sheets.iter().enumerate() {
println!(" {:3}: {}", i, sheet.get_name());
i += 1;
}
return Ok(());
@ -28,7 +27,6 @@ pub fn xlsxtocsv(args: &Arguments) -> Result<(), Error> {
IncludeHidden::Both => (true, true),
};
// get the sheet from name or number if specified, else the first of the spreadsheet
let sheet = match book.get_sheet_by_name(&args.worksheet) {
Some(sheet) => sheet,
None => {
@ -36,11 +34,10 @@ pub fn xlsxtocsv(args: &Arguments) -> Result<(), Error> {
Ok(sheetnum) => sheetnum,
Err(_) => return Err(Error::new("cannot open sheet")),
};
let sheet = match book.get_sheet(&(sheetnum as usize)) {
match book.get_sheet(&(sheetnum as usize)) {
Some(sheet) => sheet,
None => return Err(Error::new("cannot open sheet")),
};
sheet
}
}
};
@ -62,13 +59,13 @@ pub fn xlsxtocsv(args: &Arguments) -> Result<(), Error> {
for cell in sheet.get_cell_collection() {
let value = get_value(cell); //.get_formatted_value();
if value == "" {
if value.is_empty() {
continue;
}
let coord = cell.get_coordinate();
let col_num = coord.get_col_num().clone();
let row_num = coord.get_row_num().clone();
let col_num = *coord.get_col_num();
let row_num = *coord.get_row_num();
if col_num > num_cols {
num_cols = col_num;
}
@ -79,17 +76,19 @@ pub fn xlsxtocsv(args: &Arguments) -> Result<(), Error> {
let num_cols = num_cols;
let num_rows = num_rows;
// get hidden columns if needed
/*
get hidden columns if needed
*/
let mut hidden_columns: Vec<u32> = Vec::new();
if !include_hidden_columns {
for i in 1..=num_cols {
if let Some(dim) = sheet.get_column_dimension_by_number(&i) {
if *dim.get_hidden() {
if let Some(dim) = sheet.get_column_dimension_by_number(&i)
&& *dim.get_hidden()
{
hidden_columns.push(i);
}
}
}
}
// create the empty row string
let mut empty_row = String::from("");
@ -122,7 +121,7 @@ pub fn xlsxtocsv(args: &Arguments) -> Result<(), Error> {
seq_row_num += 1;
line += number_row(&args.number_rows, args.separator, seq_row_num, i).as_str();
line += empty_row.as_str();
writer.write(line.as_bytes()).unwrap();
writer.write_all(line.as_bytes()).unwrap();
continue;
}
}
@ -175,8 +174,7 @@ pub fn xlsxtocsv(args: &Arguments) -> Result<(), Error> {
value = value.replace('\r', "").replace('\n', " ");
if let Some(ref replacement) = args.replace_separator_by {
value = value.replace(args.separator, replacement);
} else {
if value.contains(args.separator) {
} else if value.contains(args.separator) {
return Err(Error::new(
format!(
"Cell {} contains separator char, use -r to choose a replacement char",
@ -185,11 +183,9 @@ pub fn xlsxtocsv(args: &Arguments) -> Result<(), Error> {
.as_str(),
));
}
}
if let Some(ref replacement) = args.replace_end_of_line_by {
value = value.replace(&args.end_of_line, replacement);
} else {
if value.contains(&args.end_of_line) {
} else if value.contains(&args.end_of_line) {
return Err(Error::new(
format!(
"Cell {} contains end of line string, use -R to choose a replacement string",
@ -198,11 +194,10 @@ pub fn xlsxtocsv(args: &Arguments) -> Result<(), Error> {
.as_str(),
));
}
}
line += value.as_str();
}
line += args.end_of_line.as_str();
writer.write(line.as_bytes()).unwrap();
writer.write_all(line.as_bytes()).unwrap();
}
Ok(())
@ -257,13 +252,11 @@ impl MergedCells {
&& row >= *range.get_coordinate_start_row().unwrap().get_num()
&& row <= *range.get_coordinate_end_row().unwrap().get_num()
{
let col_start = range.get_coordinate_start_col().unwrap().get_num().clone();
let row_start = range.get_coordinate_start_row().unwrap().get_num().clone();
let col_start = *range.get_coordinate_start_col().unwrap().get_num();
let row_start = *range.get_coordinate_start_row().unwrap().get_num();
if self.fill_horizontal && self.fill_vertical
|| self.fill_horizontal && row == row_start
|| self.fill_vertical && col == col_start
|| col == col_start && row == row_start
if (self.fill_horizontal || col == col_start)
&& (self.fill_vertical || row == row_start)
{
return Some((col_start, row_start));
}

@ -0,0 +1,373 @@
use std::io::{BufWriter, Write, stdout};
use std::path::Path;
use umya_spreadsheet::{Cell, Range, Spreadsheet, Worksheet, reader};
use crate::arguments::{Arguments, FillMergedCells, IncludeHidden, NumberRows, TrimSpaces};
use crate::error::Error;
pub fn xlsxtocsv(args: &Arguments) -> Result<(), Error> {
let book = reader::xlsx::read(Path::new(&args.file))
.unwrap_or_else(|_| panic!("Can't open {}", args.file));
if args.list_worksheets {
list_worksheets(&book);
return Ok(());
}
let sheet = get_sheet(&book, &args.worksheet)?;
let config = ProcessingConfig::from_args(args);
process_sheet(sheet, &config, args)?;
Ok(())
}
// === Configuration ===
struct ProcessingConfig {
include_hidden_columns: bool,
include_hidden_rows: bool,
fill_horizontal: bool,
fill_vertical: bool,
}
impl ProcessingConfig {
fn from_args(args: &Arguments) -> Self {
let (include_hidden_columns, include_hidden_rows) = match args.include_hidden {
IncludeHidden::None => (false, false),
IncludeHidden::Rows => (false, true),
IncludeHidden::Columns => (true, false),
IncludeHidden::Both => (true, true),
};
let (fill_horizontal, fill_vertical) = match args.fill_merged_cells {
FillMergedCells::None => (false, false),
FillMergedCells::Horizontal => (true, false),
FillMergedCells::Vertical => (false, true),
FillMergedCells::Both => (true, true),
};
Self {
include_hidden_columns,
include_hidden_rows,
fill_horizontal,
fill_vertical,
}
}
}
// === Sheet Selection ===
fn list_worksheets(book: &Spreadsheet) {
println!("List of worksheets :");
let sheets = book.get_sheet_collection();
for (i, sheet) in sheets.iter().enumerate() {
println!(" {:3}: {}", i, sheet.get_name());
}
}
fn get_sheet<'a>(book: &'a Spreadsheet, worksheet_name: &str) -> Result<&'a Worksheet, Error> {
if let Some(sheet) = book.get_sheet_by_name(worksheet_name) {
return Ok(sheet);
}
let sheetnum: u32 = worksheet_name
.parse()
.map_err(|_| Error::new("cannot open sheet"))?;
book.get_sheet(&(sheetnum as usize))
.ok_or_else(|| Error::new("cannot open sheet"))
}
// === Dimensions ===
struct SheetDimensions {
num_cols: u32,
num_rows: u32,
}
fn calculate_dimensions(sheet: &Worksheet) -> SheetDimensions {
let mut num_cols = 0;
let mut num_rows = 0;
for cell in sheet.get_cell_collection() {
let value = get_value(cell);
if value.is_empty() {
continue;
}
let coord = cell.get_coordinate();
let col_num = *coord.get_col_num();
let row_num = *coord.get_row_num();
if col_num > num_cols {
num_cols = col_num;
}
if row_num > num_rows {
num_rows = row_num;
}
}
SheetDimensions { num_cols, num_rows }
}
// === Hidden Columns ===
fn get_hidden_columns(sheet: &Worksheet, num_cols: u32, include_hidden: bool) -> Vec<u32> {
if include_hidden {
return Vec::new();
}
(1..=num_cols)
.filter(|&i| {
sheet
.get_column_dimension_by_number(&i)
.map(|dim| *dim.get_hidden())
.unwrap_or(false)
})
.collect()
}
// === Row Processing ===
fn is_row_hidden(sheet: &Worksheet, row_num: u32) -> bool {
sheet
.get_row_dimension(&row_num)
.map(|dim| *dim.get_hidden())
.unwrap_or(false)
}
fn process_sheet(
sheet: &Worksheet,
config: &ProcessingConfig,
args: &Arguments,
) -> Result<(), Error> {
let dims = calculate_dimensions(sheet);
let hidden_columns = get_hidden_columns(sheet, dims.num_cols, config.include_hidden_columns);
let merged_cells = MergedCells::new(sheet, config.fill_horizontal, config.fill_vertical);
if args.skip_rows > dims.num_rows {
return Err(Error::new("Number of rows < number of rows to skip"));
}
let empty_row = create_empty_row(dims.num_cols, hidden_columns.len() as u32, args);
let stdout = stdout();
let mut writer = BufWriter::new(stdout.lock());
let mut seq_row_num = 0;
for row_num in (args.skip_rows + 1)..=dims.num_rows {
// Skip hidden rows if configured
if !config.include_hidden_rows && is_row_hidden(sheet, row_num) {
continue;
}
// Handle empty rows
if sheet.get_row_dimension(&row_num).is_none() {
seq_row_num += 1;
let line = format!(
"{}{}",
number_row(&args.number_rows, args.separator, seq_row_num, row_num),
empty_row
);
writer.write_all(line.as_bytes()).unwrap();
continue;
}
seq_row_num += 1;
let line = process_row(
sheet,
row_num,
seq_row_num,
dims.num_cols,
&hidden_columns,
&merged_cells,
args,
)?;
writer.write_all(line.as_bytes()).unwrap();
}
Ok(())
}
fn process_row(
sheet: &Worksheet,
row_num: u32,
seq_row_num: u32,
num_cols: u32,
hidden_columns: &[u32],
merged_cells: &MergedCells,
args: &Arguments,
) -> Result<String, Error> {
let mut line = number_row(&args.number_rows, args.separator, seq_row_num, row_num);
let mut first = true;
for col_num in 1..=num_cols {
if hidden_columns.contains(&col_num) {
continue;
}
if !first {
line.push(args.separator);
}
first = false;
let cell_value = get_cell_value(sheet, col_num, row_num, merged_cells);
let processed_value = process_cell_value(cell_value, args)?;
line += &processed_value;
}
line += args.end_of_line.as_str();
Ok(line)
}
fn get_cell_value(
sheet: &Worksheet,
col_num: u32,
row_num: u32,
merged_cells: &MergedCells,
) -> String {
let cell = match sheet.get_cell((col_num, row_num)) {
Some(cell) => cell,
None => return String::new(),
};
// Check if cell is in a merged range
if let Some((merge_col, merge_row)) = merged_cells.in_merged_cell(col_num, row_num) {
sheet
.get_cell((merge_col, merge_row))
.map(get_value)
.unwrap_or_default()
} else {
get_value(cell)
}
}
fn process_cell_value(mut value: String, args: &Arguments) -> Result<String, Error> {
// Trim spaces
value = match args.trim {
TrimSpaces::End => value.trim_end().to_string(),
TrimSpaces::Start => value.trim_start().to_string(),
TrimSpaces::Both => value.trim().to_string(),
TrimSpaces::None => value,
};
// Replace line breaks
value = value.replace('\r', "").replace('\n', " ");
// Handle separator replacement
if let Some(ref replacement) = args.replace_separator_by {
value = value.replace(args.separator, replacement);
} else if value.contains(args.separator) {
return Err(Error::new(
"Cell contains separator char, use -r to choose a replacement char",
));
}
// Handle end of line replacement
if let Some(ref replacement) = args.replace_end_of_line_by {
value = value.replace(&args.end_of_line, replacement);
} else if value.contains(&args.end_of_line) {
return Err(Error::new(
"Cell contains end of line string, use -R to choose a replacement string",
));
}
Ok(value)
}
// === Utility Functions ===
fn create_empty_row(num_cols: u32, num_hidden: u32, args: &Arguments) -> String {
let visible_cols = num_cols - num_hidden;
let mut row = String::new();
for _ in 1..visible_cols {
row.push(args.separator);
}
row += args.end_of_line.as_str();
row
}
fn number_row(
number_row: &NumberRows,
separator: char,
seq_row_num: u32,
actual_row: u32,
) -> String {
match number_row {
NumberRows::AsIs => format!("{}{}", actual_row, separator),
NumberRows::Sequential => format!("{}{}", seq_row_num, separator),
NumberRows::None => String::from(""),
}
}
fn get_value(cell: &Cell) -> String {
match cell.get_raw_value() {
umya_spreadsheet::CellRawValue::String(val) => String::from(val.clone()),
umya_spreadsheet::CellRawValue::RichText(text) => (*text.get_text()).to_owned(),
umya_spreadsheet::CellRawValue::Lazy(lazy) => (*lazy.clone()).to_owned(),
umya_spreadsheet::CellRawValue::Numeric(num) => num.to_string(),
umya_spreadsheet::CellRawValue::Bool(bo) => bo.to_string(),
umya_spreadsheet::CellRawValue::Error(_) | umya_spreadsheet::CellRawValue::Empty => {
String::new()
}
}
}
// === Merged Cells ===
struct MergedCells {
merged_cells: Vec<Range>,
fill_horizontal: bool,
fill_vertical: bool,
}
impl MergedCells {
pub fn new(sheet: &Worksheet, fill_horizontal: bool, fill_vertical: bool) -> Self {
let merged_cells = sheet.get_merge_cells().iter().cloned().collect();
Self {
merged_cells,
fill_horizontal,
fill_vertical,
}
}
pub fn in_merged_cell(&self, col: u32, row: u32) -> Option<(u32, u32)> {
for range in &self.merged_cells {
if self.is_in_range(range, col, row) {
let col_start = *range.get_coordinate_start_col()?.get_num();
let row_start = *range.get_coordinate_start_row()?.get_num();
if (self.fill_horizontal || col == col_start)
&& (self.fill_vertical || row == row_start)
{
return Some((col_start, row_start));
}
}
}
None
}
fn is_in_range(&self, range: &Range, col: u32, row: u32) -> bool {
let Some(start_col) = range.get_coordinate_start_col() else {
return false;
};
let Some(end_col) = range.get_coordinate_end_col() else {
return false;
};
let Some(start_row) = range.get_coordinate_start_row() else {
return false;
};
let Some(end_row) = range.get_coordinate_end_row() else {
return false;
};
col >= *start_col.get_num()
&& col <= *end_col.get_num()
&& row >= *start_row.get_num()
&& row <= *end_row.get_num()
}
}
Loading…
Cancel
Save