Skip to main content

spreadsheet_mcp/cli/commands/
write.rs

1use crate::core::types::CellEdit;
2use crate::model::Warning;
3use crate::runtime::stateless::StatelessRuntime;
4use anyhow::{Context, Result, bail};
5use serde::Serialize;
6use serde_json::Value;
7use std::path::PathBuf;
8
9#[derive(Debug, Serialize)]
10struct CopyResponse {
11    source: String,
12    dest: String,
13    bytes_copied: u64,
14}
15
16#[derive(Debug, Serialize)]
17struct EditResponse {
18    file: String,
19    sheet: String,
20    edits_applied: usize,
21    recalc_needed: bool,
22    warnings: Vec<Warning>,
23}
24
25pub async fn copy(source: PathBuf, dest: PathBuf) -> Result<Value> {
26    let runtime = StatelessRuntime;
27    let source = runtime.normalize_existing_file(&source)?;
28    let dest = runtime.normalize_destination_path(&dest)?;
29    let bytes_copied = runtime.copy_file(&source, &dest).with_context(|| {
30        format!(
31            "failed to copy workbook from '{}' to '{}'",
32            source.display(),
33            dest.display()
34        )
35    })?;
36
37    Ok(serde_json::to_value(CopyResponse {
38        source: source.display().to_string(),
39        dest: dest.display().to_string(),
40        bytes_copied,
41    })?)
42}
43
44pub async fn edit(file: PathBuf, sheet: String, edits: Vec<String>) -> Result<Value> {
45    if edits.is_empty() {
46        bail!("at least one edit must be provided");
47    }
48
49    let runtime = StatelessRuntime;
50    let file = runtime.normalize_existing_file(&file)?;
51
52    let mut normalized_edits = Vec::with_capacity(edits.len());
53    let mut warnings = Vec::new();
54    for (idx, entry) in edits.into_iter().enumerate() {
55        let (edit, entry_warnings) = crate::core::write::normalize_shorthand_edit(&entry)
56            .with_context(|| format!("invalid shorthand edit at index {}", idx))?;
57        normalized_edits.push(edit);
58        warnings.extend(entry_warnings.into_iter().map(|warning| Warning {
59            code: warning.code,
60            message: warning.message,
61        }));
62    }
63
64    runtime.apply_edits(&file, &sheet, &normalized_edits)?;
65
66    Ok(serde_json::to_value(EditResponse {
67        file: file.display().to_string(),
68        sheet,
69        edits_applied: normalized_edits.len(),
70        recalc_needed: true,
71        warnings,
72    })?)
73}
74
75pub fn parse_shorthand_for_tests(entries: Vec<String>) -> Result<(Vec<CellEdit>, Vec<Warning>)> {
76    let mut edits = Vec::with_capacity(entries.len());
77    let mut warnings = Vec::new();
78    for entry in entries {
79        let (edit, entry_warnings) = crate::core::write::normalize_shorthand_edit(&entry)?;
80        edits.push(edit);
81        warnings.extend(entry_warnings.into_iter().map(|warning| Warning {
82            code: warning.code,
83            message: warning.message,
84        }));
85    }
86    Ok((edits, warnings))
87}