Skip to main content

spreadsheet_mcp/
model.rs

1use crate::caps::BackendCaps;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, Default)]
7#[serde(transparent)]
8pub struct WorkbookId(pub String);
9
10impl WorkbookId {
11    pub fn as_str(&self) -> &str {
12        &self.0
13    }
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
17pub struct Warning {
18    pub code: String,
19    pub message: String,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
23pub struct WorkbookDescriptor {
24    pub workbook_id: WorkbookId,
25    pub short_id: String,
26    pub slug: String,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub folder: Option<String>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub path: Option<String>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub client_path: Option<String>,
33    pub bytes: u64,
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub last_modified: Option<String>,
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub revision_id: Option<String>,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub caps: Option<BackendCaps>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
43pub struct WorkbookListResponse {
44    pub workbooks: Vec<WorkbookDescriptor>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub next_offset: Option<u32>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
50pub struct WorkbookDescription {
51    pub workbook_id: WorkbookId,
52    pub short_id: String,
53    pub slug: String,
54    pub path: String,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub client_path: Option<String>,
57    pub bytes: u64,
58    pub sheet_count: usize,
59    pub defined_names: usize,
60    pub tables: usize,
61    pub macros_present: bool,
62    pub last_modified: Option<String>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub revision_id: Option<String>,
65    pub caps: BackendCaps,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
69pub struct WorkbookSummaryResponse {
70    pub workbook_id: WorkbookId,
71    pub workbook_short_id: String,
72    pub slug: String,
73    pub sheet_count: usize,
74    pub total_cells: u64,
75    pub total_formulas: u64,
76    pub breakdown: WorkbookBreakdown,
77    pub region_counts: RegionCountSummary,
78    #[serde(skip_serializing_if = "Vec::is_empty")]
79    pub key_named_ranges: Vec<NamedRangeDescriptor>,
80    #[serde(skip_serializing_if = "Vec::is_empty")]
81    pub suggested_entry_points: Vec<EntryPoint>,
82    #[serde(skip_serializing_if = "Vec::is_empty")]
83    pub notes: Vec<String>,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
87pub struct WorkbookBreakdown {
88    pub data_sheets: u32,
89    pub calculator_sheets: u32,
90    pub parameter_sheets: u32,
91    pub metadata_sheets: u32,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
95pub struct RegionCountSummary {
96    pub data: u32,
97    pub parameters: u32,
98    pub outputs: u32,
99    pub calculator: u32,
100    pub metadata: u32,
101    pub other: u32,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
105pub struct EntryPoint {
106    pub sheet_name: String,
107    pub region_id: Option<u32>,
108    pub bounds: Option<String>,
109    pub rationale: String,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
113pub struct SheetSummary {
114    pub name: String,
115    pub visible: bool,
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub row_count: Option<u32>,
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub column_count: Option<u32>,
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub non_empty_cells: Option<u32>,
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub formula_cells: Option<u32>,
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub cached_values: Option<u32>,
126    pub classification: SheetClassification,
127    #[serde(skip_serializing_if = "Vec::is_empty")]
128    pub style_tags: Vec<String>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
132#[serde(rename_all = "snake_case")]
133pub enum SheetClassification {
134    Data,
135    Calculator,
136    Mixed,
137    Metadata,
138    Empty,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
142pub struct SheetListResponse {
143    pub workbook_id: WorkbookId,
144    pub workbook_short_id: String,
145    pub sheets: Vec<SheetSummary>,
146    #[serde(skip_serializing_if = "Option::is_none")]
147    pub next_offset: Option<u32>,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
151pub struct SheetOverviewResponse {
152    pub workbook_id: WorkbookId,
153    pub workbook_short_id: String,
154    pub sheet_name: String,
155    pub narrative: String,
156    pub regions: Vec<SheetRegion>,
157    pub detected_regions: Vec<DetectedRegion>,
158    pub detected_region_count: u32,
159    pub detected_regions_truncated: bool,
160    pub key_ranges: Vec<String>,
161    pub formula_ratio: f32,
162    pub notable_features: Vec<String>,
163    pub notes: Vec<String>,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
167pub struct SheetRegion {
168    pub kind: RegionKind,
169    pub address: String,
170    pub description: String,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
174pub enum RegionKind {
175    #[serde(rename = "likely_table")]
176    Table,
177    #[serde(rename = "likely_data")]
178    Data,
179    #[serde(rename = "likely_parameters")]
180    Parameters,
181    #[serde(rename = "likely_outputs")]
182    Outputs,
183    #[serde(rename = "likely_calculator")]
184    Calculator,
185    #[serde(rename = "likely_metadata")]
186    Metadata,
187    #[serde(rename = "likely_styles")]
188    Styles,
189    #[serde(rename = "likely_comments")]
190    Comments,
191    #[serde(rename = "unknown")]
192    Other,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
196pub struct DetectedRegion {
197    pub id: u32,
198    pub bounds: String,
199    pub header_row: Option<u32>,
200    pub headers: Vec<String>,
201    pub header_count: u32,
202    pub headers_truncated: bool,
203    pub row_count: u32,
204    pub classification: RegionKind,
205    pub region_kind: Option<RegionKind>,
206    pub confidence: f32,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
210pub struct SheetPageResponse {
211    pub workbook_id: WorkbookId,
212    pub workbook_short_id: String,
213    pub sheet_name: String,
214    #[serde(skip_serializing_if = "Vec::is_empty")]
215    pub rows: Vec<RowSnapshot>,
216    #[serde(skip_serializing_if = "Option::is_none")]
217    pub next_start_row: Option<u32>,
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub header_row: Option<RowSnapshot>,
220    #[serde(skip_serializing_if = "Option::is_none")]
221    pub compact: Option<SheetPageCompact>,
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub values_only: Option<SheetPageValues>,
224    pub format: SheetPageFormat,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
228pub struct RowSnapshot {
229    pub row_index: u32,
230    pub cells: Vec<CellSnapshot>,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
234pub struct CellSnapshot {
235    pub address: String,
236    pub value: Option<CellValue>,
237    pub formula: Option<String>,
238    pub cached_value: Option<CellValue>,
239    pub number_format: Option<String>,
240    pub style_tags: Vec<String>,
241    pub notes: Vec<String>,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
245#[serde(tag = "kind", content = "value")]
246pub enum CellValue {
247    Text(String),
248    Number(f64),
249    Bool(bool),
250    Error(String),
251    Date(String),
252}
253
254#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
255#[serde(rename_all = "snake_case")]
256pub enum CellValueKind {
257    Text,
258    Number,
259    Bool,
260    Error,
261    Date,
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
265#[serde(untagged)]
266pub enum CellValuePrimitive {
267    Text(String),
268    Number(f64),
269    Bool(bool),
270}
271
272#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
273#[serde(rename_all = "snake_case")]
274pub enum TableOutputFormat {
275    Json,
276    Values,
277    Csv,
278}
279
280#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
281#[serde(rename_all = "snake_case")]
282pub enum SheetPageFormat {
283    #[default]
284    Full,
285    Compact,
286    ValuesOnly,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
290pub struct SheetPageCompact {
291    pub headers: Vec<String>,
292    pub header_row: Vec<Option<CellValue>>,
293    pub rows: Vec<Vec<Option<CellValue>>>,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
297pub struct SheetPageValues {
298    pub rows: Vec<Vec<Option<CellValue>>>,
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
302pub struct SheetStatisticsResponse {
303    pub workbook_id: WorkbookId,
304    pub workbook_short_id: String,
305    pub sheet_name: String,
306    pub row_count: u32,
307    pub column_count: u32,
308    pub density: f32,
309    #[serde(skip_serializing_if = "Vec::is_empty")]
310    pub numeric_columns: Vec<ColumnSummary>,
311    #[serde(skip_serializing_if = "Vec::is_empty")]
312    pub text_columns: Vec<ColumnSummary>,
313    pub null_counts: BTreeMap<String, u32>,
314    pub duplicate_warnings: Vec<String>,
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
318pub struct ColumnSummary {
319    pub header: Option<String>,
320    pub column: String,
321    #[serde(skip_serializing_if = "Vec::is_empty")]
322    pub samples: Vec<CellValue>,
323    pub min: Option<f64>,
324    pub max: Option<f64>,
325    pub mean: Option<f64>,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
329pub struct SheetFormulaMapResponse {
330    pub workbook_id: WorkbookId,
331    pub workbook_short_id: String,
332    pub sheet_name: String,
333    pub groups: Vec<FormulaGroup>,
334    #[serde(skip_serializing_if = "Option::is_none")]
335    pub next_offset: Option<u32>,
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
339pub struct FormulaGroup {
340    pub fingerprint: String,
341    #[serde(skip_serializing_if = "Vec::is_empty")]
342    pub addresses: Vec<String>,
343    #[serde(skip_serializing_if = "Option::is_none")]
344    pub count: Option<u32>,
345    pub formula: String,
346    pub is_array: bool,
347    pub is_shared: bool,
348    pub is_volatile: bool,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
352pub struct FormulaTraceResponse {
353    pub workbook_id: WorkbookId,
354    pub workbook_short_id: String,
355    pub sheet_name: String,
356    pub origin: String,
357    pub direction: TraceDirection,
358    pub layers: Vec<TraceLayer>,
359    pub next_cursor: Option<TraceCursor>,
360    pub notes: Vec<String>,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
364pub struct FormulaTraceEdge {
365    pub from: String,
366    pub to: String,
367    pub formula: Option<String>,
368    pub note: Option<String>,
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
372pub struct TraceLayer {
373    pub depth: u32,
374    pub summary: TraceLayerSummary,
375    pub highlights: TraceLayerHighlights,
376    pub edges: Vec<FormulaTraceEdge>,
377    pub has_more: bool,
378}
379
380#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
381pub struct TraceLayerSummary {
382    pub total_nodes: usize,
383    pub formula_nodes: usize,
384    pub value_nodes: usize,
385    pub blank_nodes: usize,
386    pub external_nodes: usize,
387    pub unique_formula_groups: usize,
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
391pub struct TraceLayerHighlights {
392    pub top_ranges: Vec<TraceRangeHighlight>,
393    pub top_formula_groups: Vec<TraceFormulaGroupHighlight>,
394    pub notable_cells: Vec<TraceCellHighlight>,
395}
396
397#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
398pub struct TraceRangeHighlight {
399    pub start: String,
400    pub end: String,
401    pub count: usize,
402    pub literals: usize,
403    pub formulas: usize,
404    pub blanks: usize,
405    pub sample_values: Vec<CellValue>,
406    pub sample_formulas: Vec<String>,
407    pub sample_addresses: Vec<String>,
408}
409
410#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
411pub struct TraceFormulaGroupHighlight {
412    pub fingerprint: String,
413    pub formula: String,
414    pub count: usize,
415    pub sample_addresses: Vec<String>,
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
419pub struct TraceCellHighlight {
420    pub address: String,
421    pub kind: TraceCellKind,
422    pub value: Option<CellValue>,
423    pub formula: Option<String>,
424}
425
426#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
427#[serde(rename_all = "snake_case")]
428pub enum TraceCellKind {
429    Formula,
430    Literal,
431    Blank,
432    External,
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
436pub struct TraceCursor {
437    pub depth: u32,
438    pub offset: usize,
439}
440
441#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
442#[serde(rename_all = "snake_case")]
443pub enum TraceDirection {
444    Precedents,
445    Dependents,
446}
447
448#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
449pub struct NamedRangeDescriptor {
450    pub name: String,
451    pub scope: Option<String>,
452    pub refers_to: String,
453    pub kind: NamedItemKind,
454    pub sheet_name: Option<String>,
455    pub comment: Option<String>,
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
459#[serde(rename_all = "snake_case")]
460pub enum NamedItemKind {
461    NamedRange,
462    Table,
463    Formula,
464    Unknown,
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
468pub struct NamedRangesResponse {
469    pub workbook_id: WorkbookId,
470    pub workbook_short_id: String,
471    pub items: Vec<NamedRangeDescriptor>,
472}
473
474#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
475pub struct FindFormulaMatch {
476    pub address: String,
477    pub sheet_name: String,
478    pub formula: String,
479    pub cached_value: Option<CellValue>,
480    pub context: Vec<RowSnapshot>,
481}
482
483#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
484pub struct FindFormulaResponse {
485    pub workbook_id: WorkbookId,
486    pub workbook_short_id: String,
487    pub matches: Vec<FindFormulaMatch>,
488    #[serde(skip_serializing_if = "Option::is_none")]
489    pub next_offset: Option<u32>,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
493pub struct VolatileScanEntry {
494    pub address: String,
495    pub sheet_name: String,
496    pub function: String,
497    pub note: Option<String>,
498}
499
500#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
501pub struct VolatileScanResponse {
502    pub workbook_id: WorkbookId,
503    pub workbook_short_id: String,
504    pub items: Vec<VolatileScanEntry>,
505    #[serde(skip_serializing_if = "Option::is_none")]
506    pub next_offset: Option<u32>,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
510pub struct StyleDescriptor {
511    pub font: Option<FontDescriptor>,
512    pub fill: Option<FillDescriptor>,
513    pub borders: Option<BordersDescriptor>,
514    pub alignment: Option<AlignmentDescriptor>,
515    pub number_format: Option<String>,
516}
517
518#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
519pub struct FontDescriptor {
520    pub name: Option<String>,
521    pub size: Option<f64>,
522    pub bold: Option<bool>,
523    pub italic: Option<bool>,
524    pub underline: Option<String>,
525    pub strikethrough: Option<bool>,
526    pub color: Option<String>,
527}
528
529#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
530#[serde(tag = "kind", rename_all = "snake_case")]
531pub enum FillDescriptor {
532    Pattern(PatternFillDescriptor),
533    Gradient(GradientFillDescriptor),
534}
535
536#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
537pub struct PatternFillDescriptor {
538    pub pattern_type: Option<String>,
539    pub foreground_color: Option<String>,
540    pub background_color: Option<String>,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
544pub struct GradientFillDescriptor {
545    pub degree: Option<f64>,
546    pub stops: Vec<GradientStopDescriptor>,
547}
548
549#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
550pub struct GradientStopDescriptor {
551    pub position: f64,
552    pub color: String,
553}
554
555#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
556pub struct BordersDescriptor {
557    pub left: Option<BorderSideDescriptor>,
558    pub right: Option<BorderSideDescriptor>,
559    pub top: Option<BorderSideDescriptor>,
560    pub bottom: Option<BorderSideDescriptor>,
561    pub diagonal: Option<BorderSideDescriptor>,
562    pub vertical: Option<BorderSideDescriptor>,
563    pub horizontal: Option<BorderSideDescriptor>,
564    pub diagonal_up: Option<bool>,
565    pub diagonal_down: Option<bool>,
566}
567
568#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
569pub struct BorderSideDescriptor {
570    pub style: Option<String>,
571    pub color: Option<String>,
572}
573
574#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
575pub struct AlignmentDescriptor {
576    pub horizontal: Option<String>,
577    pub vertical: Option<String>,
578    pub wrap_text: Option<bool>,
579    pub text_rotation: Option<u32>,
580}
581
582// Patch variants for write tools (Phase 2+). Double-option fields distinguish:
583// - missing field => no change (merge mode)
584// - null => clear to default
585// - value => set/merge that value
586#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
587pub struct StylePatch {
588    #[serde(default)]
589    pub font: Option<Option<FontPatch>>,
590    #[serde(default)]
591    pub fill: Option<Option<FillPatch>>,
592    #[serde(default)]
593    pub borders: Option<Option<BordersPatch>>,
594    #[serde(default)]
595    pub alignment: Option<Option<AlignmentPatch>>,
596    #[serde(default)]
597    pub number_format: Option<Option<String>>,
598}
599
600#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
601pub struct FontPatch {
602    #[serde(default)]
603    pub name: Option<Option<String>>,
604    #[serde(default)]
605    pub size: Option<Option<f64>>,
606    #[serde(default)]
607    pub bold: Option<Option<bool>>,
608    #[serde(default)]
609    pub italic: Option<Option<bool>>,
610    #[serde(default)]
611    pub underline: Option<Option<String>>,
612    #[serde(default)]
613    pub strikethrough: Option<Option<bool>>,
614    #[serde(default)]
615    pub color: Option<Option<String>>,
616}
617
618#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
619#[serde(tag = "kind", rename_all = "snake_case")]
620pub enum FillPatch {
621    Pattern(PatternFillPatch),
622    Gradient(GradientFillPatch),
623}
624
625#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
626pub struct PatternFillPatch {
627    #[serde(default)]
628    pub pattern_type: Option<Option<String>>,
629    #[serde(default)]
630    pub foreground_color: Option<Option<String>>,
631    #[serde(default)]
632    pub background_color: Option<Option<String>>,
633}
634
635#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
636pub struct GradientFillPatch {
637    #[serde(default)]
638    pub degree: Option<Option<f64>>,
639    #[serde(default)]
640    pub stops: Option<Vec<GradientStopPatch>>,
641}
642
643#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
644pub struct GradientStopPatch {
645    pub position: f64,
646    pub color: String,
647}
648
649#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
650pub struct BordersPatch {
651    #[serde(default)]
652    pub left: Option<Option<BorderSidePatch>>,
653    #[serde(default)]
654    pub right: Option<Option<BorderSidePatch>>,
655    #[serde(default)]
656    pub top: Option<Option<BorderSidePatch>>,
657    #[serde(default)]
658    pub bottom: Option<Option<BorderSidePatch>>,
659    #[serde(default)]
660    pub diagonal: Option<Option<BorderSidePatch>>,
661    #[serde(default)]
662    pub vertical: Option<Option<BorderSidePatch>>,
663    #[serde(default)]
664    pub horizontal: Option<Option<BorderSidePatch>>,
665    #[serde(default)]
666    pub diagonal_up: Option<Option<bool>>,
667    #[serde(default)]
668    pub diagonal_down: Option<Option<bool>>,
669}
670
671#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
672pub struct BorderSidePatch {
673    #[serde(default)]
674    pub style: Option<Option<String>>,
675    #[serde(default)]
676    pub color: Option<Option<String>>,
677}
678
679#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
680pub struct AlignmentPatch {
681    #[serde(default)]
682    pub horizontal: Option<Option<String>>,
683    #[serde(default)]
684    pub vertical: Option<Option<String>>,
685    #[serde(default)]
686    pub wrap_text: Option<Option<bool>>,
687    #[serde(default)]
688    pub text_rotation: Option<Option<u32>>,
689}
690
691#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
692pub struct SheetStylesResponse {
693    pub workbook_id: WorkbookId,
694    pub workbook_short_id: String,
695    pub sheet_name: String,
696    pub styles: Vec<StyleSummary>,
697    #[serde(skip_serializing_if = "Vec::is_empty")]
698    pub conditional_rules: Vec<String>,
699    pub total_styles: u32,
700    pub styles_truncated: bool,
701}
702
703#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
704pub struct StyleSummary {
705    pub style_id: String,
706    pub occurrences: u32,
707    pub tags: Vec<String>,
708    #[serde(skip_serializing_if = "Vec::is_empty")]
709    pub example_cells: Vec<String>,
710    pub descriptor: Option<StyleDescriptor>,
711    #[serde(skip_serializing_if = "Vec::is_empty")]
712    pub cell_ranges: Vec<String>,
713    pub ranges_truncated: bool,
714}
715
716#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
717pub struct WorkbookStyleSummaryResponse {
718    pub workbook_id: WorkbookId,
719    pub workbook_short_id: String,
720    #[serde(skip_serializing_if = "Option::is_none")]
721    pub theme: Option<ThemeSummary>,
722    #[serde(skip_serializing_if = "Option::is_none")]
723    pub inferred_default_style_id: Option<String>,
724    #[serde(skip_serializing_if = "Option::is_none")]
725    pub inferred_default_font: Option<FontDescriptor>,
726    pub styles: Vec<WorkbookStyleUsage>,
727    pub total_styles: u32,
728    pub styles_truncated: bool,
729    #[serde(skip_serializing_if = "Vec::is_empty")]
730    pub conditional_formats: Vec<ConditionalFormatSummary>,
731    pub conditional_formats_truncated: bool,
732    pub scan_truncated: bool,
733    #[serde(skip_serializing_if = "Vec::is_empty")]
734    pub notes: Vec<String>,
735}
736
737#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
738pub struct WorkbookStyleUsage {
739    pub style_id: String,
740    pub occurrences: u32,
741    pub tags: Vec<String>,
742    #[serde(skip_serializing_if = "Vec::is_empty")]
743    pub example_cells: Vec<String>,
744    pub descriptor: Option<StyleDescriptor>,
745}
746
747#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
748pub struct ThemeSummary {
749    pub name: Option<String>,
750    pub colors: BTreeMap<String, String>,
751    pub font_scheme: ThemeFontSchemeSummary,
752}
753
754#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
755pub struct ThemeFontSchemeSummary {
756    pub major_latin: Option<String>,
757    pub major_east_asian: Option<String>,
758    pub major_complex_script: Option<String>,
759    pub minor_latin: Option<String>,
760    pub minor_east_asian: Option<String>,
761    pub minor_complex_script: Option<String>,
762}
763
764#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
765pub struct ConditionalFormatSummary {
766    pub sheet_name: String,
767    pub range: String,
768    pub rule_types: Vec<String>,
769    pub rule_count: u32,
770}
771
772#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
773pub struct ManifestStubResponse {
774    pub workbook_id: WorkbookId,
775    pub workbook_short_id: String,
776    pub slug: String,
777    pub sheets: Vec<ManifestSheetStub>,
778}
779
780#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
781pub struct ManifestSheetStub {
782    pub sheet_name: String,
783    pub classification: SheetClassification,
784    pub candidate_expectations: Vec<String>,
785    pub notes: Vec<String>,
786}
787
788#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
789#[serde(rename_all = "snake_case")]
790pub enum FindMode {
791    #[default]
792    Value,
793    Label,
794}
795
796#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
797#[serde(rename_all = "snake_case")]
798pub enum LabelDirection {
799    Right,
800    Below,
801    Any,
802}
803
804#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
805pub struct FindValueMatch {
806    pub address: String,
807    pub sheet_name: String,
808    pub value: Option<CellValue>,
809    pub row_context: Option<RowContext>,
810    pub neighbors: Option<NeighborValues>,
811    pub label_hit: Option<LabelHit>,
812}
813
814#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
815pub struct RowContext {
816    pub headers: Vec<String>,
817    pub values: Vec<Option<CellValue>>,
818}
819
820#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
821pub struct NeighborValues {
822    pub left: Option<CellValue>,
823    pub right: Option<CellValue>,
824    pub up: Option<CellValue>,
825    pub down: Option<CellValue>,
826}
827
828#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
829pub struct LabelHit {
830    pub label_address: String,
831    pub label: String,
832}
833
834#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
835pub struct FindValueResponse {
836    pub workbook_id: WorkbookId,
837    pub workbook_short_id: String,
838    pub matches: Vec<FindValueMatch>,
839    #[serde(skip_serializing_if = "Option::is_none")]
840    pub next_offset: Option<u32>,
841}
842
843pub type TableRow = BTreeMap<String, Option<CellValue>>;
844
845#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
846pub struct ReadTableResponse {
847    pub workbook_id: WorkbookId,
848    pub workbook_short_id: String,
849    pub sheet_name: String,
850    pub table_name: Option<String>,
851    #[serde(skip_serializing_if = "Vec::is_empty")]
852    pub warnings: Vec<Warning>,
853    #[serde(skip_serializing_if = "Vec::is_empty")]
854    pub headers: Vec<String>,
855    #[serde(skip_serializing_if = "Vec::is_empty")]
856    pub rows: Vec<TableRow>,
857    #[serde(skip_serializing_if = "Option::is_none")]
858    pub values: Option<Vec<Vec<Option<CellValuePrimitive>>>>,
859    #[serde(skip_serializing_if = "Option::is_none")]
860    pub types: Option<Vec<Vec<Option<CellValueKind>>>>,
861    #[serde(skip_serializing_if = "Option::is_none")]
862    pub csv: Option<String>,
863    pub total_rows: u32,
864    #[serde(skip_serializing_if = "Option::is_none")]
865    pub next_offset: Option<u32>,
866}
867
868#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
869pub struct ColumnTypeSummary {
870    pub name: String,
871    pub inferred_type: String,
872    pub nulls: u32,
873    pub distinct: u32,
874    pub top_values: Vec<String>,
875    pub min: Option<f64>,
876    pub max: Option<f64>,
877    pub mean: Option<f64>,
878}
879
880#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
881pub struct TableProfileResponse {
882    pub workbook_id: WorkbookId,
883    pub workbook_short_id: String,
884    pub sheet_name: String,
885    pub table_name: Option<String>,
886    pub headers: Vec<String>,
887    pub column_types: Vec<ColumnTypeSummary>,
888    pub row_count: u32,
889    #[serde(skip_serializing_if = "Vec::is_empty")]
890    pub samples: Vec<TableRow>,
891    #[serde(skip_serializing_if = "Vec::is_empty")]
892    pub notes: Vec<String>,
893}
894
895#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
896pub struct RangeValuesResponse {
897    pub workbook_id: WorkbookId,
898    pub workbook_short_id: String,
899    pub sheet_name: String,
900    #[serde(skip_serializing_if = "Vec::is_empty")]
901    pub warnings: Vec<Warning>,
902    pub values: Vec<RangeValuesEntry>,
903}
904
905#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
906pub struct RangeValuesEntry {
907    pub range: String,
908    #[serde(skip_serializing_if = "Option::is_none")]
909    pub rows: Option<Vec<Vec<Option<CellValue>>>>,
910    #[serde(skip_serializing_if = "Option::is_none")]
911    pub values: Option<Vec<Vec<Option<CellValuePrimitive>>>>,
912    #[serde(skip_serializing_if = "Option::is_none")]
913    pub csv: Option<String>,
914    #[serde(skip_serializing_if = "Option::is_none")]
915    pub next_start_row: Option<u32>,
916}
917
918#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
919pub struct CloseWorkbookResponse {
920    pub workbook_id: WorkbookId,
921    pub message: String,
922}
923
924#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
925pub struct VbaProjectSummaryResponse {
926    pub workbook_id: WorkbookId,
927    pub workbook_short_id: String,
928    pub has_vba: bool,
929    pub code_page: Option<u16>,
930    pub sys_kind: Option<String>,
931    pub modules: Vec<VbaModuleDescriptor>,
932    pub modules_truncated: bool,
933    pub references: Vec<VbaReferenceDescriptor>,
934    pub references_truncated: bool,
935    pub notes: Vec<String>,
936}
937
938#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
939pub struct VbaModuleDescriptor {
940    pub name: String,
941    pub stream_name: String,
942    pub doc_string: String,
943    pub text_offset: u64,
944    pub help_context: u32,
945    pub module_type: String,
946    pub read_only: bool,
947    pub private: bool,
948}
949
950#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
951pub struct VbaReferenceDescriptor {
952    pub kind: String,
953    pub debug: String,
954}
955
956#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
957pub struct VbaModuleSourceResponse {
958    pub workbook_id: WorkbookId,
959    pub workbook_short_id: String,
960    pub module_name: String,
961    pub offset_lines: u32,
962    pub limit_lines: u32,
963    pub total_lines: u32,
964    pub truncated: bool,
965    pub source: String,
966}