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