Skip to main content

spreadsheet_kit/core/
recalc.rs

1#[cfg(feature = "recalc")]
2use crate::core::types::RecalculateOutcome;
3use anyhow::Result;
4#[cfg(feature = "recalc")]
5use anyhow::anyhow;
6#[cfg(not(feature = "recalc"))]
7use anyhow::bail;
8#[cfg(feature = "recalc")]
9use std::path::Path;
10#[cfg(feature = "recalc")]
11use std::sync::Arc;
12
13#[cfg(feature = "recalc")]
14pub async fn execute_with_backend(
15    path: &Path,
16    timeout_ms: Option<u64>,
17    backend: Arc<dyn crate::recalc::RecalcBackend>,
18) -> Result<RecalculateOutcome> {
19    let result = backend.recalculate(path, timeout_ms).await?;
20    Ok(RecalculateOutcome {
21        backend: result.backend_name.to_string(),
22        duration_ms: result.duration_ms,
23        cells_evaluated: result.cells_evaluated,
24        eval_errors: result.eval_errors,
25    })
26}
27
28#[cfg(feature = "recalc")]
29pub fn select_backend_from_env() -> Result<Arc<dyn crate::recalc::RecalcBackend>> {
30    use crate::config::RecalcBackendKind;
31    use crate::recalc::RecalcBackend;
32
33    #[cfg(feature = "recalc-formualizer")]
34    let formualizer: Option<Arc<dyn RecalcBackend>> =
35        Some(Arc::new(crate::recalc::FormualizerBackend));
36    #[cfg(not(feature = "recalc-formualizer"))]
37    let formualizer: Option<Arc<dyn RecalcBackend>> = None;
38
39    #[cfg(feature = "recalc-libreoffice")]
40    let libreoffice: Option<Arc<dyn RecalcBackend>> = {
41        let backend: Arc<dyn RecalcBackend> = Arc::new(crate::recalc::LibreOfficeBackend::new(
42            crate::recalc::RecalcConfig::default(),
43        ));
44        if backend.is_available() {
45            Some(backend)
46        } else {
47            None
48        }
49    };
50    #[cfg(not(feature = "recalc-libreoffice"))]
51    let libreoffice: Option<Arc<dyn RecalcBackend>> = None;
52
53    let requested = std::env::var("SPREADSHEET_MCP_RECALC_BACKEND")
54        .ok()
55        .and_then(|value| parse_recalc_backend_kind(&value))
56        .unwrap_or(RecalcBackendKind::Auto);
57
58    let selected = match requested {
59        RecalcBackendKind::Formualizer => formualizer,
60        RecalcBackendKind::Libreoffice => libreoffice,
61        RecalcBackendKind::Auto => formualizer.or(libreoffice),
62    };
63
64    selected.ok_or_else(|| {
65        anyhow!(
66            "no recalc backend available for this build/runtime; requested {:?}",
67            requested
68        )
69    })
70}
71
72#[cfg(feature = "recalc")]
73fn parse_recalc_backend_kind(value: &str) -> Option<crate::config::RecalcBackendKind> {
74    use crate::config::RecalcBackendKind;
75
76    match value.to_ascii_lowercase().as_str() {
77        "auto" => Some(RecalcBackendKind::Auto),
78        "formualizer" => Some(RecalcBackendKind::Formualizer),
79        "libreoffice" => Some(RecalcBackendKind::Libreoffice),
80        _ => None,
81    }
82}
83
84#[cfg(not(feature = "recalc"))]
85pub fn unavailable() -> Result<()> {
86    bail!(
87        "recalculate is not available in this build; rebuild with a recalc feature (e.g. --features recalc-formualizer)"
88    )
89}