cargo_openvm/
util.rs

1use std::{
2    fs::{read_dir, read_to_string},
3    path::{Path, PathBuf},
4};
5
6use eyre::Result;
7use openvm_build::{get_in_scope_packages, get_workspace_packages};
8use openvm_sdk::config::{AppConfig, SdkVmConfig};
9#[cfg(feature = "evm-prove")]
10use openvm_sdk::keygen::{AggProvingKey, Halo2ProvingKey};
11use serde::de::DeserializeOwned;
12
13use crate::{
14    commands::RunCargoArgs,
15    default::{default_app_config, DEFAULT_APP_PK_NAME, DEFAULT_APP_VK_NAME},
16};
17
18pub(crate) fn read_to_struct_toml<T: DeserializeOwned>(path: impl AsRef<Path>) -> Result<T> {
19    let toml = read_to_string(path)?;
20    let ret = toml::from_str(&toml)?;
21    Ok(ret)
22}
23
24pub fn read_config_toml_or_default(config: impl AsRef<Path>) -> Result<AppConfig<SdkVmConfig>> {
25    if config.as_ref().exists() {
26        read_to_struct_toml(config)
27    } else {
28        println!(
29            "{:?} not found, using default application configuration",
30            config.as_ref()
31        );
32        Ok(default_app_config())
33    }
34}
35
36#[cfg(feature = "evm-prove")]
37pub fn read_default_agg_and_halo2_pk() -> Result<(AggProvingKey, Halo2ProvingKey)> {
38    use openvm_sdk::fs::read_object_from_file;
39
40    let agg_pk = read_object_from_file(crate::default::default_agg_stark_pk_path())?;
41    let halo2_pk = read_object_from_file(crate::default::default_agg_halo2_pk_path())?;
42    Ok((agg_pk, halo2_pk))
43}
44
45pub fn find_manifest_dir(mut current_dir: PathBuf) -> Result<PathBuf> {
46    current_dir = current_dir.canonicalize()?;
47    while !current_dir.join("Cargo.toml").exists() {
48        current_dir = current_dir
49            .parent()
50            .expect("Could not find Cargo.toml in current directory or any parent directory")
51            .to_path_buf();
52    }
53    Ok(current_dir)
54}
55
56pub fn get_manifest_path_and_dir(manifest_path: &Option<PathBuf>) -> Result<(PathBuf, PathBuf)> {
57    let manifest_dir = if let Some(manifest_path) = &manifest_path {
58        if !manifest_path.ends_with("Cargo.toml") {
59            return Err(eyre::eyre!(
60                "manifest_path must be a path to a Cargo.toml file"
61            ));
62        }
63        manifest_path.parent().unwrap().canonicalize()?
64    } else {
65        find_manifest_dir(PathBuf::from("."))?
66    };
67    let manifest_path = manifest_dir.join("Cargo.toml");
68    Ok((manifest_path.clone(), manifest_dir))
69}
70
71pub fn get_target_dir(target_dir: &Option<PathBuf>, manifest_path: &PathBuf) -> PathBuf {
72    target_dir
73        .clone()
74        .unwrap_or_else(|| openvm_build::get_target_dir(manifest_path))
75}
76
77pub fn get_target_output_dir(target_dir: &Path, profile: &str) -> PathBuf {
78    target_dir.join("openvm").join(profile).to_path_buf()
79}
80
81pub fn get_app_pk_path(target_dir: &Path) -> PathBuf {
82    target_dir.join("openvm").join(DEFAULT_APP_PK_NAME)
83}
84
85pub fn get_app_vk_path(target_dir: &Path) -> PathBuf {
86    target_dir.join("openvm").join(DEFAULT_APP_VK_NAME)
87}
88
89pub fn get_app_commit_path(target_output_dir: &Path, target_name: PathBuf) -> PathBuf {
90    let commit_name = target_name.with_extension("commit.json");
91    target_output_dir.join(commit_name)
92}
93
94// Given the arguments to a run command, this function isolates the executable to
95// run. If a specific binary or example is specified it will return that, else it
96// will search the workspace/package for binary targets. If there is a single
97// binary that will be returned, else an error will be raised.
98pub fn get_single_target_name(cargo_args: &RunCargoArgs) -> Result<PathBuf> {
99    get_single_target_name_raw(
100        &cargo_args.bin,
101        &cargo_args.example,
102        &cargo_args.manifest_path,
103        &cargo_args.package,
104    )
105}
106
107pub fn get_single_target_name_raw(
108    bin: &[String],
109    example: &[String],
110    manifest_path: &Option<PathBuf>,
111    package: &Option<String>,
112) -> Result<PathBuf> {
113    let num_targets = bin.len() + example.len();
114    let single_target_name = if num_targets > 1 {
115        return Err(eyre::eyre!(
116            "`cargo openvm run` can run at most one executable, but multiple were specified"
117        ));
118    } else if num_targets == 0 {
119        let (_, manifest_dir) = get_manifest_path_and_dir(manifest_path)?;
120
121        let packages = if package.is_some() {
122            get_workspace_packages(&manifest_dir)
123        } else {
124            get_in_scope_packages(&manifest_dir)
125        }
126        .into_iter()
127        .filter(|pkg| {
128            if let Some(package) = package {
129                pkg.name == *package
130            } else {
131                true
132            }
133        })
134        .collect::<Vec<_>>();
135
136        let binaries = packages
137            .iter()
138            .flat_map(|pkg| pkg.targets.iter())
139            .filter(|t| t.is_bin())
140            .collect::<Vec<_>>();
141
142        if binaries.len() > 1 {
143            return Err(eyre::eyre!(
144                "Could not determine which binary to run. Use the --bin flag to specify.\n\
145                    Available targets: {:?}",
146                binaries.iter().map(|t| t.name.clone()).collect::<Vec<_>>()
147            ));
148        } else if binaries.is_empty() {
149            return Err(eyre::eyre!(
150                "No binaries found. If you would like to run an example, use the --example flag.",
151            ));
152        } else {
153            PathBuf::from(binaries[0].name.clone())
154        }
155    } else if bin.is_empty() {
156        PathBuf::from("examples").join(&example[0])
157    } else {
158        PathBuf::from(bin[0].clone())
159    };
160    Ok(single_target_name)
161}
162
163pub fn get_files_with_ext(dir: &Path, extension: &str) -> Result<Vec<PathBuf>> {
164    let dir = dir.canonicalize()?;
165    let mut files = Vec::new();
166    for entry in read_dir(dir)? {
167        let path = entry?.path();
168        if path.is_file()
169            && path
170                .to_str()
171                .is_some_and(|path_str| path_str.ends_with(extension))
172        {
173            files.push(path);
174        }
175    }
176    Ok(files)
177}