#[derive(Debug, Default, Clone, PartialEq)]
pub struct Workbook {
pub sheets: Vec<Sheet>,
pub defined_names: Vec<DefinedName>,
pub properties: DocumentProperties,
pub protection: Option<WorkbookProtection>,
pub write_protection: Option<WriteProtection>,
pub active_tab: Option<usize>,
pub external_links: Vec<ExternalWorkbookLink>,
pub vba_project: Option<Vec<u8>>,
pub calculation: Option<CalculationProperties>,
pub table_styles: Vec<TableStyle>,
pub named_cell_styles: Vec<NamedCellStyle>,
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Sheet {
pub name: String,
pub rows: Vec<Row>,
pub conditional_formatting_rules: Vec<ConditionalFormattingRule>,
pub data_validation_rules: Vec<DataValidationRule>,
pub merged_ranges: Vec<String>,
pub column_settings: Vec<ColumnSetting>,
pub auto_filter_range: Option<String>,
pub freeze_panes: Option<FreezePane>,
pub print_settings: PrintSettings,
pub protection: Option<SheetProtection>,
pub hyperlinks: Vec<CellHyperlink>,
pub comments: Vec<CellComment>,
pub table: Option<ExcelTable>,
pub visibility: SheetVisibility,
pub zoom_scale: Option<u32>,
pub tab_color: Option<String>,
pub show_grid_lines: Option<bool>,
pub show_row_col_headers: Option<bool>,
pub show_zeros: Option<bool>,
pub right_to_left: Option<bool>,
pub active_cell: Option<String>,
pub default_column_width: Option<f64>,
pub default_row_height: Option<f64>,
pub row_breaks: Vec<u32>,
pub column_breaks: Vec<u32>,
pub scenarios: Vec<Scenario>,
pub auto_filter_columns: Vec<AutoFilterColumn>,
pub ignored_errors: Vec<IgnoredError>,
pub protected_ranges: Vec<ProtectedRange>,
pub sort_state: Option<SortState>,
pub outline_summary_below: Option<bool>,
pub outline_summary_right: Option<bool>,
pub drawing: Option<SheetDrawing>,
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Row {
pub cells: Vec<Cell>,
pub height: Option<f64>,
pub hidden: bool,
pub outline_level: u8,
pub collapsed: bool,
pub default_format: Option<CellFormat>,
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Cell {
pub value: CellValue,
pub format: Option<CellFormat>,
}
#[derive(Debug, Default, Clone, PartialEq)]
pub enum CellValue {
#[default]
Empty,
Text(String),
Number(f64),
Formula {
formula: Option<String>,
cached_value: Option<f64>,
},
Boolean(bool),
Date(ExcelDateTime),
RichText(Vec<RichTextRun>),
ArrayFormula {
formula: String,
range: String,
cached_value: Option<f64>,
},
SharedFormula {
formula: Option<String>,
group_index: u32,
range: Option<String>,
cached_value: Option<f64>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExcelDateTime {
pub year: i32,
pub month: u32,
pub day: u32,
pub hour: u32,
pub minute: u32,
pub second: u32,
}
impl ExcelDateTime {
pub fn date(year: i32, month: u32, day: u32) -> Self {
Self {
year,
month,
day,
hour: 0,
minute: 0,
second: 0,
}
}
pub fn with_time(mut self, hour: u32, minute: u32, second: u32) -> Self {
self.hour = hour;
self.minute = minute;
self.second = second;
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct RichTextRun {
pub text: String,
pub bold: bool,
pub italic: bool,
pub font_color: Option<String>,
pub font_size: Option<f64>,
pub font_name: Option<String>,
pub underline: bool,
pub strike: bool,
}
impl RichTextRun {
pub fn new(text: impl Into<String>) -> Self {
Self {
text: text.into(),
bold: false,
italic: false,
font_color: None,
font_size: None,
font_name: None,
underline: false,
strike: false,
}
}
pub fn with_bold(mut self, bold: bool) -> Self {
self.bold = bold;
self
}
pub fn with_italic(mut self, italic: bool) -> Self {
self.italic = italic;
self
}
pub fn with_font_color(mut self, color: impl Into<String>) -> Self {
self.font_color = Some(color.into());
self
}
pub fn with_font_size(mut self, size: f64) -> Self {
self.font_size = Some(size);
self
}
pub fn with_font_name(mut self, font_name: impl Into<String>) -> Self {
self.font_name = Some(font_name.into());
self
}
pub fn with_underline(mut self, underline: bool) -> Self {
self.underline = underline;
self
}
pub fn with_strike(mut self, strike: bool) -> Self {
self.strike = strike;
self
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct CellFormat {
pub number_format: Option<String>,
pub bold: bool,
pub italic: bool,
pub font_size: Option<f64>,
pub font_color: Option<String>,
pub fill_color: Option<String>,
pub horizontal_alignment: Option<HorizontalAlignment>,
pub vertical_alignment: Option<VerticalAlignment>,
pub border: Border,
pub font_name: Option<String>,
pub underline: bool,
pub strike: bool,
pub wrap_text: bool,
pub indent: Option<u32>,
pub text_rotation: Option<u16>,
pub shrink_to_fit: bool,
pub pattern_fill: Option<PatternFill>,
pub gradient_fill: Option<GradientFill>,
pub locked: Option<bool>,
pub formula_hidden: Option<bool>,
pub named_style: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HorizontalAlignment {
Left,
Center,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VerticalAlignment {
Top,
Center,
Bottom,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BorderStyle {
Thin,
Medium,
Thick,
Dashed,
Dotted,
Double,
Hair,
}
#[derive(Debug, Clone, PartialEq)]
pub struct BorderEdge {
pub style: BorderStyle,
pub color: Option<String>,
}
impl BorderEdge {
pub fn new(style: BorderStyle) -> Self {
Self { style, color: None }
}
pub fn with_color(mut self, color: impl Into<String>) -> Self {
self.color = Some(color.into());
self
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Border {
pub top: Option<BorderEdge>,
pub bottom: Option<BorderEdge>,
pub left: Option<BorderEdge>,
pub right: Option<BorderEdge>,
pub diagonal: Option<BorderEdge>,
pub diagonal_up: bool,
pub diagonal_down: bool,
}
impl Border {
pub fn new() -> Self {
Self::default()
}
pub fn with_top(mut self, edge: BorderEdge) -> Self {
self.top = Some(edge);
self
}
pub fn with_bottom(mut self, edge: BorderEdge) -> Self {
self.bottom = Some(edge);
self
}
pub fn with_left(mut self, edge: BorderEdge) -> Self {
self.left = Some(edge);
self
}
pub fn with_right(mut self, edge: BorderEdge) -> Self {
self.right = Some(edge);
self
}
pub fn with_diagonal(mut self, edge: BorderEdge, up: bool, down: bool) -> Self {
self.diagonal = Some(edge);
self.diagonal_up = up;
self.diagonal_down = down;
self
}
pub fn all(style: BorderStyle, color: impl Into<String>) -> Self {
let color = color.into();
Self {
top: Some(BorderEdge::new(style).with_color(color.clone())),
bottom: Some(BorderEdge::new(style).with_color(color.clone())),
left: Some(BorderEdge::new(style).with_color(color.clone())),
right: Some(BorderEdge::new(style).with_color(color)),
diagonal: None,
diagonal_up: false,
diagonal_down: false,
}
}
}
impl Workbook {
pub fn new() -> Self {
Self::default()
}
pub fn with_sheet(mut self, sheet: Sheet) -> Self {
self.sheets.push(sheet);
self
}
pub fn add_sheet(&mut self, name: impl Into<String>) -> &mut Sheet {
self.sheets.push(Sheet::new(name));
self.sheets.last_mut().expect("a sheet was just pushed")
}
pub fn with_defined_name(mut self, defined_name: DefinedName) -> Self {
self.defined_names.push(defined_name);
self
}
pub fn with_calculation(mut self, calculation: CalculationProperties) -> Self {
self.calculation = Some(calculation);
self
}
pub fn with_table_style(mut self, table_style: TableStyle) -> Self {
self.table_styles.push(table_style);
self
}
pub fn with_write_protection(mut self, write_protection: WriteProtection) -> Self {
self.write_protection = Some(write_protection);
self
}
pub fn with_named_cell_style(mut self, named_cell_style: NamedCellStyle) -> Self {
self.named_cell_styles.push(named_cell_style);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CalculationMode {
Automatic,
AutomaticExceptTables,
Manual,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CalculationProperties {
pub mode: Option<CalculationMode>,
pub iterate: bool,
}
impl CalculationProperties {
pub fn new() -> Self {
Self {
mode: None,
iterate: false,
}
}
pub fn with_mode(mut self, mode: CalculationMode) -> Self {
self.mode = Some(mode);
self
}
pub fn with_iterate(mut self, iterate: bool) -> Self {
self.iterate = iterate;
self
}
}
impl Default for CalculationProperties {
fn default() -> Self {
Self::new()
}
}
impl Sheet {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
rows: Vec::new(),
conditional_formatting_rules: Vec::new(),
data_validation_rules: Vec::new(),
merged_ranges: Vec::new(),
column_settings: Vec::new(),
auto_filter_range: None,
freeze_panes: None,
print_settings: PrintSettings::default(),
protection: None,
hyperlinks: Vec::new(),
comments: Vec::new(),
table: None,
visibility: SheetVisibility::Visible,
zoom_scale: None,
row_breaks: Vec::new(),
column_breaks: Vec::new(),
scenarios: Vec::new(),
auto_filter_columns: Vec::new(),
tab_color: None,
show_grid_lines: None,
show_row_col_headers: None,
show_zeros: None,
right_to_left: None,
active_cell: None,
default_column_width: None,
default_row_height: None,
ignored_errors: Vec::new(),
protected_ranges: Vec::new(),
sort_state: None,
outline_summary_below: None,
outline_summary_right: None,
drawing: None,
}
}
pub fn with_row(mut self, row: Row) -> Self {
self.rows.push(row);
self
}
pub fn write_cell(&mut self, row: u32, col: u32, cell: impl Into<Cell>) -> &mut Cell {
assert!(
row >= 1 && col >= 1,
"write_cell uses 1-based row/col indices, got ({row}, {col})"
);
let cell = cell.into();
let row_index = (row - 1) as usize;
let col_index = (col - 1) as usize;
if self.rows.len() <= row_index {
self.rows.resize_with(row_index + 1, Row::default);
}
let target_row = &mut self.rows[row_index];
if target_row.cells.len() <= col_index {
target_row.cells.resize_with(col_index + 1, Cell::default);
}
target_row.cells[col_index] = cell;
&mut target_row.cells[col_index]
}
pub fn with_conditional_formatting_rule(mut self, rule: ConditionalFormattingRule) -> Self {
self.conditional_formatting_rules.push(rule);
self
}
pub fn with_data_validation_rule(mut self, rule: DataValidationRule) -> Self {
self.data_validation_rules.push(rule);
self
}
pub fn with_merged_range(mut self, range: impl Into<String>) -> Self {
self.merged_ranges.push(range.into());
self
}
pub fn with_column_setting(mut self, setting: ColumnSetting) -> Self {
self.column_settings.push(setting);
self
}
pub fn with_auto_filter_range(mut self, range: impl Into<String>) -> Self {
self.auto_filter_range = Some(range.into());
self
}
pub fn with_freeze_panes(mut self, freeze_panes: FreezePane) -> Self {
self.freeze_panes = Some(freeze_panes);
self
}
pub fn with_print_settings(mut self, print_settings: PrintSettings) -> Self {
self.print_settings = print_settings;
self
}
pub fn with_protection(mut self, protection: SheetProtection) -> Self {
self.protection = Some(protection);
self
}
pub fn with_hyperlink(mut self, hyperlink: CellHyperlink) -> Self {
self.hyperlinks.push(hyperlink);
self
}
pub fn with_comment(mut self, comment: CellComment) -> Self {
self.comments.push(comment);
self
}
pub fn with_table(mut self, table: ExcelTable) -> Self {
self.table = Some(table);
self
}
pub fn with_drawing(mut self, drawing: SheetDrawing) -> Self {
self.drawing = Some(drawing);
self
}
pub fn with_visibility(mut self, visibility: SheetVisibility) -> Self {
self.visibility = visibility;
self
}
pub fn with_zoom_scale(mut self, zoom_scale: u32) -> Self {
self.zoom_scale = Some(zoom_scale);
self
}
pub fn with_tab_color(mut self, color: impl Into<String>) -> Self {
self.tab_color = Some(color.into());
self
}
pub fn with_show_grid_lines(mut self, show: bool) -> Self {
self.show_grid_lines = Some(show);
self
}
pub fn with_show_row_col_headers(mut self, show: bool) -> Self {
self.show_row_col_headers = Some(show);
self
}
pub fn with_show_zeros(mut self, show: bool) -> Self {
self.show_zeros = Some(show);
self
}
pub fn with_right_to_left(mut self, right_to_left: bool) -> Self {
self.right_to_left = Some(right_to_left);
self
}
pub fn with_active_cell(mut self, cell: impl Into<String>) -> Self {
self.active_cell = Some(cell.into());
self
}
pub fn with_default_column_width(mut self, width: f64) -> Self {
self.default_column_width = Some(width);
self
}
pub fn with_default_row_height(mut self, height: f64) -> Self {
self.default_row_height = Some(height);
self
}
pub fn with_row_break(mut self, row: u32) -> Self {
self.row_breaks.push(row);
self
}
pub fn with_column_break(mut self, column: u32) -> Self {
self.column_breaks.push(column);
self
}
pub fn with_scenario(mut self, scenario: Scenario) -> Self {
self.scenarios.push(scenario);
self
}
pub fn with_auto_filter_column(mut self, column: AutoFilterColumn) -> Self {
self.auto_filter_columns.push(column);
self
}
pub fn with_ignored_error(mut self, ignored_error: IgnoredError) -> Self {
self.ignored_errors.push(ignored_error);
self
}
pub fn with_protected_range(mut self, protected_range: ProtectedRange) -> Self {
self.protected_ranges.push(protected_range);
self
}
pub fn with_sort_state(mut self, sort_state: SortState) -> Self {
self.sort_state = Some(sort_state);
self
}
pub fn with_outline_summary_below(mut self, summary_below: bool) -> Self {
self.outline_summary_below = Some(summary_below);
self
}
pub fn with_outline_summary_right(mut self, summary_right: bool) -> Self {
self.outline_summary_right = Some(summary_right);
self
}
}
impl Row {
pub fn new() -> Self {
Self::default()
}
pub fn with_cell(mut self, cell: Cell) -> Self {
self.cells.push(cell);
self
}
pub fn with_text(self, text: impl Into<String>) -> Self {
self.with_cell(Cell::text(text))
}
pub fn with_number(self, number: f64) -> Self {
self.with_cell(Cell::number(number))
}
pub fn with_formula(self, formula: impl Into<String>) -> Self {
self.with_cell(Cell::formula(formula))
}
pub fn with_height(mut self, height: f64) -> Self {
self.height = Some(height);
self
}
pub fn with_hidden(mut self, hidden: bool) -> Self {
self.hidden = hidden;
self
}
pub fn with_outline_level(mut self, level: u8) -> Self {
self.outline_level = level;
self
}
pub fn with_collapsed(mut self, collapsed: bool) -> Self {
self.collapsed = collapsed;
self
}
pub fn with_style(mut self, style: CellFormat) -> Self {
self.default_format = Some(style);
self
}
}
impl Cell {
pub fn new() -> Self {
Self::default()
}
pub fn text(text: impl Into<String>) -> Self {
Self {
value: CellValue::Text(text.into()),
format: None,
}
}
pub fn number(number: f64) -> Self {
Self {
value: CellValue::Number(number),
format: None,
}
}
pub fn formula(formula: impl Into<String>) -> Self {
Self {
value: CellValue::Formula {
formula: Some(formula.into()),
cached_value: None,
},
format: None,
}
}
pub fn boolean(value: bool) -> Self {
Self {
value: CellValue::Boolean(value),
format: None,
}
}
pub fn date(date: ExcelDateTime) -> Self {
Self {
value: CellValue::Date(date),
format: None,
}
}
pub fn rich_text(runs: Vec<RichTextRun>) -> Self {
Self {
value: CellValue::RichText(runs),
format: None,
}
}
pub fn array_formula(formula: impl Into<String>, range: impl Into<String>) -> Self {
Self {
value: CellValue::ArrayFormula {
formula: formula.into(),
range: range.into(),
cached_value: None,
},
format: None,
}
}
pub fn shared_formula(
formula: impl Into<String>,
group_index: u32,
range: impl Into<String>,
) -> Self {
Self {
value: CellValue::SharedFormula {
formula: Some(formula.into()),
group_index,
range: Some(range.into()),
cached_value: None,
},
format: None,
}
}
pub fn shared_formula_follower(group_index: u32) -> Self {
Self {
value: CellValue::SharedFormula {
formula: None,
group_index,
range: None,
cached_value: None,
},
format: None,
}
}
pub fn with_cached_value(mut self, value: f64) -> Self {
match &mut self.value {
CellValue::Formula { cached_value, .. }
| CellValue::ArrayFormula { cached_value, .. }
| CellValue::SharedFormula { cached_value, .. } => {
*cached_value = Some(value);
}
_ => {}
}
self
}
pub fn with_format(mut self, format: CellFormat) -> Self {
self.format = Some(format);
self
}
}
impl From<&str> for Cell {
fn from(text: &str) -> Self {
Cell::text(text)
}
}
impl From<String> for Cell {
fn from(text: String) -> Self {
Cell::text(text)
}
}
impl From<f64> for Cell {
fn from(number: f64) -> Self {
Cell::number(number)
}
}
impl From<bool> for Cell {
fn from(value: bool) -> Self {
Cell::boolean(value)
}
}
impl CellFormat {
pub fn new() -> Self {
Self::default()
}
pub fn with_number_format(mut self, format: impl Into<String>) -> Self {
self.number_format = Some(format.into());
self
}
pub fn with_bold(mut self, bold: bool) -> Self {
self.bold = bold;
self
}
pub fn with_italic(mut self, italic: bool) -> Self {
self.italic = italic;
self
}
pub fn with_font_size(mut self, size: f64) -> Self {
self.font_size = Some(size);
self
}
pub fn with_font_color(mut self, color: impl Into<String>) -> Self {
self.font_color = Some(color.into());
self
}
pub fn with_fill_color(mut self, color: impl Into<String>) -> Self {
self.fill_color = Some(color.into());
self
}
pub fn with_horizontal_alignment(mut self, alignment: HorizontalAlignment) -> Self {
self.horizontal_alignment = Some(alignment);
self
}
pub fn with_vertical_alignment(mut self, alignment: VerticalAlignment) -> Self {
self.vertical_alignment = Some(alignment);
self
}
pub fn with_border(mut self, border: Border) -> Self {
self.border = border;
self
}
pub fn with_font_name(mut self, font_name: impl Into<String>) -> Self {
self.font_name = Some(font_name.into());
self
}
pub fn with_underline(mut self, underline: bool) -> Self {
self.underline = underline;
self
}
pub fn with_strike(mut self, strike: bool) -> Self {
self.strike = strike;
self
}
pub fn with_wrap_text(mut self, wrap_text: bool) -> Self {
self.wrap_text = wrap_text;
self
}
pub fn with_indent(mut self, indent: u32) -> Self {
self.indent = Some(indent);
self
}
pub fn with_text_rotation(mut self, text_rotation: u16) -> Self {
self.text_rotation = Some(text_rotation);
self
}
pub fn with_shrink_to_fit(mut self, shrink_to_fit: bool) -> Self {
self.shrink_to_fit = shrink_to_fit;
self
}
pub fn with_pattern_fill(mut self, pattern_fill: PatternFill) -> Self {
self.pattern_fill = Some(pattern_fill);
self
}
pub fn with_gradient_fill(mut self, gradient_fill: GradientFill) -> Self {
self.gradient_fill = Some(gradient_fill);
self
}
pub fn with_locked(mut self, locked: bool) -> Self {
self.locked = Some(locked);
self
}
pub fn with_formula_hidden(mut self, formula_hidden: bool) -> Self {
self.formula_hidden = Some(formula_hidden);
self
}
pub fn with_named_style(mut self, name: impl Into<String>) -> Self {
self.named_style = Some(name.into());
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PatternType {
Gray125,
Gray0625,
DarkGray,
MediumGray,
LightGray,
DarkHorizontal,
DarkVertical,
DarkDown,
DarkUp,
DarkGrid,
DarkTrellis,
LightHorizontal,
LightVertical,
LightDown,
LightUp,
LightGrid,
LightTrellis,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PatternFill {
pub pattern_type: PatternType,
pub fg_color: Option<String>,
pub bg_color: Option<String>,
}
impl PatternFill {
pub fn new(pattern_type: PatternType) -> Self {
Self {
pattern_type,
fg_color: None,
bg_color: None,
}
}
pub fn with_fg_color(mut self, color: impl Into<String>) -> Self {
self.fg_color = Some(color.into());
self
}
pub fn with_bg_color(mut self, color: impl Into<String>) -> Self {
self.bg_color = Some(color.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GradientStop {
pub position: f64,
pub color: String,
}
impl GradientStop {
pub fn new(position: f64, color: impl Into<String>) -> Self {
Self {
position,
color: color.into(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GradientFill {
pub angle: f64,
pub stops: Vec<GradientStop>,
}
impl GradientFill {
pub fn new(angle: f64, stops: Vec<GradientStop>) -> Self {
Self { angle, stops }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComparisonOperator {
LessThan,
LessThanOrEqual,
Equal,
NotEqual,
GreaterThanOrEqual,
GreaterThan,
Between,
NotBetween,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ConditionalFormattingRule {
pub range: String,
pub condition: ConditionalFormattingCondition,
pub format: CellFormat,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConditionalFormattingCondition {
CellIs {
operator: ComparisonOperator,
formula1: String,
formula2: Option<String>,
},
Expression {
formula: String,
},
ColorScale(Vec<ColorScaleStop>),
DataBar {
min: CfvoPosition,
max: CfvoPosition,
color: String,
},
IconSet(IconSetType),
Top10 {
rank: u32,
percent: bool,
bottom: bool,
},
AboveAverage {
above: bool,
equal_average: bool,
},
DuplicateValues,
UniqueValues,
ContainsText {
operator: TextComparisonOperator,
text: String,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct ColorScaleStop {
pub position: CfvoPosition,
pub color: String,
}
impl ColorScaleStop {
pub fn new(position: CfvoPosition, color: impl Into<String>) -> Self {
Self {
position,
color: color.into(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CfvoPosition {
Min,
Max,
Percent(f64),
Number(f64),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IconSetType {
ThreeTrafficLights,
ThreeArrows,
ThreeFlags,
ThreeSymbols,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextComparisonOperator {
Contains,
NotContains,
BeginsWith,
EndsWith,
}
impl ConditionalFormattingRule {
pub fn cell_is(
range: impl Into<String>,
operator: ComparisonOperator,
formula1: impl Into<String>,
format: CellFormat,
) -> Self {
Self {
range: range.into(),
condition: ConditionalFormattingCondition::CellIs {
operator,
formula1: formula1.into(),
formula2: None,
},
format,
}
}
pub fn with_second_formula(mut self, formula2: impl Into<String>) -> Self {
if let ConditionalFormattingCondition::CellIs {
formula2: existing, ..
} = &mut self.condition
{
*existing = Some(formula2.into());
}
self
}
pub fn expression(
range: impl Into<String>,
formula: impl Into<String>,
format: CellFormat,
) -> Self {
Self {
range: range.into(),
condition: ConditionalFormattingCondition::Expression {
formula: formula.into(),
},
format,
}
}
pub fn color_scale(range: impl Into<String>, stops: Vec<ColorScaleStop>) -> Self {
Self {
range: range.into(),
condition: ConditionalFormattingCondition::ColorScale(stops),
format: CellFormat::default(),
}
}
pub fn data_bar(
range: impl Into<String>,
min: CfvoPosition,
max: CfvoPosition,
color: impl Into<String>,
) -> Self {
Self {
range: range.into(),
condition: ConditionalFormattingCondition::DataBar {
min,
max,
color: color.into(),
},
format: CellFormat::default(),
}
}
pub fn icon_set(range: impl Into<String>, icon_set: IconSetType) -> Self {
Self {
range: range.into(),
condition: ConditionalFormattingCondition::IconSet(icon_set),
format: CellFormat::default(),
}
}
pub fn top10(range: impl Into<String>, rank: u32, percent: bool, format: CellFormat) -> Self {
Self {
range: range.into(),
condition: ConditionalFormattingCondition::Top10 {
rank,
percent,
bottom: false,
},
format,
}
}
pub fn with_bottom(mut self, bottom: bool) -> Self {
if let ConditionalFormattingCondition::Top10 {
bottom: existing, ..
} = &mut self.condition
{
*existing = bottom;
}
self
}
pub fn above_average(range: impl Into<String>, above: bool, format: CellFormat) -> Self {
Self {
range: range.into(),
condition: ConditionalFormattingCondition::AboveAverage {
above,
equal_average: false,
},
format,
}
}
pub fn duplicate_values(range: impl Into<String>, format: CellFormat) -> Self {
Self {
range: range.into(),
condition: ConditionalFormattingCondition::DuplicateValues,
format,
}
}
pub fn unique_values(range: impl Into<String>, format: CellFormat) -> Self {
Self {
range: range.into(),
condition: ConditionalFormattingCondition::UniqueValues,
format,
}
}
pub fn contains_text(
range: impl Into<String>,
operator: TextComparisonOperator,
text: impl Into<String>,
format: CellFormat,
) -> Self {
Self {
range: range.into(),
condition: ConditionalFormattingCondition::ContainsText {
operator,
text: text.into(),
},
format,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Message {
pub title: Option<String>,
pub text: String,
}
impl Message {
pub fn new(text: impl Into<String>) -> Self {
Self {
title: None,
text: text.into(),
}
}
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DataValidationRule {
pub range: String,
pub kind: DataValidationKind,
pub allow_blank: bool,
pub input_message: Option<Message>,
pub error_message: Option<Message>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum DataValidationKind {
List { source: String },
WholeNumber {
operator: ComparisonOperator,
formula1: String,
formula2: Option<String>,
},
Decimal {
operator: ComparisonOperator,
formula1: String,
formula2: Option<String>,
},
Date {
operator: ComparisonOperator,
formula1: String,
formula2: Option<String>,
},
Time {
operator: ComparisonOperator,
formula1: String,
formula2: Option<String>,
},
TextLength {
operator: ComparisonOperator,
formula1: String,
formula2: Option<String>,
},
Custom { formula: String },
}
impl DataValidationRule {
pub fn list(range: impl Into<String>, source: impl Into<String>) -> Self {
Self {
range: range.into(),
kind: DataValidationKind::List {
source: source.into(),
},
allow_blank: true,
input_message: None,
error_message: None,
}
}
pub fn whole_number(
range: impl Into<String>,
operator: ComparisonOperator,
formula1: impl Into<String>,
) -> Self {
Self {
range: range.into(),
kind: DataValidationKind::WholeNumber {
operator,
formula1: formula1.into(),
formula2: None,
},
allow_blank: true,
input_message: None,
error_message: None,
}
}
pub fn decimal(
range: impl Into<String>,
operator: ComparisonOperator,
formula1: impl Into<String>,
) -> Self {
Self {
range: range.into(),
kind: DataValidationKind::Decimal {
operator,
formula1: formula1.into(),
formula2: None,
},
allow_blank: true,
input_message: None,
error_message: None,
}
}
pub fn date(
range: impl Into<String>,
operator: ComparisonOperator,
formula1: impl Into<String>,
) -> Self {
Self {
range: range.into(),
kind: DataValidationKind::Date {
operator,
formula1: formula1.into(),
formula2: None,
},
allow_blank: true,
input_message: None,
error_message: None,
}
}
pub fn time(
range: impl Into<String>,
operator: ComparisonOperator,
formula1: impl Into<String>,
) -> Self {
Self {
range: range.into(),
kind: DataValidationKind::Time {
operator,
formula1: formula1.into(),
formula2: None,
},
allow_blank: true,
input_message: None,
error_message: None,
}
}
pub fn text_length(
range: impl Into<String>,
operator: ComparisonOperator,
formula1: impl Into<String>,
) -> Self {
Self {
range: range.into(),
kind: DataValidationKind::TextLength {
operator,
formula1: formula1.into(),
formula2: None,
},
allow_blank: true,
input_message: None,
error_message: None,
}
}
pub fn custom(range: impl Into<String>, formula: impl Into<String>) -> Self {
Self {
range: range.into(),
kind: DataValidationKind::Custom {
formula: formula.into(),
},
allow_blank: true,
input_message: None,
error_message: None,
}
}
pub fn with_second_formula(mut self, formula2: impl Into<String>) -> Self {
match &mut self.kind {
DataValidationKind::WholeNumber {
formula2: existing, ..
}
| DataValidationKind::Decimal {
formula2: existing, ..
}
| DataValidationKind::Date {
formula2: existing, ..
}
| DataValidationKind::Time {
formula2: existing, ..
}
| DataValidationKind::TextLength {
formula2: existing, ..
} => {
*existing = Some(formula2.into());
}
DataValidationKind::List { .. } | DataValidationKind::Custom { .. } => {}
}
self
}
pub fn with_allow_blank(mut self, allow_blank: bool) -> Self {
self.allow_blank = allow_blank;
self
}
pub fn with_input_message(mut self, message: Message) -> Self {
self.input_message = Some(message);
self
}
pub fn with_error_message(mut self, message: Message) -> Self {
self.error_message = Some(message);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DefinedName {
pub name: String,
pub refers_to: String,
pub local_sheet_id: Option<usize>,
pub hidden: bool,
pub comment: Option<String>,
}
impl DefinedName {
pub fn new(name: impl Into<String>, refers_to: impl Into<String>) -> Self {
Self {
name: name.into(),
refers_to: refers_to.into(),
local_sheet_id: None,
hidden: false,
comment: None,
}
}
pub fn with_local_sheet_id(mut self, local_sheet_id: usize) -> Self {
self.local_sheet_id = Some(local_sheet_id);
self
}
pub fn with_hidden(mut self, hidden: bool) -> Self {
self.hidden = hidden;
self
}
pub fn with_comment(mut self, comment: impl Into<String>) -> Self {
self.comment = Some(comment.into());
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SheetVisibility {
#[default]
Visible,
Hidden,
VeryHidden,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ColumnSetting {
pub column: usize,
pub width: Option<f64>,
pub hidden: bool,
pub outline_level: u8,
pub collapsed: bool,
pub style: Option<CellFormat>,
}
impl ColumnSetting {
pub fn new(column: usize) -> Self {
Self {
column,
width: None,
hidden: false,
outline_level: 0,
collapsed: false,
style: None,
}
}
pub fn with_width(mut self, width: f64) -> Self {
self.width = Some(width);
self
}
pub fn with_style(mut self, style: CellFormat) -> Self {
self.style = Some(style);
self
}
pub fn with_hidden(mut self, hidden: bool) -> Self {
self.hidden = hidden;
self
}
pub fn with_collapsed(mut self, collapsed: bool) -> Self {
self.collapsed = collapsed;
self
}
pub fn with_outline_level(mut self, level: u8) -> Self {
self.outline_level = level;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FreezePane {
pub frozen_columns: usize,
pub frozen_rows: usize,
}
impl FreezePane {
pub fn new(frozen_columns: usize, frozen_rows: usize) -> Self {
Self {
frozen_columns,
frozen_rows,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PageOrientation {
Portrait,
Landscape,
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct PrintSettings {
pub print_area: Option<String>,
pub repeat_rows: Option<String>,
pub repeat_columns: Option<String>,
pub fit_to_width: Option<u32>,
pub fit_to_height: Option<u32>,
pub scale: Option<u32>,
pub orientation: Option<PageOrientation>,
pub header: Option<String>,
pub footer: Option<String>,
pub header_first: Option<String>,
pub footer_first: Option<String>,
pub header_even: Option<String>,
pub footer_even: Option<String>,
}
impl PrintSettings {
pub fn new() -> Self {
Self::default()
}
pub fn with_print_area(mut self, area: impl Into<String>) -> Self {
self.print_area = Some(area.into());
self
}
pub fn with_repeat_rows(mut self, rows: impl Into<String>) -> Self {
self.repeat_rows = Some(rows.into());
self
}
pub fn with_repeat_columns(mut self, columns: impl Into<String>) -> Self {
self.repeat_columns = Some(columns.into());
self
}
pub fn with_fit_to_width(mut self, pages: u32) -> Self {
self.fit_to_width = Some(pages);
self
}
pub fn with_fit_to_height(mut self, pages: u32) -> Self {
self.fit_to_height = Some(pages);
self
}
pub fn with_scale(mut self, percent: u32) -> Self {
self.scale = Some(percent);
self
}
pub fn with_orientation(mut self, orientation: PageOrientation) -> Self {
self.orientation = Some(orientation);
self
}
pub fn with_header(mut self, header: impl Into<String>) -> Self {
self.header = Some(header.into());
self
}
pub fn with_footer(mut self, footer: impl Into<String>) -> Self {
self.footer = Some(footer.into());
self
}
pub fn with_header_first(mut self, header: impl Into<String>) -> Self {
self.header_first = Some(header.into());
self
}
pub fn with_footer_first(mut self, footer: impl Into<String>) -> Self {
self.footer_first = Some(footer.into());
self
}
pub fn with_header_even(mut self, header: impl Into<String>) -> Self {
self.header_even = Some(header.into());
self
}
pub fn with_footer_even(mut self, footer: impl Into<String>) -> Self {
self.footer_even = Some(footer.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SheetProtection {
pub password: Option<String>,
}
impl SheetProtection {
pub fn locked() -> Self {
Self { password: None }
}
pub fn with_password(password: impl Into<String>) -> Self {
Self {
password: Some(password.into()),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WorkbookProtection {
pub password: Option<String>,
pub lock_structure: bool,
pub lock_windows: bool,
}
impl WorkbookProtection {
pub fn locked() -> Self {
Self {
password: None,
lock_structure: true,
lock_windows: false,
}
}
pub fn with_password(mut self, password: impl Into<String>) -> Self {
self.password = Some(password.into());
self
}
pub fn with_lock_windows(mut self, lock_windows: bool) -> Self {
self.lock_windows = lock_windows;
self
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct WriteProtection {
pub read_only_recommended: bool,
pub user_name: Option<String>,
pub password: Option<String>,
}
impl WriteProtection {
pub fn recommended() -> Self {
Self {
read_only_recommended: true,
user_name: None,
password: None,
}
}
pub fn with_password(mut self, password: impl Into<String>) -> Self {
self.password = Some(password.into());
self
}
pub fn with_user_name(mut self, user_name: impl Into<String>) -> Self {
self.user_name = Some(user_name.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProtectedRange {
pub name: String,
pub range: String,
pub password: Option<String>,
}
impl ProtectedRange {
pub fn new(name: impl Into<String>, range: impl Into<String>) -> Self {
Self {
name: name.into(),
range: range.into(),
password: None,
}
}
pub fn with_password(mut self, password: impl Into<String>) -> Self {
self.password = Some(password.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CellHyperlink {
pub cell: String,
pub target: HyperlinkTarget,
pub tooltip: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum HyperlinkTarget {
External(String),
Internal(String),
}
impl CellHyperlink {
pub fn external(cell: impl Into<String>, url: impl Into<String>) -> Self {
Self {
cell: cell.into(),
target: HyperlinkTarget::External(url.into()),
tooltip: None,
}
}
pub fn internal(cell: impl Into<String>, location: impl Into<String>) -> Self {
Self {
cell: cell.into(),
target: HyperlinkTarget::Internal(location.into()),
tooltip: None,
}
}
pub fn with_tooltip(mut self, tooltip: impl Into<String>) -> Self {
self.tooltip = Some(tooltip.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CellComment {
pub cell: String,
pub author: String,
pub text: String,
pub rich_text: Option<Vec<RichTextRun>>,
}
impl CellComment {
pub fn new(
cell: impl Into<String>,
author: impl Into<String>,
text: impl Into<String>,
) -> Self {
Self {
cell: cell.into(),
author: author.into(),
text: text.into(),
rich_text: None,
}
}
pub fn with_rich_text(mut self, runs: Vec<RichTextRun>) -> Self {
self.text = runs.iter().map(|run| run.text.as_str()).collect();
self.rich_text = Some(runs);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TableStyle {
pub name: String,
pub elements: Vec<TableStyleElement>,
}
impl TableStyle {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
elements: Vec::new(),
}
}
pub fn with_element(mut self, element: TableStyleElement) -> Self {
self.elements.push(element);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct NamedCellStyle {
pub name: String,
pub format: CellFormat,
pub builtin_id: Option<u32>,
}
impl NamedCellStyle {
pub fn new(name: impl Into<String>, format: CellFormat) -> Self {
Self {
name: name.into(),
format,
builtin_id: None,
}
}
pub fn with_builtin_id(mut self, builtin_id: u32) -> Self {
self.builtin_id = Some(builtin_id);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TableStyleElement {
pub element_type: TableStyleElementType,
pub format: CellFormat,
}
impl TableStyleElement {
pub fn new(element_type: TableStyleElementType, format: CellFormat) -> Self {
Self {
element_type,
format,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TableStyleElementType {
WholeTable,
HeaderRow,
TotalRow,
FirstRowStripe,
SecondRowStripe,
FirstColumnStripe,
SecondColumnStripe,
FirstColumn,
LastColumn,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ExcelTable {
pub name: String,
pub range: String,
pub columns: Vec<TableColumn>,
pub show_totals_row: bool,
pub sort_state: Option<SortState>,
pub table_style_name: Option<String>,
}
impl ExcelTable {
pub fn new<S: Into<String>>(
name: impl Into<String>,
range: impl Into<String>,
columns: Vec<S>,
) -> Self {
Self {
name: name.into(),
range: range.into(),
columns: columns.into_iter().map(TableColumn::new).collect(),
show_totals_row: false,
sort_state: None,
table_style_name: None,
}
}
pub fn with_columns(
name: impl Into<String>,
range: impl Into<String>,
columns: Vec<TableColumn>,
) -> Self {
Self {
name: name.into(),
range: range.into(),
columns,
show_totals_row: false,
sort_state: None,
table_style_name: None,
}
}
pub fn with_totals_row(mut self, show: bool) -> Self {
self.show_totals_row = show;
self
}
pub fn with_sort_state(mut self, sort_state: SortState) -> Self {
self.sort_state = Some(sort_state);
self
}
pub fn with_table_style_name(mut self, name: impl Into<String>) -> Self {
self.table_style_name = Some(name.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TableColumn {
pub name: String,
pub calculated_formula: Option<String>,
pub totals_row_function: Option<TotalsRowFunction>,
pub totals_row_label: Option<String>,
pub totals_row_formula: Option<String>,
}
impl TableColumn {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
calculated_formula: None,
totals_row_function: None,
totals_row_label: None,
totals_row_formula: None,
}
}
pub fn with_calculated_formula(mut self, formula: impl Into<String>) -> Self {
self.calculated_formula = Some(formula.into());
self
}
pub fn with_totals_row_function(mut self, function: TotalsRowFunction) -> Self {
self.totals_row_function = Some(function);
self
}
pub fn with_totals_row_label(mut self, label: impl Into<String>) -> Self {
self.totals_row_label = Some(label.into());
self
}
pub fn with_totals_row_formula(mut self, formula: impl Into<String>) -> Self {
self.totals_row_formula = Some(formula.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SortState {
pub range: String,
pub conditions: Vec<SortCondition>,
}
impl SortState {
pub fn new(range: impl Into<String>) -> Self {
Self {
range: range.into(),
conditions: Vec::new(),
}
}
pub fn with_condition(mut self, condition: SortCondition) -> Self {
self.conditions.push(condition);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SortCondition {
pub range: String,
pub descending: bool,
}
impl SortCondition {
pub fn new(range: impl Into<String>) -> Self {
Self {
range: range.into(),
descending: false,
}
}
pub fn with_descending(mut self, descending: bool) -> Self {
self.descending = descending;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TotalsRowFunction {
Sum,
Average,
Count,
CountNums,
Max,
Min,
StdDev,
Var,
Custom,
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct DocumentProperties {
pub title: Option<String>,
pub author: Option<String>,
pub subject: Option<String>,
pub keywords: Option<String>,
pub company: Option<String>,
pub manager: Option<String>,
pub hyperlink_base: Option<String>,
pub custom_properties: Vec<(String, CustomPropertyValue)>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CustomPropertyValue {
Text(String),
Bool(bool),
Int(i32),
Number(f64),
}
impl DocumentProperties {
pub fn new() -> Self {
Self::default()
}
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
pub fn with_author(mut self, author: impl Into<String>) -> Self {
self.author = Some(author.into());
self
}
pub fn with_subject(mut self, subject: impl Into<String>) -> Self {
self.subject = Some(subject.into());
self
}
pub fn with_keywords(mut self, keywords: impl Into<String>) -> Self {
self.keywords = Some(keywords.into());
self
}
pub fn with_company(mut self, company: impl Into<String>) -> Self {
self.company = Some(company.into());
self
}
pub fn with_manager(mut self, manager: impl Into<String>) -> Self {
self.manager = Some(manager.into());
self
}
pub fn with_hyperlink_base(mut self, hyperlink_base: impl Into<String>) -> Self {
self.hyperlink_base = Some(hyperlink_base.into());
self
}
pub fn with_custom_property(
mut self,
name: impl Into<String>,
value: CustomPropertyValue,
) -> Self {
self.custom_properties.push((name.into(), value));
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Scenario {
pub name: String,
pub inputs: Vec<(String, String)>,
pub comment: Option<String>,
}
impl Scenario {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
inputs: Vec::new(),
comment: None,
}
}
pub fn with_input(mut self, cell: impl Into<String>, value: impl Into<String>) -> Self {
self.inputs.push((cell.into(), value.into()));
self
}
pub fn with_comment(mut self, comment: impl Into<String>) -> Self {
self.comment = Some(comment.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AutoFilterColumn {
pub column_offset: u32,
pub visible_values: Vec<String>,
}
impl AutoFilterColumn {
pub fn new(column_offset: u32, visible_values: Vec<String>) -> Self {
Self {
column_offset,
visible_values,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ExternalWorkbookLink {
pub target: String,
pub sheet_names: Vec<String>,
pub defined_names: Vec<(String, String)>,
}
impl ExternalWorkbookLink {
pub fn new(target: impl Into<String>) -> Self {
Self {
target: target.into(),
sheet_names: Vec::new(),
defined_names: Vec::new(),
}
}
pub fn with_sheet_name(mut self, name: impl Into<String>) -> Self {
self.sheet_names.push(name.into());
self
}
pub fn with_defined_name(
mut self,
name: impl Into<String>,
refers_to: impl Into<String>,
) -> Self {
self.defined_names.push((name.into(), refers_to.into()));
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IgnoredError {
pub range: String,
pub number_stored_as_text: bool,
pub formula_range: bool,
pub formula: bool,
pub unlocked_formula: bool,
pub empty_cell_reference: bool,
pub list_data_validation: bool,
pub calculated_column: bool,
pub two_digit_text_year: bool,
pub eval_error: bool,
}
impl IgnoredError {
pub fn new(range: impl Into<String>) -> Self {
Self {
range: range.into(),
number_stored_as_text: false,
formula_range: false,
formula: false,
unlocked_formula: false,
empty_cell_reference: false,
list_data_validation: false,
calculated_column: false,
two_digit_text_year: false,
eval_error: false,
}
}
pub fn with_number_stored_as_text(mut self, value: bool) -> Self {
self.number_stored_as_text = value;
self
}
pub fn with_formula_range(mut self, value: bool) -> Self {
self.formula_range = value;
self
}
pub fn with_formula(mut self, value: bool) -> Self {
self.formula = value;
self
}
pub fn with_unlocked_formula(mut self, value: bool) -> Self {
self.unlocked_formula = value;
self
}
pub fn with_empty_cell_reference(mut self, value: bool) -> Self {
self.empty_cell_reference = value;
self
}
pub fn with_list_data_validation(mut self, value: bool) -> Self {
self.list_data_validation = value;
self
}
pub fn with_calculated_column(mut self, value: bool) -> Self {
self.calculated_column = value;
self
}
pub fn with_two_digit_text_year(mut self, value: bool) -> Self {
self.two_digit_text_year = value;
self
}
pub fn with_eval_error(mut self, value: bool) -> Self {
self.eval_error = value;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct CellAnchorPoint {
pub column: u32,
pub column_offset_emu: i64,
pub row: u32,
pub row_offset_emu: i64,
}
impl CellAnchorPoint {
pub fn new(column: u32, row: u32) -> Self {
Self {
column,
row,
column_offset_emu: 0,
row_offset_emu: 0,
}
}
pub fn with_column_offset_emu(mut self, offset: i64) -> Self {
self.column_offset_emu = offset;
self
}
pub fn with_row_offset_emu(mut self, offset: i64) -> Self {
self.row_offset_emu = offset;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PictureFormat {
Png,
Jpeg,
Gif,
Bmp,
}
impl PictureFormat {
pub(crate) fn extension(self) -> &'static str {
match self {
PictureFormat::Png => "png",
PictureFormat::Jpeg => "jpeg",
PictureFormat::Gif => "gif",
PictureFormat::Bmp => "bmp",
}
}
pub(crate) fn content_type(self) -> &'static str {
match self {
PictureFormat::Png => "image/png",
PictureFormat::Jpeg => "image/jpeg",
PictureFormat::Gif => "image/gif",
PictureFormat::Bmp => "image/bmp",
}
}
pub(crate) fn from_extension(extension: &str) -> Option<Self> {
Some(match extension.to_ascii_lowercase().as_str() {
"png" => PictureFormat::Png,
"jpeg" | "jpg" => PictureFormat::Jpeg,
"gif" => PictureFormat::Gif,
"bmp" => PictureFormat::Bmp,
_ => return None,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SheetPicture {
pub data: Vec<u8>,
pub format: PictureFormat,
pub name: String,
pub shape_properties: Option<drawing::ShapeProperties>,
}
impl SheetPicture {
pub fn new(data: impl Into<Vec<u8>>, format: PictureFormat) -> Self {
Self {
data: data.into(),
format,
name: String::new(),
shape_properties: None,
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
}
pub fn with_shape_properties(mut self, properties: drawing::ShapeProperties) -> Self {
self.shape_properties = Some(properties);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SheetChart {
pub chart_space: chart::ChartSpace,
pub name: String,
}
impl SheetChart {
pub fn new(chart_space: chart::ChartSpace) -> Self {
Self {
chart_space,
name: String::new(),
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DrawingObject {
Picture(Box<SheetPicture>),
Chart(Box<SheetChart>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct DrawingAnchor {
pub from: CellAnchorPoint,
pub to: CellAnchorPoint,
pub object: DrawingObject,
}
impl DrawingAnchor {
pub fn new(from: CellAnchorPoint, to: CellAnchorPoint, object: DrawingObject) -> Self {
Self { from, to, object }
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SheetDrawing {
pub anchors: Vec<DrawingAnchor>,
}
impl SheetDrawing {
pub fn new() -> Self {
Self::default()
}
pub fn with_anchor(mut self, anchor: DrawingAnchor) -> Self {
self.anchors.push(anchor);
self
}
}