light_program_test/utils/
find_light_bin.rs

1use std::path::PathBuf;
2
3pub fn find_light_bin() -> Option<PathBuf> {
4    // Run the 'which light' command to find the location of 'light' binary
5    #[cfg(not(feature = "devenv"))]
6    {
7        use std::{fs, process::Command};
8
9        let output = Command::new("which")
10            .arg("light")
11            .output()
12            .expect("Failed to execute 'which light'");
13
14        if !output.status.success() {
15            return None;
16        }
17
18        let light_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
19        let light_path = PathBuf::from(&light_path);
20
21        // Follow the symlink to find the actual script location.
22        // This works for npm, bun, and yarn which all create symlinks from their
23        // bin directory to the actual script in the package.
24        let symlink_target = fs::read_link(&light_path).ok()?;
25
26        // Resolve relative symlinks (e.g., "../lib/node_modules/...")
27        let resolved_path = if symlink_target.is_relative() {
28            let parent = light_path.parent()?;
29            parent.join(&symlink_target).canonicalize().ok()?
30        } else {
31            symlink_target.canonicalize().ok()?
32        };
33
34        // Navigate up to find the package root (contains bin/ directory with .so files)
35        // The symlink target is typically: .../zk-compression-cli/test_bin/run
36        // We need to find: .../zk-compression-cli/bin/
37        let mut current = resolved_path.as_path();
38        while let Some(parent) = current.parent() {
39            let bin_dir = parent.join("bin");
40            // Check if this bin/ directory contains .so files (our target)
41            if bin_dir.exists() && bin_dir.join("account_compression.so").exists() {
42                return Some(bin_dir);
43            }
44            current = parent;
45        }
46
47        None
48    }
49    #[cfg(feature = "devenv")]
50    {
51        println!("Use only in light protocol monorepo. Using 'git rev-parse --show-toplevel' to find the location of 'light' binary");
52        let light_protocol_toplevel = String::from_utf8_lossy(
53            &std::process::Command::new("git")
54                .arg("rev-parse")
55                .arg("--show-toplevel")
56                .output()
57                .expect("Failed to get top-level directory")
58                .stdout,
59        )
60        .trim()
61        .to_string();
62        let light_path = PathBuf::from(format!("{}/target/deploy/", light_protocol_toplevel));
63        Some(light_path)
64    }
65}
66
67#[cfg(all(not(feature = "devenv"), test))]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_find_light_bin() {
73        let bin_path = find_light_bin();
74        println!("find_light_bin() returned: {:?}", bin_path);
75
76        if let Some(path) = &bin_path {
77            println!("Path exists: {}", path.exists());
78            println!(
79                "account_compression.so exists: {}",
80                path.join("account_compression.so").exists()
81            );
82        }
83
84        // Only assert if light CLI is installed
85        if bin_path.is_some() {
86            let path = bin_path.unwrap();
87            assert!(path.exists(), "bin directory should exist");
88            assert!(
89                path.join("account_compression.so").exists(),
90                "account_compression.so should exist in bin directory"
91            );
92        }
93    }
94}