Skip to main content

jar_genesis/
lean.rs

1use std::path::Path;
2use std::process::Command;
3
4use serde::Serialize;
5use serde::de::DeserializeOwned;
6use thiserror::Error;
7
8#[derive(Debug, Error)]
9pub enum LeanError {
10    #[error("lean tool '{tool}' failed: {message}")]
11    ToolFailed { tool: String, message: String },
12    #[error("lean tool '{tool}' not found at {path}")]
13    NotFound { tool: String, path: String },
14    #[error("io error: {0}")]
15    Io(#[from] std::io::Error),
16    #[error("JSON error: {0}")]
17    Json(#[from] serde_json::Error),
18}
19
20/// Invoke a genesis CLI subcommand by piping JSON to stdin and reading JSON from stdout.
21/// Maps tool names like "genesis_select_targets" to "genesis select-targets".
22pub fn invoke<I: Serialize, O: DeserializeOwned>(
23    tool: &str,
24    input: &I,
25    spec_dir: &Path,
26) -> Result<O, LeanError> {
27    let bin_path = spec_dir.join(".lake/build/bin/genesis");
28    if !bin_path.exists() {
29        return Err(LeanError::NotFound {
30            tool: tool.to_string(),
31            path: bin_path.display().to_string(),
32        });
33    }
34
35    // Map legacy tool names to subcommands:
36    // "genesis_select_targets" -> "select-targets"
37    // "genesis_evaluate" -> "evaluate"
38    let subcommand = tool
39        .strip_prefix("genesis_")
40        .unwrap_or(tool)
41        .replace('_', "-");
42
43    let input_json = serde_json::to_string(input)?;
44    let output = Command::new(&bin_path)
45        .arg(&subcommand)
46        .stdin(std::process::Stdio::piped())
47        .stdout(std::process::Stdio::piped())
48        .stderr(std::process::Stdio::piped())
49        .spawn()
50        .and_then(|mut child| {
51            use std::io::Write;
52            if let Some(ref mut stdin) = child.stdin {
53                stdin.write_all(input_json.as_bytes())?;
54            }
55            child.wait_with_output()
56        })?;
57
58    if !output.status.success() {
59        let stderr = String::from_utf8_lossy(&output.stderr);
60        return Err(LeanError::ToolFailed {
61            tool: tool.to_string(),
62            message: stderr.trim().to_string(),
63        });
64    }
65
66    let stdout = String::from_utf8_lossy(&output.stdout);
67    Ok(serde_json::from_str(stdout.trim())?)
68}