Skip to main content

excel_mcp_server/tools/
write.rs

1use 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}