Skip to main content

memstead_mcp/
read_mems.rs

1//! Batch-install helper for the `--read-mem` CLI flag.
2//!
3//! Wraps [`memstead_git_branch::mem_cache::install_to_cache`] +
4//! [`memstead_git_branch::mem_cache::register_cached_archive`] in a
5//! small loop so the binary entry point stays thin and integration
6//! tests can drive the behavior without spawning the MCP server. Each
7//! archive lands in the global cache and registers as a
8//! **workspace-level read-only mount** — the same model `memstead
9//! install` produces; no writable mem's config is touched.
10
11use std::path::{Path, PathBuf};
12
13use memstead_git_branch::mem_cache::{self, CacheInstallOutcome, InstallError, MountRegistration};
14
15/// Outcome of processing a single `--read-mem` argument.
16///
17/// Owning enum instead of `Result` so callers can iterate the full batch
18/// and decide per entry how loudly to surface it — the binary warn-logs,
19/// tests inspect structure.
20#[derive(Debug)]
21pub enum ReadMemResult {
22    /// Validator accepted the archive; it is cached and mounted
23    /// (either or both may have been no-ops for already-present
24    /// content).
25    Installed {
26        archive: PathBuf,
27        outcome: CacheInstallOutcome,
28        mount: MountRegistration,
29    },
30    /// Validation, cache I/O, or mount registration failed. The error
31    /// `Display` preserves path + reason, so a warn log over the
32    /// value is actionable without unwrapping the variant.
33    Failed { archive: PathBuf, error: String },
34}
35
36/// Install every `--read-mem` archive as a workspace-level read-only
37/// mount, one by one, collecting per-archive outcomes.
38///
39/// **Warn-and-continue semantics.** A malformed archive does not abort
40/// the batch — the caller receives a `Failed` entry and keeps going.
41/// The write mem stays useful on its own; tearing the server down
42/// over one bad `--read-mem` is worse DX than a visible warning plus
43/// a running server.
44///
45/// Relative `archive` paths resolve against `cwd`. The caller persists
46/// the mount state once after the batch (`engine.persist_state()`)
47/// when any entry reports a `Registered` / `Refreshed` mount.
48pub fn install_read_mems(
49    engine: &mut memstead_base::Engine,
50    archives: &[PathBuf],
51    cwd: &Path,
52) -> Vec<ReadMemResult> {
53    let writable: Vec<String> = engine
54        .mem_router()
55        .writable_mems()
56        .iter()
57        .map(|n| n.to_string())
58        .collect();
59
60    archives
61        .iter()
62        .map(|archive| {
63            let archive = if archive.is_absolute() {
64                archive.clone()
65            } else {
66                cwd.join(archive)
67            };
68            let writable_refs: Vec<&str> = writable.iter().map(String::as_str).collect();
69            let outcome = match mem_cache::install_to_cache(&archive, &writable_refs) {
70                Ok(o) => o,
71                Err(e) => {
72                    return ReadMemResult::Failed {
73                        archive,
74                        error: e.to_string(),
75                    };
76                }
77            };
78            match mem_cache::register_cached_archive(engine, &outcome, "--read-mem") {
79                Ok(mount) => ReadMemResult::Installed {
80                    archive,
81                    outcome,
82                    mount,
83                },
84                Err(e) => ReadMemResult::Failed {
85                    archive,
86                    error: e.to_string(),
87                },
88            }
89        })
90        .collect()
91}
92
93/// Re-exported for callers that log validation failures specifically.
94pub type ReadMemInstallError = InstallError;