excel_mcp_server/tools/
write.rs1use super::common::workbook_not_found;
2use crate::engines::zavora;
3use crate::store::WorkbookStore;
4use crate::types::inputs::*;
5use crate::types::responses::*;
6
7pub fn write_cells(
8 store: &mut WorkbookStore,
9 input: WriteCellsInput,
10) -> Result<String, anyhow::Error> {
11 let entry = match store.get_mut(&input.workbook_id) {
12 Some(e) => e,
13 None => return Ok(workbook_not_found(store, &input.workbook_id)),
14 };
15 let idx = match find_sheet(&entry.data, &input.sheet_name) {
16 Some(i) => i,
17 None => return Ok(sheet_not_found(&input.sheet_name)),
18 };
19 let ws = entry
20 .data
21 .worksheet(idx)
22 .map_err(|e| anyhow::anyhow!("{e}"))?;
23 let mut count = 0usize;
24 for cw in &input.cells {
25 let (row, col) = match zavora_xlsx::utility::parse_cell_ref(&cw.cell) {
26 Ok(rc) => rc,
27 Err(e) => {
28 return Ok(error(
29 ErrorCategory::ParseError,
30 &format!("Invalid cell reference '{}': {e}", cw.cell),
31 "Use A1 notation.",
32 ))
33 }
34 };
35 if let Err(e) = zavora::write_json_value(ws, row, col, &cw.value) {
36 return Ok(error(
37 ErrorCategory::IoError,
38 &format!("Write error: {e}"),
39 "Check value type.",
40 ));
41 }
42 count += 1;
43 }
44 Ok(success(
45 "Cells written",
46 WriteResult {
47 cells_written: count,
48 range_covered: format!("{} cells", count),
49 },
50 ))
51}
52
53pub fn write_row(store: &mut WorkbookStore, input: WriteRowInput) -> Result<String, anyhow::Error> {
54 let entry = match store.get_mut(&input.workbook_id) {
55 Some(e) => e,
56 None => return Ok(workbook_not_found(store, &input.workbook_id)),
57 };
58 let idx = match find_sheet(&entry.data, &input.sheet_name) {
59 Some(i) => i,
60 None => return Ok(sheet_not_found(&input.sheet_name)),
61 };
62 let (row, start_col) = zavora_xlsx::utility::parse_cell_ref(&input.start_cell)
63 .map_err(|e| anyhow::anyhow!("{e}"))?;
64 let ws = entry
65 .data
66 .worksheet(idx)
67 .map_err(|e| anyhow::anyhow!("{e}"))?;
68 for (i, val) in input.values.iter().enumerate() {
69 zavora::write_json_value(ws, row, start_col + i as u16, val)
70 .map_err(|e| anyhow::anyhow!("{e}"))?;
71 }
72 let end = zavora_xlsx::utility::to_a1(row, start_col + input.values.len() as u16 - 1);
73 Ok(success(
74 "Row written",
75 WriteResult {
76 cells_written: input.values.len(),
77 range_covered: format!("{}:{}", input.start_cell, end),
78 },
79 ))
80}
81
82pub fn write_column(
83 store: &mut WorkbookStore,
84 input: WriteColumnInput,
85) -> Result<String, anyhow::Error> {
86 let entry = match store.get_mut(&input.workbook_id) {
87 Some(e) => e,
88 None => return Ok(workbook_not_found(store, &input.workbook_id)),
89 };
90 let idx = match find_sheet(&entry.data, &input.sheet_name) {
91 Some(i) => i,
92 None => return Ok(sheet_not_found(&input.sheet_name)),
93 };
94 let (start_row, col) = zavora_xlsx::utility::parse_cell_ref(&input.start_cell)
95 .map_err(|e| anyhow::anyhow!("{e}"))?;
96 let ws = entry
97 .data
98 .worksheet(idx)
99 .map_err(|e| anyhow::anyhow!("{e}"))?;
100 for (i, val) in input.values.iter().enumerate() {
101 zavora::write_json_value(ws, start_row + i as u32, col, val)
102 .map_err(|e| anyhow::anyhow!("{e}"))?;
103 }
104 let end = zavora_xlsx::utility::to_a1(start_row + input.values.len() as u32 - 1, col);
105 Ok(success(
106 "Column written",
107 WriteResult {
108 cells_written: input.values.len(),
109 range_covered: format!("{}:{}", input.start_cell, end),
110 },
111 ))
112}
113
114fn find_sheet(wb: &zavora_xlsx::Workbook, name: &str) -> Option<usize> {
115 wb.sheet_names().iter().position(|n| *n == name)
116}
117
118fn sheet_not_found(name: &str) -> String {
119 error(
120 ErrorCategory::NotFound,
121 &format!("Sheet '{}' not found", name),
122 "Check sheet name.",
123 )
124}