Skip to main content

excel_mcp_server/types/
inputs.rs

1use schemars::JsonSchema;
2use serde::Deserialize;
3
4use super::enums::*;
5
6// ── Serde default helpers ──────────────────────────────────────────
7
8fn default_chart_width() -> u32 {
9    480
10}
11
12fn default_chart_height() -> u32 {
13    288
14}
15
16fn default_comma() -> String {
17    ",".to_string()
18}
19
20fn default_true() -> bool {
21    true
22}
23
24// ── Workbook lifecycle inputs ──────────────────────────────────────
25
26/// Input for creating a new empty workbook
27#[derive(Deserialize, JsonSchema)]
28#[serde(deny_unknown_fields)]
29pub struct CreateWorkbookInput {}
30
31/// Input for opening an existing Excel file
32#[derive(Deserialize, JsonSchema)]
33#[serde(deny_unknown_fields)]
34pub struct OpenWorkbookInput {
35    /// Absolute or relative path to the Excel file (xlsx, xlsm, xls, ods)
36    pub file_path: String,
37    /// If true, opens in read-only mode using the fast calamine engine.
38    /// If false, opens in edit mode using umya-spreadsheet. Default: false
39    #[serde(default)]
40    pub read_only: bool,
41}
42
43/// Input for saving a workbook to disk
44#[derive(Deserialize, JsonSchema)]
45#[serde(deny_unknown_fields)]
46pub struct SaveWorkbookInput {
47    /// The workbook handle returned by create_workbook or open_workbook
48    pub workbook_id: String,
49    /// Destination file path (must end in .xlsx)
50    pub file_path: String,
51}
52
53/// Input for closing a workbook and freeing memory
54#[derive(Deserialize, JsonSchema)]
55#[serde(deny_unknown_fields)]
56pub struct CloseWorkbookInput {
57    /// The workbook handle to close
58    pub workbook_id: String,
59}
60
61// ── Read inputs ────────────────────────────────────────────────────
62
63/// Input for reading sheet data with optional range and pagination
64#[derive(Deserialize, JsonSchema)]
65#[serde(deny_unknown_fields)]
66pub struct ReadSheetInput {
67    /// The workbook handle
68    pub workbook_id: String,
69    /// Name of the sheet to read
70    pub sheet_name: String,
71    /// Optional range in A1:B2 notation. If omitted, reads the entire used range
72    pub range: Option<String>,
73    /// Continuation token from a previous paginated response
74    pub continuation_token: Option<String>,
75}
76
77/// Input for reading a single cell
78#[derive(Deserialize, JsonSchema)]
79#[serde(deny_unknown_fields)]
80pub struct ReadCellInput {
81    /// The workbook handle
82    pub workbook_id: String,
83    /// Name of the sheet containing the cell
84    pub sheet_name: String,
85    /// Cell reference in A1 notation (e.g., "C5")
86    pub cell: String,
87}
88
89// ── Write inputs ───────────────────────────────────────────────────
90
91/// A single cell write operation
92#[derive(Deserialize, JsonSchema)]
93#[serde(deny_unknown_fields)]
94pub struct CellWrite {
95    /// Cell reference in A1 notation
96    pub cell: String,
97    /// Value to write. Strings starting with "=" are written as formulas.
98    /// Numbers, booleans, and ISO 8601 dates are auto-detected.
99    pub value: serde_json::Value,
100}
101
102/// Input for batch cell writing
103#[derive(Deserialize, JsonSchema)]
104#[serde(deny_unknown_fields)]
105pub struct WriteCellsInput {
106    /// The workbook handle
107    pub workbook_id: String,
108    /// Name of the target sheet
109    pub sheet_name: String,
110    /// Array of cell writes to apply
111    pub cells: Vec<CellWrite>,
112}
113
114/// Input for writing a row of values starting from a cell
115#[derive(Deserialize, JsonSchema)]
116#[serde(deny_unknown_fields)]
117pub struct WriteRowInput {
118    /// The workbook handle
119    pub workbook_id: String,
120    /// Name of the target sheet
121    pub sheet_name: String,
122    /// Starting cell reference (values fill rightward)
123    pub start_cell: String,
124    /// Values to write in consecutive columns
125    pub values: Vec<serde_json::Value>,
126}
127
128/// Input for writing a column of values starting from a cell
129#[derive(Deserialize, JsonSchema)]
130#[serde(deny_unknown_fields)]
131pub struct WriteColumnInput {
132    /// The workbook handle
133    pub workbook_id: String,
134    /// Name of the target sheet
135    pub sheet_name: String,
136    /// Starting cell reference (values fill downward)
137    pub start_cell: String,
138    /// Values to write in consecutive rows
139    pub values: Vec<serde_json::Value>,
140}
141
142// ── Formatting inputs ──────────────────────────────────────────────
143
144/// Input for applying formatting to a range of cells
145#[derive(Deserialize, JsonSchema)]
146#[serde(deny_unknown_fields)]
147pub struct SetCellFormatInput {
148    /// The workbook handle
149    pub workbook_id: String,
150    /// Name of the target sheet
151    pub sheet_name: String,
152    /// Range in A1:B2 notation
153    pub range: String,
154    /// Whether to apply bold font weight
155    #[serde(default)]
156    pub bold: Option<bool>,
157    /// Whether to apply italic font style
158    #[serde(default)]
159    pub italic: Option<bool>,
160    /// Whether to apply underline
161    #[serde(default)]
162    pub underline: Option<bool>,
163    /// Font size in points
164    #[serde(default)]
165    pub font_size: Option<f64>,
166    /// Hex color string for font, e.g., "#FF0000"
167    #[serde(default)]
168    pub font_color: Option<String>,
169    /// Hex color string for cell background
170    #[serde(default)]
171    pub background_color: Option<String>,
172    /// Excel number format string, e.g., "#,##0.00"
173    #[serde(default)]
174    pub number_format: Option<String>,
175    /// Horizontal text alignment
176    #[serde(default)]
177    pub horizontal_alignment: Option<HorizontalAlignment>,
178    /// Vertical text alignment
179    #[serde(default)]
180    pub vertical_alignment: Option<VerticalAlignment>,
181    /// Border style for all edges
182    #[serde(default)]
183    pub border_style: Option<BorderStyle>,
184}
185
186/// Input for merging a range of cells
187#[derive(Deserialize, JsonSchema)]
188#[serde(deny_unknown_fields)]
189pub struct MergeCellsInput {
190    /// The workbook handle
191    pub workbook_id: String,
192    /// Name of the target sheet
193    pub sheet_name: String,
194    /// Range to merge in A1:B2 notation
195    pub range: String,
196}
197
198// ── Chart, image, table inputs ─────────────────────────────────────
199
200/// Input for adding a chart to a worksheet
201#[derive(Deserialize, JsonSchema)]
202#[serde(deny_unknown_fields)]
203pub struct AddChartInput {
204    /// The workbook handle
205    pub workbook_id: String,
206    /// Name of the target sheet
207    pub sheet_name: String,
208    /// Type of chart to create
209    pub chart_type: ChartType,
210    /// Data range in A1:B2 notation
211    pub data_range: String,
212    /// Optional chart title
213    #[serde(default)]
214    pub title: Option<String>,
215    /// Optional X-axis label
216    #[serde(default)]
217    pub x_axis_label: Option<String>,
218    /// Optional Y-axis label
219    #[serde(default)]
220    pub y_axis_label: Option<String>,
221    /// Position of the chart legend
222    #[serde(default)]
223    pub legend_position: Option<LegendPosition>,
224    /// Chart width in pixels. Default: 480
225    #[serde(default = "default_chart_width")]
226    pub width: u32,
227    /// Chart height in pixels. Default: 288
228    #[serde(default = "default_chart_height")]
229    pub height: u32,
230}
231
232/// Input for embedding an image in a worksheet
233#[derive(Deserialize, JsonSchema)]
234#[serde(deny_unknown_fields)]
235pub struct AddImageInput {
236    /// The workbook handle
237    pub workbook_id: String,
238    /// Name of the target sheet
239    pub sheet_name: String,
240    /// Cell where the image top-left corner is anchored
241    pub cell: String,
242    /// Path to a PNG or JPEG image file
243    pub image_path: String,
244    /// Optional width in pixels to scale the image
245    #[serde(default)]
246    pub width: Option<u32>,
247    /// Optional height in pixels to scale the image
248    #[serde(default)]
249    pub height: Option<u32>,
250}
251
252/// Input for creating an Excel Table
253#[derive(Deserialize, JsonSchema)]
254#[serde(deny_unknown_fields)]
255pub struct AddTableInput {
256    /// The workbook handle
257    pub workbook_id: String,
258    /// Name of the target sheet
259    pub sheet_name: String,
260    /// Range for the table in A1:B2 notation (includes header row)
261    pub range: String,
262    /// Column header names
263    pub columns: Vec<String>,
264    /// Optional Excel table style name (e.g., "Table Style Medium 2")
265    #[serde(default)]
266    pub style: Option<String>,
267    /// Whether to show a totals row. Default: false
268    #[serde(default)]
269    pub totals_row: bool,
270    /// Whether to enable autofilter. Default: true
271    #[serde(default = "default_true")]
272    pub autofilter: bool,
273}
274
275// ── Conditional formatting, validation, sparkline inputs ───────────
276
277/// Input for adding a conditional formatting rule
278#[derive(Deserialize, JsonSchema)]
279#[serde(deny_unknown_fields)]
280pub struct AddConditionalFormatInput {
281    /// The workbook handle
282    pub workbook_id: String,
283    /// Name of the target sheet
284    pub sheet_name: String,
285    /// Range to apply the rule to in A1:B2 notation
286    pub range: String,
287    /// The conditional formatting rule definition
288    pub rule: ConditionalFormatRule,
289    /// Formatting to apply when condition is met
290    #[serde(default)]
291    pub format: Option<ConditionalFormatStyle>,
292}
293
294/// Input for adding data validation to a range
295#[derive(Deserialize, JsonSchema)]
296#[serde(deny_unknown_fields)]
297pub struct AddDataValidationInput {
298    /// The workbook handle
299    pub workbook_id: String,
300    /// Name of the target sheet
301    pub sheet_name: String,
302    /// Range to apply validation to in A1:B2 notation
303    pub range: String,
304    /// The validation rule definition
305    pub validation: ValidationRule,
306    /// Optional input message shown when the cell is selected
307    #[serde(default)]
308    pub input_message: Option<ValidationMessage>,
309    /// Optional error alert shown when invalid data is entered
310    #[serde(default)]
311    pub error_alert: Option<ValidationAlert>,
312}
313
314// ── Layout inputs ──────────────────────────────────────────────────
315
316/// Input for setting the width of a column
317#[derive(Deserialize, JsonSchema)]
318#[serde(deny_unknown_fields)]
319pub struct SetColumnWidthInput {
320    /// The workbook handle
321    pub workbook_id: String,
322    /// Name of the target sheet
323    pub sheet_name: String,
324    /// Column identifier in letter notation (e.g., "A", "BC")
325    pub column: String,
326    /// Width in character units
327    pub width: f64,
328}
329
330/// Input for setting the height of a row
331#[derive(Deserialize, JsonSchema)]
332#[serde(deny_unknown_fields)]
333pub struct SetRowHeightInput {
334    /// The workbook handle
335    pub workbook_id: String,
336    /// Name of the target sheet
337    pub sheet_name: String,
338    /// 1-based row number
339    pub row: u32,
340    /// Height in points
341    pub height: f64,
342}
343
344/// Input for freezing panes at a cell position
345#[derive(Deserialize, JsonSchema)]
346#[serde(deny_unknown_fields)]
347pub struct FreezePanesInput {
348    /// The workbook handle
349    pub workbook_id: String,
350    /// Name of the target sheet
351    pub sheet_name: String,
352    /// Cell reference — rows above and columns left of this cell are frozen
353    pub cell: String,
354}
355
356/// Input for adding a sparkline to a cell
357#[derive(Deserialize, JsonSchema)]
358#[serde(deny_unknown_fields)]
359pub struct AddSparklineInput {
360    /// The workbook handle
361    pub workbook_id: String,
362    /// Name of the target sheet
363    pub sheet_name: String,
364    /// Cell where the sparkline is placed
365    pub target_cell: String,
366    /// Data range for the sparkline values
367    pub data_range: String,
368    /// Type of sparkline to create
369    pub sparkline_type: SparklineType,
370}
371
372// ── Search and export inputs ───────────────────────────────────────
373
374/// Input for searching cell values across sheets
375#[derive(Deserialize, JsonSchema)]
376#[serde(deny_unknown_fields)]
377pub struct SearchCellsInput {
378    /// The workbook handle
379    pub workbook_id: String,
380    /// If omitted, searches all sheets
381    #[serde(default)]
382    pub sheet_name: Option<String>,
383    /// The value or substring to search for
384    pub query: String,
385    /// Search mode: exact or substring. Default: substring
386    #[serde(default)]
387    pub match_mode: MatchMode,
388}
389
390/// Input for exporting a sheet as CSV
391#[derive(Deserialize, JsonSchema)]
392#[serde(deny_unknown_fields)]
393pub struct SheetToCsvInput {
394    /// The workbook handle
395    pub workbook_id: String,
396    /// Name of the sheet to export
397    pub sheet_name: String,
398    /// Delimiter character. Default: ","
399    #[serde(default = "default_comma")]
400    pub delimiter: String,
401}
402
403// ── Sheet management inputs ────────────────────────────────────────
404
405/// Input for adding a new worksheet
406#[derive(Deserialize, JsonSchema)]
407#[serde(deny_unknown_fields)]
408pub struct AddSheetInput {
409    /// The workbook handle
410    pub workbook_id: String,
411    /// Name for the new sheet
412    pub sheet_name: String,
413}
414
415/// Input for renaming an existing worksheet
416#[derive(Deserialize, JsonSchema)]
417#[serde(deny_unknown_fields)]
418pub struct RenameSheetInput {
419    /// The workbook handle
420    pub workbook_id: String,
421    /// Current name of the sheet to rename
422    pub current_name: String,
423    /// New name for the sheet
424    pub new_name: String,
425}
426
427/// Input for deleting a worksheet
428#[derive(Deserialize, JsonSchema)]
429#[serde(deny_unknown_fields)]
430pub struct DeleteSheetInput {
431    /// The workbook handle
432    pub workbook_id: String,
433    /// Name of the sheet to delete
434    pub sheet_name: String,
435}
436
437/// Input for listing all sheets in a workbook
438#[derive(Deserialize, JsonSchema)]
439#[serde(deny_unknown_fields)]
440pub struct ListSheetsInput {
441    /// The workbook handle
442    pub workbook_id: String,
443}
444
445/// Input for getting the dimensions of a sheet
446#[derive(Deserialize, JsonSchema)]
447#[serde(deny_unknown_fields)]
448pub struct GetSheetDimensionsInput {
449    /// The workbook handle
450    pub workbook_id: String,
451    /// Name of the sheet to measure
452    pub sheet_name: String,
453}
454
455/// Input for describing a workbook's structure and sample data
456#[derive(Deserialize, JsonSchema)]
457#[serde(deny_unknown_fields)]
458pub struct DescribeWorkbookInput {
459    /// The workbook handle
460    pub workbook_id: String,
461}
462
463// ── New tools: Batch 1–4 ───────────────────────────────────────────
464
465/// Margins for page setup (inches)
466#[derive(Deserialize, JsonSchema)]
467pub struct PageMargins {
468    #[serde(default)]
469    pub top: Option<f64>,
470    #[serde(default)]
471    pub bottom: Option<f64>,
472    #[serde(default)]
473    pub left: Option<f64>,
474    #[serde(default)]
475    pub right: Option<f64>,
476}
477
478/// Fit-to-page settings
479#[derive(Deserialize, JsonSchema)]
480pub struct FitToPages {
481    pub width: u16,
482    pub height: u16,
483}
484
485/// Repeat rows for printing
486#[derive(Deserialize, JsonSchema)]
487pub struct RepeatRows {
488    /// First row (0-based)
489    pub first: u32,
490    /// Last row (0-based)
491    pub last: u32,
492}
493
494/// Input for configuring page setup, headers, footers, print options
495#[derive(Deserialize, JsonSchema)]
496#[serde(deny_unknown_fields)]
497pub struct SetPageSetupInput {
498    pub workbook_id: String,
499    pub sheet_name: String,
500    #[serde(default)]
501    pub landscape: Option<bool>,
502    #[serde(default)]
503    pub paper_size: Option<u8>,
504    #[serde(default)]
505    pub margins: Option<PageMargins>,
506    #[serde(default)]
507    pub fit_to_pages: Option<FitToPages>,
508    #[serde(default)]
509    pub print_scale: Option<u16>,
510    /// Print area in A1:B2 notation
511    #[serde(default)]
512    pub print_area: Option<String>,
513    #[serde(default)]
514    pub repeat_rows: Option<RepeatRows>,
515    /// Excel header string (supports &L, &C, &R, &P, &N, &D codes)
516    #[serde(default)]
517    pub header: Option<String>,
518    /// Excel footer string
519    #[serde(default)]
520    pub footer: Option<String>,
521    #[serde(default)]
522    pub print_gridlines: Option<bool>,
523    #[serde(default)]
524    pub center_horizontally: Option<bool>,
525    #[serde(default)]
526    pub center_vertically: Option<bool>,
527}
528
529/// Input for adding a comment/note to a cell
530#[derive(Deserialize, JsonSchema)]
531#[serde(deny_unknown_fields)]
532pub struct AddCommentInput {
533    pub workbook_id: String,
534    pub sheet_name: String,
535    pub cell: String,
536    pub text: String,
537    #[serde(default)]
538    pub author: Option<String>,
539}
540
541/// Input for adding a hyperlink to a cell
542#[derive(Deserialize, JsonSchema)]
543#[serde(deny_unknown_fields)]
544pub struct AddHyperlinkInput {
545    pub workbook_id: String,
546    pub sheet_name: String,
547    pub cell: String,
548    pub url: String,
549    #[serde(default)]
550    pub tooltip: Option<String>,
551    /// Display text (if different from URL)
552    #[serde(default)]
553    pub display_text: Option<String>,
554}
555
556/// Input for adding a defined name (named range)
557#[derive(Deserialize, JsonSchema)]
558#[serde(deny_unknown_fields)]
559pub struct AddDefinedNameInput {
560    pub workbook_id: String,
561    pub name: String,
562    /// Formula or range reference, e.g. "Sheet1!$A$1:$B$10"
563    pub formula: String,
564}
565
566/// Input for listing defined names
567#[derive(Deserialize, JsonSchema)]
568#[serde(deny_unknown_fields)]
569pub struct ListDefinedNamesInput {
570    pub workbook_id: String,
571}
572
573/// Input for sheet display settings
574#[derive(Deserialize, JsonSchema)]
575#[serde(deny_unknown_fields)]
576pub struct SetSheetSettingsInput {
577    pub workbook_id: String,
578    pub sheet_name: String,
579    #[serde(default)]
580    pub hidden: Option<bool>,
581    #[serde(default)]
582    pub very_hidden: Option<bool>,
583    #[serde(default)]
584    pub zoom: Option<u16>,
585    #[serde(default)]
586    pub hide_gridlines: Option<bool>,
587    #[serde(default)]
588    pub hide_headings: Option<bool>,
589    #[serde(default)]
590    pub tab_color: Option<String>,
591    #[serde(default)]
592    pub right_to_left: Option<bool>,
593}
594
595/// Input for setting the active (visible) sheet
596#[derive(Deserialize, JsonSchema)]
597#[serde(deny_unknown_fields)]
598pub struct SetActiveSheetInput {
599    pub workbook_id: String,
600    /// 0-based sheet index
601    pub sheet_index: usize,
602}
603
604/// Input for inserting/deleting rows
605#[derive(Deserialize, JsonSchema)]
606#[serde(deny_unknown_fields)]
607pub struct InsertDeleteRowsInput {
608    pub workbook_id: String,
609    pub sheet_name: String,
610    /// 1-based row number where insertion/deletion starts
611    pub at_row: u32,
612    /// Number of rows to insert or delete
613    pub count: u32,
614}
615
616/// Input for inserting/deleting columns
617#[derive(Deserialize, JsonSchema)]
618#[serde(deny_unknown_fields)]
619pub struct InsertDeleteColumnsInput {
620    pub workbook_id: String,
621    pub sheet_name: String,
622    /// Column letter where insertion/deletion starts (e.g. "C")
623    pub at_column: String,
624    pub count: u16,
625}
626
627/// Input for grouping rows or columns (outline)
628#[derive(Deserialize, JsonSchema)]
629#[serde(deny_unknown_fields)]
630pub struct GroupRowsInput {
631    pub workbook_id: String,
632    pub sheet_name: String,
633    /// First row (1-based)
634    pub start: u32,
635    /// Last row (1-based)
636    pub end: u32,
637    /// Outline level (1-7). Default: 1
638    #[serde(default = "default_level")]
639    pub level: u8,
640}
641
642/// Input for grouping columns
643#[derive(Deserialize, JsonSchema)]
644#[serde(deny_unknown_fields)]
645pub struct GroupColumnsInput {
646    pub workbook_id: String,
647    pub sheet_name: String,
648    /// First column letter
649    pub start: String,
650    /// Last column letter
651    pub end: String,
652    #[serde(default = "default_level")]
653    pub level: u8,
654}
655
656fn default_level() -> u8 {
657    1
658}
659
660/// Input for protecting a sheet
661#[derive(Deserialize, JsonSchema)]
662#[serde(deny_unknown_fields)]
663pub struct ProtectSheetInput {
664    pub workbook_id: String,
665    pub sheet_name: String,
666    #[serde(default)]
667    pub password: Option<String>,
668}
669
670/// Input for protecting a workbook
671#[derive(Deserialize, JsonSchema)]
672#[serde(deny_unknown_fields)]
673pub struct ProtectWorkbookInput {
674    pub workbook_id: String,
675    #[serde(default)]
676    pub password: Option<String>,
677}
678
679/// Input for autofitting column widths
680#[derive(Deserialize, JsonSchema)]
681#[serde(deny_unknown_fields)]
682pub struct AutofitColumnsInput {
683    pub workbook_id: String,
684    pub sheet_name: String,
685}
686
687/// A single chart series definition
688#[derive(Deserialize, JsonSchema)]
689pub struct ChartSeriesInput {
690    /// Data range for values (e.g. "Sheet1!$B$2:$B$10")
691    pub values: String,
692    /// Data range for categories/labels
693    #[serde(default)]
694    pub categories: Option<String>,
695    /// Series name
696    #[serde(default)]
697    pub name: Option<String>,
698    /// Hex color for the series
699    #[serde(default)]
700    pub color: Option<String>,
701    /// Show data labels on this series
702    #[serde(default)]
703    pub data_labels: Option<bool>,
704    /// Trendline type: linear, exponential, polynomial, power, logarithmic, moving_average
705    #[serde(default)]
706    pub trendline: Option<String>,
707    /// Marker type: circle, diamond, square, triangle, none
708    #[serde(default)]
709    pub marker: Option<String>,
710    /// Use secondary Y axis
711    #[serde(default)]
712    pub secondary_axis: Option<bool>,
713    /// Line width in points
714    #[serde(default)]
715    pub line_width: Option<f64>,
716    /// Dash style: "solid", "dash", "dot", "dash_dot", "long_dash", "long_dash_dot"
717    #[serde(default)]
718    pub dash_style: Option<String>,
719    /// Gradient stops: array of {color, position}
720    #[serde(default)]
721    pub gradient: Option<Vec<GradientStopInput>>,
722    /// Bubble sizes range (for bubble charts)
723    #[serde(default)]
724    pub bubble_sizes: Option<String>,
725    /// Error bar configuration
726    #[serde(default)]
727    pub error_bars: Option<ErrorBarInput>,
728}
729
730#[derive(Deserialize, JsonSchema)]
731pub struct ErrorBarInput {
732    /// "both", "plus", "minus"
733    #[serde(default = "default_both")]
734    pub bar_type: String,
735    /// "fixed", "percentage", "std_dev", "std_error"
736    #[serde(default = "default_fixed")]
737    pub value_type: String,
738    pub value: f64,
739}
740
741fn default_both() -> String {
742    "both".to_string()
743}
744fn default_fixed() -> String {
745    "fixed".to_string()
746}
747
748/// Pivot chart source
749#[derive(Deserialize, JsonSchema)]
750pub struct PivotChartSourceInput {
751    pub pivot_table: String,
752    pub sheet: String,
753}
754
755/// Enhanced chart input with full series control
756#[derive(Deserialize, JsonSchema)]
757#[serde(deny_unknown_fields)]
758pub struct AddChartEnhancedInput {
759    pub workbook_id: String,
760    pub sheet_name: String,
761    pub chart_type: ChartType,
762    /// Individual series definitions (preferred over data_range)
763    #[serde(default)]
764    pub series: Vec<ChartSeriesInput>,
765    /// Simple data range (used if series is empty)
766    #[serde(default)]
767    pub data_range: Option<String>,
768    /// Cell where chart top-left is placed (e.g. "E2"). Default: "A1"
769    #[serde(default)]
770    pub cell: Option<String>,
771    #[serde(default)]
772    pub title: Option<String>,
773    #[serde(default)]
774    pub x_axis_label: Option<String>,
775    #[serde(default)]
776    pub y_axis_label: Option<String>,
777    #[serde(default)]
778    pub legend_position: Option<LegendPosition>,
779    #[serde(default = "default_chart_width")]
780    pub width: u32,
781    #[serde(default = "default_chart_height")]
782    pub height: u32,
783    /// Link chart to a pivot table
784    #[serde(default)]
785    pub pivot_source: Option<PivotChartSourceInput>,
786    /// Show data table below chart
787    #[serde(default)]
788    pub show_data_table: Option<bool>,
789    /// 3D perspective: rot_x, rot_y, perspective
790    #[serde(default)]
791    pub view_3d: Option<View3DInput>,
792    /// Preset chart style number (1-48)
793    #[serde(default)]
794    pub style: Option<u8>,
795    /// Accessibility alt text
796    #[serde(default)]
797    pub alt_text: Option<AltTextInput>,
798    /// Y-axis minimum value
799    #[serde(default)]
800    pub y_axis_min: Option<f64>,
801    /// Y-axis maximum value
802    #[serde(default)]
803    pub y_axis_max: Option<f64>,
804    /// Y-axis logarithmic base (e.g. 10)
805    #[serde(default)]
806    pub y_axis_log_base: Option<f64>,
807    /// Reverse X axis direction
808    #[serde(default)]
809    pub x_axis_reverse: Option<bool>,
810    /// Reverse Y axis direction
811    #[serde(default)]
812    pub y_axis_reverse: Option<bool>,
813    /// X-axis number format string
814    #[serde(default)]
815    pub x_axis_format: Option<String>,
816    /// Y-axis number format string
817    #[serde(default)]
818    pub y_axis_format: Option<String>,
819    /// Show drop lines
820    #[serde(default)]
821    pub drop_lines: Option<bool>,
822    /// Show high-low lines
823    #[serde(default)]
824    pub high_low_lines: Option<bool>,
825    /// Plot area background fill color (hex)
826    #[serde(default)]
827    pub plot_area_fill: Option<String>,
828}
829
830/// Pivot table value field
831#[derive(Deserialize, JsonSchema)]
832pub struct PivotValueFieldInput {
833    pub field: String,
834    /// Aggregation: sum, count, average, max, min, product, count_nums, std_dev, var
835    #[serde(default = "default_sum")]
836    pub aggregation: String,
837}
838
839fn default_sum() -> String {
840    "sum".to_string()
841}
842
843/// Input for creating a pivot table
844#[derive(Deserialize, JsonSchema)]
845#[serde(deny_unknown_fields)]
846pub struct AddPivotTableInput {
847    pub workbook_id: String,
848    pub sheet_name: String,
849    /// Cell where pivot table starts (e.g. "A1")
850    #[serde(default)]
851    pub cell: Option<String>,
852    pub name: String,
853    /// Source range including sheet (e.g. "'Data'!$A$1:$E$100")
854    pub source_range: String,
855    #[serde(default)]
856    pub row_fields: Vec<String>,
857    #[serde(default)]
858    pub column_fields: Vec<String>,
859    pub value_fields: Vec<PivotValueFieldInput>,
860    #[serde(default)]
861    pub filter_fields: Vec<String>,
862    #[serde(default)]
863    pub style: Option<String>,
864    /// Layout: compact, outline, tabular. Default: compact
865    #[serde(default)]
866    pub layout: Option<String>,
867    /// Calculated fields to add
868    #[serde(default)]
869    pub calculated_fields: Vec<PivotCalculatedFieldInput>,
870    /// Date grouping for fields
871    #[serde(default)]
872    pub date_groups: Vec<PivotDateGroupInput>,
873    /// Numeric range grouping for fields
874    #[serde(default)]
875    pub range_groups: Vec<PivotRangeGroupInput>,
876    /// Number format for value fields
877    #[serde(default)]
878    pub value_formats: Vec<PivotValueFormatInput>,
879    /// Toggle subtotals per field
880    #[serde(default)]
881    pub subtotals: Vec<PivotSubtotalToggle>,
882    /// Show grand totals for rows
883    #[serde(default)]
884    pub grand_total_rows: Option<bool>,
885    /// Show grand totals for columns
886    #[serde(default)]
887    pub grand_total_cols: Option<bool>,
888    /// Show row headers
889    #[serde(default)]
890    pub show_row_headers: Option<bool>,
891    /// Show column headers
892    #[serde(default)]
893    pub show_column_headers: Option<bool>,
894    /// Show row stripes
895    #[serde(default)]
896    pub show_row_stripes: Option<bool>,
897}
898
899/// Input for reading comments from a sheet
900#[derive(Deserialize, JsonSchema)]
901#[serde(deny_unknown_fields)]
902pub struct ReadCommentsInput {
903    pub workbook_id: String,
904    pub sheet_name: String,
905}
906
907/// A rich text run
908#[derive(Deserialize, JsonSchema)]
909pub struct RichTextRunInput {
910    pub text: String,
911    #[serde(default)]
912    pub bold: Option<bool>,
913    #[serde(default)]
914    pub italic: Option<bool>,
915    #[serde(default)]
916    pub color: Option<String>,
917    #[serde(default)]
918    pub font_size: Option<f64>,
919}
920
921/// Input for writing rich text to a cell
922#[derive(Deserialize, JsonSchema)]
923#[serde(deny_unknown_fields)]
924pub struct WriteRichTextInput {
925    pub workbook_id: String,
926    pub sheet_name: String,
927    pub cell: String,
928    pub runs: Vec<RichTextRunInput>,
929}
930
931// ── Batch 5–8: Remaining 22 tools ──────────────────────────────────
932
933/// Input for setting column/row format
934#[derive(Deserialize, JsonSchema)]
935#[serde(deny_unknown_fields)]
936pub struct SetColumnFormatInput {
937    pub workbook_id: String,
938    pub sheet_name: String,
939    pub column: String,
940    #[serde(default)]
941    pub bold: Option<bool>,
942    #[serde(default)]
943    pub italic: Option<bool>,
944    #[serde(default)]
945    pub font_size: Option<f64>,
946    #[serde(default)]
947    pub font_color: Option<String>,
948    #[serde(default)]
949    pub background_color: Option<String>,
950    #[serde(default)]
951    pub number_format: Option<String>,
952}
953
954#[derive(Deserialize, JsonSchema)]
955#[serde(deny_unknown_fields)]
956pub struct SetRowFormatInput {
957    pub workbook_id: String,
958    pub sheet_name: String,
959    /// 1-based row number
960    pub row: u32,
961    #[serde(default)]
962    pub bold: Option<bool>,
963    #[serde(default)]
964    pub italic: Option<bool>,
965    #[serde(default)]
966    pub font_size: Option<f64>,
967    #[serde(default)]
968    pub font_color: Option<String>,
969    #[serde(default)]
970    pub background_color: Option<String>,
971    #[serde(default)]
972    pub number_format: Option<String>,
973}
974
975#[derive(Deserialize, JsonSchema)]
976#[serde(deny_unknown_fields)]
977pub struct SetColumnHiddenInput {
978    pub workbook_id: String,
979    pub sheet_name: String,
980    pub column: String,
981    #[serde(default)]
982    pub hidden: bool,
983}
984
985#[derive(Deserialize, JsonSchema)]
986#[serde(deny_unknown_fields)]
987pub struct SetRowHiddenInput {
988    pub workbook_id: String,
989    pub sheet_name: String,
990    pub row: u32,
991    #[serde(default)]
992    pub hidden: bool,
993}
994
995#[derive(Deserialize, JsonSchema)]
996#[serde(deny_unknown_fields)]
997pub struct SetColumnRangeWidthInput {
998    pub workbook_id: String,
999    pub sheet_name: String,
1000    pub first_column: String,
1001    pub last_column: String,
1002    pub width: f64,
1003}
1004
1005#[derive(Deserialize, JsonSchema)]
1006#[serde(deny_unknown_fields)]
1007pub struct SetDefaultRowHeightInput {
1008    pub workbook_id: String,
1009    pub sheet_name: String,
1010    pub height: f64,
1011}
1012
1013#[derive(Deserialize, JsonSchema)]
1014#[serde(deny_unknown_fields)]
1015pub struct SetSelectionInput {
1016    pub workbook_id: String,
1017    pub sheet_name: String,
1018    pub cell: String,
1019}
1020
1021#[derive(Deserialize, JsonSchema)]
1022#[serde(deny_unknown_fields)]
1023pub struct SetAutofilterInput {
1024    pub workbook_id: String,
1025    pub sheet_name: String,
1026    /// Range in A1:B2 notation
1027    pub range: String,
1028}
1029
1030#[derive(Deserialize, JsonSchema)]
1031#[serde(deny_unknown_fields)]
1032pub struct FilterColumnInput {
1033    pub workbook_id: String,
1034    pub sheet_name: String,
1035    pub column: String,
1036    pub values: Vec<String>,
1037}
1038
1039#[derive(Deserialize, JsonSchema)]
1040#[serde(deny_unknown_fields)]
1041pub struct IgnoreErrorInput {
1042    pub workbook_id: String,
1043    pub sheet_name: String,
1044    /// Error type: "number_stored_as_text", "formula_range", etc.
1045    pub error_type: String,
1046    /// Range in A1:B2 notation
1047    pub range: String,
1048}
1049
1050#[derive(Deserialize, JsonSchema)]
1051#[serde(deny_unknown_fields)]
1052pub struct SetPageBreaksInput {
1053    pub workbook_id: String,
1054    pub sheet_name: String,
1055    #[serde(default)]
1056    pub row_breaks: Vec<u32>,
1057    #[serde(default)]
1058    pub col_breaks: Vec<u16>,
1059}
1060
1061#[derive(Deserialize, JsonSchema)]
1062#[serde(deny_unknown_fields)]
1063pub struct UnprotectRangeInput {
1064    pub workbook_id: String,
1065    pub sheet_name: String,
1066    pub range: String,
1067    pub title: String,
1068    #[serde(default)]
1069    pub password: Option<String>,
1070}
1071
1072#[derive(Deserialize, JsonSchema)]
1073#[serde(deny_unknown_fields)]
1074pub struct WriteFormulaInput {
1075    pub workbook_id: String,
1076    pub sheet_name: String,
1077    pub cell: String,
1078    pub formula: String,
1079    /// Optional cached numeric result
1080    #[serde(default)]
1081    pub cached_result: Option<f64>,
1082}
1083
1084#[derive(Deserialize, JsonSchema)]
1085#[serde(deny_unknown_fields)]
1086pub struct WriteArrayFormulaInput {
1087    pub workbook_id: String,
1088    pub sheet_name: String,
1089    /// Range the array formula spans (e.g. "A1:C3")
1090    pub range: String,
1091    pub formula: String,
1092}
1093
1094#[derive(Deserialize, JsonSchema)]
1095#[serde(deny_unknown_fields)]
1096pub struct WriteDynamicFormulaInput {
1097    pub workbook_id: String,
1098    pub sheet_name: String,
1099    pub cell: String,
1100    pub formula: String,
1101}
1102
1103#[derive(Deserialize, JsonSchema)]
1104#[serde(deny_unknown_fields)]
1105pub struct WriteBlankInput {
1106    pub workbook_id: String,
1107    pub sheet_name: String,
1108    pub cell: String,
1109    #[serde(default)]
1110    pub bold: Option<bool>,
1111    #[serde(default)]
1112    pub background_color: Option<String>,
1113    #[serde(default)]
1114    pub number_format: Option<String>,
1115}
1116
1117#[derive(Deserialize, JsonSchema)]
1118#[serde(deny_unknown_fields)]
1119pub struct ClearCellInput {
1120    pub workbook_id: String,
1121    pub sheet_name: String,
1122    pub cell: String,
1123}
1124
1125#[derive(Deserialize, JsonSchema)]
1126#[serde(deny_unknown_fields)]
1127pub struct SetCalcModeInput {
1128    pub workbook_id: String,
1129    /// "auto", "manual", or "auto_no_table"
1130    pub mode: String,
1131}
1132
1133#[derive(Deserialize, JsonSchema)]
1134#[serde(deny_unknown_fields)]
1135pub struct SetPropertiesInput {
1136    pub workbook_id: String,
1137    #[serde(default)]
1138    pub title: Option<String>,
1139    #[serde(default)]
1140    pub author: Option<String>,
1141    #[serde(default)]
1142    pub subject: Option<String>,
1143    #[serde(default)]
1144    pub company: Option<String>,
1145    #[serde(default)]
1146    pub description: Option<String>,
1147}
1148
1149#[derive(Deserialize, JsonSchema)]
1150#[serde(deny_unknown_fields)]
1151pub struct MoveWorksheetInput {
1152    pub workbook_id: String,
1153    pub sheet_name: String,
1154    /// 0-based target position
1155    pub to_index: usize,
1156}
1157
1158#[derive(Deserialize, JsonSchema)]
1159#[serde(deny_unknown_fields)]
1160pub struct WriteInternalLinkInput {
1161    pub workbook_id: String,
1162    pub sheet_name: String,
1163    pub cell: String,
1164    /// Internal location (e.g. "Sheet2!A1")
1165    pub location: String,
1166    pub display_text: String,
1167}
1168
1169// ══════════════════════════════════════════════════════════════════
1170// Consolidated input types (replacing multiple separate inputs)
1171// ══════════════════════════════════════════════════════════════════
1172
1173#[derive(Deserialize, JsonSchema)]
1174#[serde(deny_unknown_fields)]
1175pub struct ConfigureWorkbookInput {
1176    pub workbook_id: String,
1177    /// "auto", "manual", or "auto_no_table"
1178    #[serde(default)]
1179    pub calc_mode: Option<String>,
1180    /// 0-based sheet index to make active
1181    #[serde(default)]
1182    pub active_sheet: Option<usize>,
1183    #[serde(default)]
1184    pub title: Option<String>,
1185    #[serde(default)]
1186    pub author: Option<String>,
1187    #[serde(default)]
1188    pub subject: Option<String>,
1189    #[serde(default)]
1190    pub company: Option<String>,
1191    #[serde(default)]
1192    pub description: Option<String>,
1193}
1194
1195#[derive(Deserialize, JsonSchema)]
1196#[serde(deny_unknown_fields)]
1197pub struct ModifyRowsInput {
1198    pub workbook_id: String,
1199    pub sheet_name: String,
1200    /// "insert" or "delete"
1201    pub action: String,
1202    /// 1-based row number
1203    pub at_row: u32,
1204    pub count: u32,
1205}
1206
1207#[derive(Deserialize, JsonSchema)]
1208#[serde(deny_unknown_fields)]
1209pub struct ModifyColumnsInput {
1210    pub workbook_id: String,
1211    pub sheet_name: String,
1212    /// "insert" or "delete"
1213    pub action: String,
1214    pub at_column: String,
1215    pub count: u16,
1216}
1217
1218#[derive(Deserialize, JsonSchema)]
1219#[serde(deny_unknown_fields)]
1220pub struct WriteFormulaConsolidatedInput {
1221    pub workbook_id: String,
1222    pub sheet_name: String,
1223    /// Cell for regular/dynamic, or range for array (e.g. "A1" or "A1:C3")
1224    pub cell: String,
1225    pub formula: String,
1226    /// "regular" (default), "array", or "dynamic"
1227    #[serde(default)]
1228    pub formula_type: Option<String>,
1229    #[serde(default)]
1230    pub cached_result: Option<f64>,
1231}
1232
1233#[derive(Deserialize, JsonSchema)]
1234#[serde(deny_unknown_fields)]
1235pub struct ManageCellInput {
1236    pub workbook_id: String,
1237    pub sheet_name: String,
1238    pub cell: String,
1239    /// "blank" or "clear"
1240    pub action: String,
1241    #[serde(default)]
1242    pub background_color: Option<String>,
1243    #[serde(default)]
1244    pub number_format: Option<String>,
1245}
1246
1247#[derive(Deserialize, JsonSchema)]
1248#[serde(deny_unknown_fields)]
1249pub struct ManageCommentsInput {
1250    pub workbook_id: String,
1251    pub sheet_name: String,
1252    /// "add" or "read"
1253    pub action: String,
1254    #[serde(default)]
1255    pub cell: Option<String>,
1256    #[serde(default)]
1257    pub text: Option<String>,
1258    #[serde(default)]
1259    pub author: Option<String>,
1260}
1261
1262#[derive(Deserialize, JsonSchema)]
1263#[serde(deny_unknown_fields)]
1264pub struct ManageDefinedNamesInput {
1265    pub workbook_id: String,
1266    /// "add" or "list"
1267    pub action: String,
1268    #[serde(default)]
1269    pub name: Option<String>,
1270    #[serde(default)]
1271    pub formula: Option<String>,
1272}
1273
1274#[derive(Deserialize, JsonSchema)]
1275#[serde(deny_unknown_fields)]
1276pub struct AddLinkInput {
1277    pub workbook_id: String,
1278    pub sheet_name: String,
1279    pub cell: String,
1280    /// "url" or "internal"
1281    #[serde(default = "default_url")]
1282    pub link_type: String,
1283    /// URL for external, or "Sheet2!A1" for internal
1284    pub target: String,
1285    #[serde(default)]
1286    pub display_text: Option<String>,
1287    #[serde(default)]
1288    pub tooltip: Option<String>,
1289}
1290
1291fn default_url() -> String {
1292    "url".to_string()
1293}
1294
1295#[derive(Deserialize, JsonSchema)]
1296#[serde(deny_unknown_fields)]
1297pub struct ProtectInput {
1298    pub workbook_id: String,
1299    /// "sheet", "workbook", or "unprotect_range"
1300    pub target: String,
1301    #[serde(default)]
1302    pub sheet_name: Option<String>,
1303    #[serde(default)]
1304    pub password: Option<String>,
1305    /// For unprotect_range: the range to allow editing
1306    #[serde(default)]
1307    pub range: Option<String>,
1308    /// For unprotect_range: title for the range
1309    #[serde(default)]
1310    pub range_title: Option<String>,
1311}
1312
1313#[derive(Deserialize, JsonSchema)]
1314#[serde(deny_unknown_fields)]
1315pub struct SetDimensionsInput {
1316    pub workbook_id: String,
1317    pub sheet_name: String,
1318    /// "column_width", "row_height", "column_range_width", or "default_row_height"
1319    pub target: String,
1320    #[serde(default)]
1321    pub column: Option<String>,
1322    #[serde(default)]
1323    pub first_column: Option<String>,
1324    #[serde(default)]
1325    pub last_column: Option<String>,
1326    /// 1-based row number
1327    #[serde(default)]
1328    pub row: Option<u32>,
1329    pub value: f64,
1330}
1331
1332#[derive(Deserialize, JsonSchema)]
1333#[serde(deny_unknown_fields)]
1334pub struct SetVisibilityInput {
1335    pub workbook_id: String,
1336    pub sheet_name: String,
1337    /// "row" or "column"
1338    pub target: String,
1339    /// Column letter (for column) or 1-based row number as string (for row)
1340    pub identifier: String,
1341    pub hidden: bool,
1342}
1343
1344#[derive(Deserialize, JsonSchema)]
1345#[serde(deny_unknown_fields)]
1346pub struct SetRowColumnFormatInput {
1347    pub workbook_id: String,
1348    pub sheet_name: String,
1349    /// "row" or "column"
1350    pub target: String,
1351    /// Column letter or 1-based row number as string
1352    pub identifier: String,
1353    #[serde(default)]
1354    pub bold: Option<bool>,
1355    #[serde(default)]
1356    pub italic: Option<bool>,
1357    #[serde(default)]
1358    pub font_size: Option<f64>,
1359    #[serde(default)]
1360    pub font_color: Option<String>,
1361    #[serde(default)]
1362    pub background_color: Option<String>,
1363    #[serde(default)]
1364    pub number_format: Option<String>,
1365}
1366
1367#[derive(Deserialize, JsonSchema)]
1368#[serde(deny_unknown_fields)]
1369pub struct GroupInput {
1370    pub workbook_id: String,
1371    pub sheet_name: String,
1372    /// "rows" or "columns"
1373    pub target: String,
1374    /// For rows: 1-based row numbers. For columns: column letters.
1375    pub start: String,
1376    pub end: String,
1377    #[serde(default = "default_level")]
1378    pub level: u8,
1379}
1380
1381#[derive(Deserialize, JsonSchema)]
1382#[serde(deny_unknown_fields)]
1383pub struct ManageAutofilterInput {
1384    pub workbook_id: String,
1385    pub sheet_name: String,
1386    /// Range in A1:B2 notation (sets autofilter)
1387    pub range: String,
1388    /// Optional: column letter to filter
1389    #[serde(default)]
1390    pub filter_column: Option<String>,
1391    /// Optional: filter values for the column
1392    #[serde(default)]
1393    pub filter_values: Option<Vec<String>>,
1394}
1395
1396// ── Batch 9: Feature-parity tools ──────────────────────────────────
1397
1398/// A single data point for a waterfall chart
1399#[derive(Deserialize, JsonSchema)]
1400pub struct WaterfallPoint {
1401    pub category: String,
1402    pub value: f64,
1403    /// "increase", "decrease", or "total"
1404    pub point_type: WaterfallPointKind,
1405}
1406
1407/// Input for adding a waterfall chart (Excel 2016+ ChartEx format)
1408#[derive(Deserialize, JsonSchema)]
1409#[serde(deny_unknown_fields)]
1410pub struct AddWaterfallChartInput {
1411    pub workbook_id: String,
1412    pub sheet_name: String,
1413    #[serde(default)]
1414    pub title: Option<String>,
1415    #[serde(default)]
1416    pub series_name: Option<String>,
1417    /// Data points for the waterfall chart
1418    pub points: Vec<WaterfallPoint>,
1419    /// Chart width in pixels. Default: 480
1420    #[serde(default = "default_chart_width")]
1421    pub width: u32,
1422    /// Chart height in pixels. Default: 288
1423    #[serde(default = "default_chart_height")]
1424    pub height: u32,
1425    /// Anchor cell for the chart. Default: "A1"
1426    #[serde(default)]
1427    pub cell: Option<String>,
1428}
1429
1430/// A single data point for a funnel chart
1431#[derive(Deserialize, JsonSchema)]
1432pub struct FunnelPoint {
1433    pub category: String,
1434    pub value: f64,
1435}
1436
1437/// Input for adding a funnel chart (Excel 2016+ ChartEx format)
1438#[derive(Deserialize, JsonSchema)]
1439#[serde(deny_unknown_fields)]
1440pub struct AddFunnelChartInput {
1441    pub workbook_id: String,
1442    pub sheet_name: String,
1443    #[serde(default)]
1444    pub title: Option<String>,
1445    #[serde(default)]
1446    pub series_name: Option<String>,
1447    /// Data points for the funnel chart
1448    pub points: Vec<FunnelPoint>,
1449    /// Chart width in pixels. Default: 480
1450    #[serde(default = "default_chart_width")]
1451    pub width: u32,
1452    /// Chart height in pixels. Default: 288
1453    #[serde(default = "default_chart_height")]
1454    pub height: u32,
1455    /// Anchor cell for the chart. Default: "A1"
1456    #[serde(default)]
1457    pub cell: Option<String>,
1458}
1459
1460/// A single data point for a treemap chart
1461#[derive(Deserialize, JsonSchema)]
1462pub struct TreemapPoint {
1463    pub category: String,
1464    pub value: f64,
1465    /// Optional hex color (e.g. "#FF0000")
1466    #[serde(default)]
1467    pub color: Option<String>,
1468}
1469
1470/// Input for adding a treemap chart (Excel 2016+ ChartEx format)
1471#[derive(Deserialize, JsonSchema)]
1472#[serde(deny_unknown_fields)]
1473pub struct AddTreemapChartInput {
1474    pub workbook_id: String,
1475    pub sheet_name: String,
1476    #[serde(default)]
1477    pub title: Option<String>,
1478    #[serde(default)]
1479    pub series_name: Option<String>,
1480    /// Data points for the treemap chart
1481    pub points: Vec<TreemapPoint>,
1482    /// Chart width in pixels. Default: 480
1483    #[serde(default = "default_chart_width")]
1484    pub width: u32,
1485    /// Chart height in pixels. Default: 288
1486    #[serde(default = "default_chart_height")]
1487    pub height: u32,
1488    /// Anchor cell for the chart. Default: "A1"
1489    #[serde(default)]
1490    pub cell: Option<String>,
1491}
1492
1493/// Input for adding a drawing shape to a worksheet
1494#[derive(Deserialize, JsonSchema)]
1495#[serde(deny_unknown_fields)]
1496pub struct AddShapeInput {
1497    pub workbook_id: String,
1498    pub sheet_name: String,
1499    /// Anchor cell (e.g. "B2")
1500    pub cell: String,
1501    /// Shape type: rectangle, rounded_rectangle, ellipse, triangle, diamond, arrow, callout, text_box
1502    pub shape_type: ShapeKind,
1503    /// Width in pixels
1504    pub width: u32,
1505    /// Height in pixels
1506    pub height: u32,
1507    /// Optional text body for the shape
1508    #[serde(default)]
1509    pub text: Option<String>,
1510    /// Fill color as hex (e.g. "#FF0000")
1511    #[serde(default)]
1512    pub fill_color: Option<String>,
1513    /// Outline color as hex (e.g. "#000000")
1514    #[serde(default)]
1515    pub outline_color: Option<String>,
1516    /// Outline width in points
1517    #[serde(default)]
1518    pub outline_width: Option<f64>,
1519    /// Font size for the text body
1520    #[serde(default)]
1521    pub font_size: Option<f64>,
1522    /// Whether the text should be bold
1523    #[serde(default)]
1524    pub bold: Option<bool>,
1525}
1526
1527/// Input for setting document properties (core + app)
1528#[derive(Deserialize, JsonSchema)]
1529#[serde(deny_unknown_fields)]
1530pub struct SetDocPropertiesInput {
1531    pub workbook_id: String,
1532    #[serde(default)]
1533    pub title: Option<String>,
1534    #[serde(default)]
1535    pub author: Option<String>,
1536    #[serde(default)]
1537    pub subject: Option<String>,
1538    #[serde(default)]
1539    pub description: Option<String>,
1540    #[serde(default)]
1541    pub keywords: Option<String>,
1542    #[serde(default)]
1543    pub category: Option<String>,
1544    #[serde(default)]
1545    pub company: Option<String>,
1546}
1547
1548// ══════════════════════════════════════════════════════════════════
1549// v0.2.0: New tools — charts, pivots, controls, protection, save formats
1550// ══════════════════════════════════════════════════════════════════
1551
1552/// Input for adding a sunburst chart (Excel 2016+ ChartEx)
1553#[derive(Deserialize, JsonSchema)]
1554#[serde(deny_unknown_fields)]
1555pub struct AddSunburstChartInput {
1556    pub workbook_id: String,
1557    pub sheet_name: String,
1558    #[serde(default)]
1559    pub title: Option<String>,
1560    #[serde(default)]
1561    pub series_name: Option<String>,
1562    pub points: Vec<TreemapPoint>,
1563    #[serde(default = "default_chart_width")]
1564    pub width: u32,
1565    #[serde(default = "default_chart_height")]
1566    pub height: u32,
1567    #[serde(default)]
1568    pub cell: Option<String>,
1569}
1570
1571/// Input for adding a histogram chart (Excel 2016+ ChartEx)
1572#[derive(Deserialize, JsonSchema)]
1573#[serde(deny_unknown_fields)]
1574pub struct AddHistogramChartInput {
1575    pub workbook_id: String,
1576    pub sheet_name: String,
1577    #[serde(default)]
1578    pub title: Option<String>,
1579    #[serde(default)]
1580    pub series_name: Option<String>,
1581    pub points: Vec<FunnelPoint>,
1582    #[serde(default)]
1583    pub bin_count: Option<u32>,
1584    #[serde(default)]
1585    pub bin_width: Option<f64>,
1586    /// Show Pareto line overlay
1587    #[serde(default)]
1588    pub pareto: Option<bool>,
1589    #[serde(default = "default_chart_width")]
1590    pub width: u32,
1591    #[serde(default = "default_chart_height")]
1592    pub height: u32,
1593    #[serde(default)]
1594    pub cell: Option<String>,
1595}
1596
1597/// Input for adding a box & whisker chart (Excel 2016+ ChartEx)
1598#[derive(Deserialize, JsonSchema)]
1599#[serde(deny_unknown_fields)]
1600pub struct AddBoxWhiskerChartInput {
1601    pub workbook_id: String,
1602    pub sheet_name: String,
1603    #[serde(default)]
1604    pub title: Option<String>,
1605    #[serde(default)]
1606    pub series_name: Option<String>,
1607    pub points: Vec<FunnelPoint>,
1608    /// Show outlier points
1609    #[serde(default)]
1610    pub show_outliers: Option<bool>,
1611    /// Show mean markers
1612    #[serde(default)]
1613    pub show_mean: Option<bool>,
1614    /// Show inner data points
1615    #[serde(default)]
1616    pub show_inner_points: Option<bool>,
1617    #[serde(default = "default_chart_width")]
1618    pub width: u32,
1619    #[serde(default = "default_chart_height")]
1620    pub height: u32,
1621    #[serde(default)]
1622    pub cell: Option<String>,
1623}
1624
1625/// Input for adding a map chart (Excel 2016+ ChartEx)
1626#[derive(Deserialize, JsonSchema)]
1627#[serde(deny_unknown_fields)]
1628pub struct AddMapChartInput {
1629    pub workbook_id: String,
1630    pub sheet_name: String,
1631    #[serde(default)]
1632    pub title: Option<String>,
1633    #[serde(default)]
1634    pub series_name: Option<String>,
1635    pub points: Vec<FunnelPoint>,
1636    /// Map level: "world", "continent", "country", "state"
1637    #[serde(default)]
1638    pub map_level: Option<String>,
1639    #[serde(default = "default_chart_width")]
1640    pub width: u32,
1641    #[serde(default = "default_chart_height")]
1642    pub height: u32,
1643    #[serde(default)]
1644    pub cell: Option<String>,
1645}
1646
1647/// Input for adding a slicer to a pivot table
1648#[derive(Deserialize, JsonSchema)]
1649#[serde(deny_unknown_fields)]
1650pub struct AddSlicerInput {
1651    pub workbook_id: String,
1652    pub sheet_name: String,
1653    /// Name of the pivot table to connect to
1654    pub pivot_table_name: String,
1655    /// Field name to filter on
1656    pub field_name: String,
1657    /// Anchor cell for the slicer
1658    #[serde(default)]
1659    pub cell: Option<String>,
1660    #[serde(default)]
1661    pub width: Option<u32>,
1662    #[serde(default)]
1663    pub height: Option<u32>,
1664}
1665
1666/// Input for adding a timeline to a pivot table
1667#[derive(Deserialize, JsonSchema)]
1668#[serde(deny_unknown_fields)]
1669pub struct AddTimelineInput {
1670    pub workbook_id: String,
1671    pub sheet_name: String,
1672    /// Name of the pivot table to connect to
1673    pub pivot_table_name: String,
1674    /// Date field name
1675    pub field_name: String,
1676    /// Anchor cell for the timeline
1677    #[serde(default)]
1678    pub cell: Option<String>,
1679    #[serde(default)]
1680    pub width: Option<u32>,
1681    #[serde(default)]
1682    pub height: Option<u32>,
1683}
1684
1685/// Input for adding a form control (button, checkbox, spinner, dropdown)
1686#[derive(Deserialize, JsonSchema)]
1687#[serde(deny_unknown_fields)]
1688pub struct AddFormControlInput {
1689    pub workbook_id: String,
1690    pub sheet_name: String,
1691    /// Anchor cell
1692    pub cell: String,
1693    /// Control type: "button", "checkbox", "spinner", "dropdown", "radio", "scroll_bar", "group_box", "label"
1694    pub control_type: String,
1695    /// Display text/label
1696    #[serde(default)]
1697    pub text: Option<String>,
1698    /// Cell link for the control value
1699    #[serde(default)]
1700    pub cell_link: Option<String>,
1701    /// Input range (for dropdown/list controls)
1702    #[serde(default)]
1703    pub input_range: Option<String>,
1704    #[serde(default)]
1705    pub width: Option<u32>,
1706    #[serde(default)]
1707    pub height: Option<u32>,
1708}
1709
1710/// Input for saving in different formats
1711#[derive(Deserialize, JsonSchema)]
1712#[serde(deny_unknown_fields)]
1713pub struct SaveWorkbookAdvancedInput {
1714    pub workbook_id: String,
1715    pub file_path: String,
1716    /// Save format: "xlsx" (default), "xlsm", "template", "template_macro", "encrypted", "parallel"
1717    #[serde(default = "default_xlsx")]
1718    pub format: String,
1719    /// Password for encrypted save
1720    #[serde(default)]
1721    pub password: Option<String>,
1722}
1723
1724fn default_xlsx() -> String {
1725    "xlsx".to_string()
1726}
1727
1728/// Input for opening a password-protected workbook
1729#[derive(Deserialize, JsonSchema)]
1730#[serde(deny_unknown_fields)]
1731pub struct OpenWorkbookEncryptedInput {
1732    pub file_path: String,
1733    pub password: String,
1734}
1735
1736/// Input for managing named ranges with full CRUD and scoping
1737#[derive(Deserialize, JsonSchema)]
1738#[serde(deny_unknown_fields)]
1739pub struct ManageNamedRangesInput {
1740    pub workbook_id: String,
1741    /// "add", "update", "remove", "list", "add_scoped"
1742    pub action: String,
1743    #[serde(default)]
1744    pub name: Option<String>,
1745    #[serde(default)]
1746    pub formula: Option<String>,
1747    /// Sheet index for scoped names
1748    #[serde(default)]
1749    pub sheet_index: Option<usize>,
1750}
1751
1752/// Input for reading worksheet metadata (used_range, hyperlinks, merge_ranges, charts)
1753#[derive(Deserialize, JsonSchema)]
1754#[serde(deny_unknown_fields)]
1755pub struct ReadSheetMetadataInput {
1756    pub workbook_id: String,
1757    pub sheet_name: String,
1758    /// What to read: "used_range", "hyperlinks", "merge_ranges", "charts", "all"
1759    #[serde(default = "default_all")]
1760    pub info: String,
1761}
1762
1763fn default_all() -> String {
1764    "all".to_string()
1765}
1766
1767/// Input for adding a chart sheet (dedicated chart-only sheet)
1768#[derive(Deserialize, JsonSchema)]
1769#[serde(deny_unknown_fields)]
1770pub struct AddChartSheetInput {
1771    pub workbook_id: String,
1772    pub sheet_name: String,
1773    pub chart_type: ChartType,
1774    #[serde(default)]
1775    pub series: Vec<ChartSeriesInput>,
1776    #[serde(default)]
1777    pub data_range: Option<String>,
1778    #[serde(default)]
1779    pub title: Option<String>,
1780    #[serde(default)]
1781    pub x_axis_label: Option<String>,
1782    #[serde(default)]
1783    pub y_axis_label: Option<String>,
1784    #[serde(default)]
1785    pub legend_position: Option<LegendPosition>,
1786}
1787
1788/// Chart enhancement options (extend existing add_chart)
1789#[derive(Deserialize, JsonSchema)]
1790pub struct ChartEnhancements {
1791    /// Show data table below chart
1792    #[serde(default)]
1793    pub show_data_table: Option<bool>,
1794    /// 3D perspective rotation
1795    #[serde(default)]
1796    pub view_3d: Option<View3DInput>,
1797    /// Preset chart style number (1-48)
1798    #[serde(default)]
1799    pub style: Option<u8>,
1800    /// Accessibility alt text (title, description)
1801    #[serde(default)]
1802    pub alt_text: Option<AltTextInput>,
1803    /// Y-axis minimum value
1804    #[serde(default)]
1805    pub y_axis_min: Option<f64>,
1806    /// Y-axis maximum value
1807    #[serde(default)]
1808    pub y_axis_max: Option<f64>,
1809    /// Y-axis logarithmic base (e.g. 10)
1810    #[serde(default)]
1811    pub y_axis_log_base: Option<f64>,
1812    /// Reverse X axis
1813    #[serde(default)]
1814    pub x_axis_reverse: Option<bool>,
1815    /// Reverse Y axis
1816    #[serde(default)]
1817    pub y_axis_reverse: Option<bool>,
1818    /// X-axis number format
1819    #[serde(default)]
1820    pub x_axis_format: Option<String>,
1821    /// Y-axis number format
1822    #[serde(default)]
1823    pub y_axis_format: Option<String>,
1824    /// Show drop lines
1825    #[serde(default)]
1826    pub drop_lines: Option<bool>,
1827    /// Show high-low lines
1828    #[serde(default)]
1829    pub high_low_lines: Option<bool>,
1830    /// Plot area background fill color (hex)
1831    #[serde(default)]
1832    pub plot_area_fill: Option<String>,
1833}
1834
1835#[derive(Deserialize, JsonSchema)]
1836pub struct View3DInput {
1837    #[serde(default)]
1838    pub rot_x: Option<i16>,
1839    #[serde(default)]
1840    pub rot_y: Option<i16>,
1841    #[serde(default)]
1842    pub perspective: Option<u8>,
1843}
1844
1845#[derive(Deserialize, JsonSchema)]
1846pub struct AltTextInput {
1847    pub title: String,
1848    pub description: String,
1849}
1850
1851/// Enhanced chart series with additional options
1852#[derive(Deserialize, JsonSchema)]
1853pub struct ChartSeriesEnhanced {
1854    pub values: String,
1855    #[serde(default)]
1856    pub categories: Option<String>,
1857    #[serde(default)]
1858    pub name: Option<String>,
1859    #[serde(default)]
1860    pub color: Option<String>,
1861    #[serde(default)]
1862    pub data_labels: Option<bool>,
1863    #[serde(default)]
1864    pub trendline: Option<String>,
1865    #[serde(default)]
1866    pub marker: Option<String>,
1867    #[serde(default)]
1868    pub secondary_axis: Option<bool>,
1869    /// Line width in points
1870    #[serde(default)]
1871    pub line_width: Option<f64>,
1872    /// Dash style: "solid", "dash", "dot", "dash_dot"
1873    #[serde(default)]
1874    pub dash_style: Option<String>,
1875    /// Gradient stops: array of [color_hex, position_0_to_1]
1876    #[serde(default)]
1877    pub gradient: Option<Vec<GradientStopInput>>,
1878    /// Bubble sizes range (for bubble charts)
1879    #[serde(default)]
1880    pub bubble_sizes: Option<String>,
1881}
1882
1883#[derive(Deserialize, JsonSchema)]
1884pub struct GradientStopInput {
1885    pub color: String,
1886    pub position: f64,
1887}
1888
1889/// Sheet protection with granular options
1890#[derive(Deserialize, JsonSchema)]
1891pub struct SheetProtectionOptionsInput {
1892    #[serde(default)]
1893    pub password: Option<String>,
1894    #[serde(default)]
1895    pub insert_rows: Option<bool>,
1896    #[serde(default)]
1897    pub delete_rows: Option<bool>,
1898    #[serde(default)]
1899    pub insert_columns: Option<bool>,
1900    #[serde(default)]
1901    pub delete_columns: Option<bool>,
1902    #[serde(default)]
1903    pub format_cells: Option<bool>,
1904    #[serde(default)]
1905    pub format_columns: Option<bool>,
1906    #[serde(default)]
1907    pub format_rows: Option<bool>,
1908    #[serde(default)]
1909    pub sort: Option<bool>,
1910    #[serde(default)]
1911    pub insert_hyperlinks: Option<bool>,
1912    #[serde(default)]
1913    pub select_locked_cells: Option<bool>,
1914    #[serde(default)]
1915    pub select_unlocked_cells: Option<bool>,
1916    #[serde(default)]
1917    pub pivot_tables: Option<bool>,
1918}
1919
1920// ══════════════════════════════════════════════════════════════════
1921// v0.2.1: Chart enhancements, pivot enhancements, threaded comments,
1922// granular protection, custom properties
1923// ══════════════════════════════════════════════════════════════════
1924
1925/// Input for adding a threaded comment (modern Excel comments with replies)
1926#[derive(Deserialize, JsonSchema)]
1927#[serde(deny_unknown_fields)]
1928pub struct AddThreadedCommentInput {
1929    pub workbook_id: String,
1930    pub sheet_name: String,
1931    pub cell: String,
1932    pub author: String,
1933    pub text: String,
1934    #[serde(default)]
1935    pub timestamp: Option<String>,
1936    /// Optional replies to add immediately
1937    #[serde(default)]
1938    pub replies: Vec<ThreadedReplyInput>,
1939}
1940
1941#[derive(Deserialize, JsonSchema)]
1942pub struct ThreadedReplyInput {
1943    pub author: String,
1944    pub text: String,
1945    #[serde(default)]
1946    pub timestamp: Option<String>,
1947}
1948
1949/// Input for protecting a sheet with granular options
1950#[derive(Deserialize, JsonSchema)]
1951#[serde(deny_unknown_fields)]
1952pub struct ProtectSheetAdvancedInput {
1953    pub workbook_id: String,
1954    pub sheet_name: String,
1955    #[serde(default)]
1956    pub password: Option<String>,
1957    /// Allow inserting rows (default: locked)
1958    #[serde(default)]
1959    pub allow_insert_rows: Option<bool>,
1960    /// Allow deleting rows
1961    #[serde(default)]
1962    pub allow_delete_rows: Option<bool>,
1963    /// Allow inserting columns
1964    #[serde(default)]
1965    pub allow_insert_columns: Option<bool>,
1966    /// Allow deleting columns
1967    #[serde(default)]
1968    pub allow_delete_columns: Option<bool>,
1969    /// Allow formatting cells
1970    #[serde(default)]
1971    pub allow_format_cells: Option<bool>,
1972    /// Allow formatting columns
1973    #[serde(default)]
1974    pub allow_format_columns: Option<bool>,
1975    /// Allow formatting rows
1976    #[serde(default)]
1977    pub allow_format_rows: Option<bool>,
1978    /// Allow sorting
1979    #[serde(default)]
1980    pub allow_sort: Option<bool>,
1981    /// Allow inserting hyperlinks
1982    #[serde(default)]
1983    pub allow_insert_hyperlinks: Option<bool>,
1984    /// Allow selecting locked cells
1985    #[serde(default)]
1986    pub allow_select_locked_cells: Option<bool>,
1987    /// Allow selecting unlocked cells
1988    #[serde(default)]
1989    pub allow_select_unlocked_cells: Option<bool>,
1990    /// Allow pivot tables
1991    #[serde(default)]
1992    pub allow_pivot_tables: Option<bool>,
1993}
1994
1995/// Input for setting custom document properties
1996#[derive(Deserialize, JsonSchema)]
1997#[serde(deny_unknown_fields)]
1998pub struct SetCustomPropertyInput {
1999    pub workbook_id: String,
2000    pub name: String,
2001    /// Value to set. Use value_type to control the type.
2002    pub value: String,
2003    /// "text" (default), "number", "integer", "bool", "datetime"
2004    #[serde(default = "default_text")]
2005    pub value_type: String,
2006}
2007
2008fn default_text() -> String {
2009    "text".to_string()
2010}
2011
2012/// Pivot table enhancement fields
2013#[derive(Deserialize, JsonSchema)]
2014pub struct PivotCalculatedFieldInput {
2015    pub name: String,
2016    pub formula: String,
2017}
2018
2019#[derive(Deserialize, JsonSchema)]
2020pub struct PivotDateGroupInput {
2021    pub field: String,
2022    /// Levels: "years", "quarters", "months", "days", "hours", "minutes", "seconds"
2023    pub levels: Vec<String>,
2024}
2025
2026#[derive(Deserialize, JsonSchema)]
2027pub struct PivotRangeGroupInput {
2028    pub field: String,
2029    pub start: f64,
2030    pub end: f64,
2031    pub interval: f64,
2032}
2033
2034#[derive(Deserialize, JsonSchema)]
2035pub struct PivotValueFormatInput {
2036    pub field: String,
2037    pub format: String,
2038}
2039
2040#[derive(Deserialize, JsonSchema)]
2041pub struct PivotSubtotalToggle {
2042    pub field: String,
2043    pub show: bool,
2044}
2045
2046// ══════════════════════════════════════════════════════════════════
2047// v0.2.1 continued: remaining gap items
2048// ══════════════════════════════════════════════════════════════════
2049
2050/// Input for reading a single cell's comment
2051#[derive(Deserialize, JsonSchema)]
2052#[serde(deny_unknown_fields)]
2053pub struct ReadCellCommentInput {
2054    pub workbook_id: String,
2055    pub sheet_name: String,
2056    pub cell: String,
2057}
2058
2059/// Input for reading a cell's format
2060#[derive(Deserialize, JsonSchema)]
2061#[serde(deny_unknown_fields)]
2062pub struct ReadCellFormatInput {
2063    pub workbook_id: String,
2064    pub sheet_name: String,
2065    pub cell: String,
2066}
2067
2068/// Input for managing custom XML parts
2069#[derive(Deserialize, JsonSchema)]
2070#[serde(deny_unknown_fields)]
2071pub struct ManageCustomXmlInput {
2072    pub workbook_id: String,
2073    /// "add" or "read"
2074    pub action: String,
2075    /// XML namespace URI
2076    pub namespace: String,
2077    /// XML content (required for "add")
2078    #[serde(default)]
2079    pub content: Option<String>,
2080}
2081
2082/// Input for adding an external data connection
2083#[derive(Deserialize, JsonSchema)]
2084#[serde(deny_unknown_fields)]
2085pub struct AddConnectionInput {
2086    pub workbook_id: String,
2087    pub connection_string: String,
2088    pub command: String,
2089}
2090
2091/// Input for setting the shared string table threshold
2092#[derive(Deserialize, JsonSchema)]
2093#[serde(deny_unknown_fields)]
2094pub struct SetSstThresholdInput {
2095    pub workbook_id: String,
2096    /// Number of unique strings before using shared string table
2097    pub threshold: usize,
2098}
2099
2100/// Input for writing typed JSON data rows (serde-based)
2101#[derive(Deserialize, JsonSchema)]
2102#[serde(deny_unknown_fields)]
2103pub struct WriteJsonRowsInput {
2104    pub workbook_id: String,
2105    pub sheet_name: String,
2106    /// Starting cell (default "A1")
2107    #[serde(default)]
2108    pub start_cell: Option<String>,
2109    /// Whether first row of data contains headers to write
2110    #[serde(default = "default_true")]
2111    pub write_headers: bool,
2112    /// Array of objects — keys become headers, values become cells
2113    pub rows: Vec<serde_json::Value>,
2114}