leo_lang/cli/commands/common/util.rs
1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use leo_package::{Package, ProgramData};
18use leo_span::Symbol;
19
20use indexmap::IndexSet;
21use std::path::PathBuf;
22use walkdir::WalkDir;
23
24/// Collects paths to Leo source files for each program in the package.
25///
26/// For each non-test program, it searches the `src` directory for `.leo` files.
27/// It separates the `main.leo` file from the rest and returns a tuple:
28/// (`main.leo` path, list of other `.leo` file paths).
29/// Test programs are included with an empty list of additional files.
30/// Programs with bytecode data are ignored.
31///
32/// # Arguments
33/// * `package` - Reference to the package containing programs.
34///
35/// # Returns
36/// A vector of tuples with the main file and other source files.
37pub fn collect_leo_paths(package: &Package) -> Vec<(PathBuf, Vec<PathBuf>)> {
38 let mut partitioned_leo_paths = Vec::new();
39 for program in &package.programs {
40 match &program.data {
41 ProgramData::SourcePath { directory, source } => {
42 if program.is_test {
43 partitioned_leo_paths.push((source.clone(), vec![]));
44 } else {
45 let src_dir = directory.join("src");
46 if !src_dir.exists() {
47 continue;
48 }
49
50 let mut all_files: Vec<PathBuf> = WalkDir::new(&src_dir)
51 .into_iter()
52 .filter_map(Result::ok)
53 .filter(|entry| entry.path().extension().and_then(|s| s.to_str()) == Some("leo"))
54 .map(|entry| entry.into_path())
55 .collect();
56
57 if let Some(index) =
58 all_files.iter().position(|p| p.file_name().and_then(|s| s.to_str()) == Some("main.leo"))
59 {
60 let main = all_files.remove(index);
61 partitioned_leo_paths.push((main, all_files));
62 }
63 }
64 }
65 ProgramData::Bytecode(..) => {}
66 }
67 }
68 partitioned_leo_paths
69}
70
71/// Collects paths to `.aleo` files that are external (non-local) dependencies.
72///
73/// Scans the package's `imports` directory and filters out files that match
74/// the names of local source-based dependencies.
75/// Only retains `.aleo` files corresponding to true external dependencies.
76///
77/// # Arguments
78/// * `package` - Reference to the package whose imports are being examined.
79///
80/// # Returns
81/// A vector of paths to `.aleo` files not associated with local source dependencies.
82pub fn collect_aleo_paths(package: &Package) -> Vec<PathBuf> {
83 let local_dependency_symbols: IndexSet<Symbol> = package
84 .programs
85 .iter()
86 .flat_map(|program| match &program.data {
87 ProgramData::SourcePath { .. } => {
88 // It's a local Leo dependency.
89 Some(program.name)
90 }
91 ProgramData::Bytecode(..) => {
92 // It's a network dependency or local .aleo dependency.
93 None
94 }
95 })
96 .collect();
97
98 package
99 .imports_directory()
100 .read_dir()
101 .ok()
102 .into_iter()
103 .flatten()
104 .flat_map(|maybe_filename| maybe_filename.ok())
105 .filter(|entry| entry.file_type().ok().map(|filetype| filetype.is_file()).unwrap_or(false))
106 .flat_map(|entry| {
107 let path = entry.path();
108 if let Some(filename) = leo_package::filename_no_aleo_extension(&path) {
109 let symbol = Symbol::intern(filename);
110 if local_dependency_symbols.contains(&symbol) { None } else { Some(path) }
111 } else {
112 None
113 }
114 })
115 .collect()
116}